tarakan/priv/repo/migrations/20260711002041_add_authorization_foundations.exs
Maxfield Luke af6077b9c3
All checks were successful
CI and deploy / Test (push) Successful in 4m52s
CI and deploy / Deploy production (push) Successful in 23s
Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at
https://git.elektrine.com/elektrine/tarakan.
2026-07-29 04:43:40 -04:00

107 lines
4 KiB
Elixir

defmodule Tarakan.Repo.Migrations.AddAuthorizationFoundations do
use Ecto.Migration
def change do
alter table(:accounts) do
add :state, :string, null: false, default: "probation"
add :platform_role, :string, null: false, default: "member"
add :trust_tier, :string, null: false, default: "new"
end
create constraint(:accounts, :accounts_state_must_be_valid,
check: "state IN ('probation', 'active', 'restricted', 'suspended', 'banned')"
)
create constraint(:accounts, :accounts_platform_role_must_be_valid,
check: "platform_role IN ('member', 'moderator', 'admin')"
)
create constraint(:accounts, :accounts_trust_tier_must_be_valid,
check: "trust_tier IN ('new', 'contributor', 'reviewer')"
)
create index(:accounts, [:state])
create index(:accounts, [:platform_role])
create index(:accounts, [:trust_tier])
alter table(:repositories) do
add :participation_mode, :string, null: false, default: "unclaimed"
end
create constraint(:repositories, :repositories_participation_mode_must_be_valid,
check:
"participation_mode IN ('unclaimed', 'community', 'maintainer_verified', 'curated', 'paused')"
)
create index(:repositories, [:participation_mode])
create table(:repository_memberships) do
add :repository_id, references(:repositories, on_delete: :delete_all), null: false
add :account_id, references(:accounts, on_delete: :delete_all), null: false
add :role, :string, null: false
add :status, :string, null: false, default: "pending"
add :verified_at, :utc_datetime_usec
add :verified_by_account_id, references(:accounts)
timestamps(type: :utc_datetime_usec)
end
create unique_index(:repository_memberships, [:repository_id, :account_id])
create index(:repository_memberships, [:account_id, :status])
create index(:repository_memberships, [:repository_id, :status, :role])
create constraint(:repository_memberships, :repository_memberships_role_must_be_valid,
check: "role IN ('steward', 'reviewer')"
)
create constraint(:repository_memberships, :repository_memberships_status_must_be_valid,
check: "status IN ('verified', 'pending', 'revoked')"
)
create constraint(
:repository_memberships,
:repository_memberships_verified_state_must_be_consistent,
check:
"(status = 'verified' AND verified_at IS NOT NULL) OR (status <> 'verified' AND verified_at IS NULL)"
)
create table(:audit_events) do
add :actor_id, references(:accounts)
add :token_id, :bigint
add :action, :string, null: false
add :subject_type, :string
add :subject_id, :bigint
add :repository_id, references(:repositories)
add :from_state, :string
add :to_state, :string
add :reason_code, :string
add :request_id, :string
add :client_version, :string
add :metadata, :map, null: false, default: %{}
timestamps(type: :utc_datetime_usec, updated_at: false)
end
create index(:audit_events, [:actor_id, :inserted_at])
create index(:audit_events, [:repository_id, :inserted_at])
create index(:audit_events, [:subject_type, :subject_id, :inserted_at])
create index(:audit_events, [:request_id])
execute """
CREATE FUNCTION tarakan_prevent_audit_event_mutation()
RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'audit_events are append-only';
END;
$$ LANGUAGE plpgsql
""",
"DROP FUNCTION IF EXISTS tarakan_prevent_audit_event_mutation()"
execute """
CREATE TRIGGER audit_events_append_only
BEFORE UPDATE OR DELETE ON audit_events
FOR EACH ROW EXECUTE FUNCTION tarakan_prevent_audit_event_mutation()
""",
"DROP TRIGGER IF EXISTS audit_events_append_only ON audit_events"
end
end