diff --git a/internal/db/migrations/postgres.gen.go b/internal/db/migrations/postgres.gen.go index 104f4ac827..a625635afa 100644 --- a/internal/db/migrations/postgres.gen.go +++ b/internal/db/migrations/postgres.gen.go @@ -1252,6 +1252,7 @@ commit; bytes: []byte(` begin; + drop function update_iam_user_auth_account; drop function insert_auth_account_subtype; drop function insert_auth_method_subtype; @@ -1286,11 +1287,12 @@ begin; ┌────────────────┐ ┌──────────────────────────┐ │ iam_user │ │ auth_account │ ├────────────────┤ ├──────────────────────────┤ - │ public_id (pk) │ │ public_id (pk) │ - │ scope_id (fk) │ ◀fk2 │ scope_id (fk1,fk2) │ - │ │┼○──────○┼│ auth_method_id (fk1) │ - │ │ │ iam_user_id (fk2) │ - └────────────────┘ └──────────────────────────┘ + │ public_id (pk) │ │ public_id (pk) │ + │ scope_id (fk) │ ◀fk2 │ scope_id (fk1) │ + │ │┼○──────○┼│ auth_method_id (fk1) │ + │ │ │ iam_user_scope_id (fk2) │ + └────────────────┘ │ iam_user_id (fk2) │ + └──────────────────────────┘ An iam_scope can have 0 to many iam_users. An iam_scope can have 0 to many auth_methods. @@ -1339,13 +1341,26 @@ begin; scope_id wt_scope_id not null, iam_user_id wt_public_id, + -- The auth_account can only be assigned to an iam_user in the same scope as + -- the auth_method the auth_account belongs to. A separate column for + -- iam_user's scope id is needed because using the scope_id column in the + -- foreign key constraint causes an error when the iam_user is deleted but + -- the auth_account still exists. This is a valid scenario since the + -- lifetime of the auth_account is tied to the auth_method not the iam_user. + iam_user_scope_id wt_scope_id, + constraint user_and_auth_account_in_same_scope + check( + (iam_user_id is null and iam_user_scope_id is null) + or + (iam_user_id is not null and (iam_user_scope_id = scope_id)) + ), -- including scope_id in fk1 and fk2 ensures the scope_id of the owning -- auth_method and the scope_id of the owning iam_user are the same foreign key (scope_id, auth_method_id) -- fk1 references auth_method (scope_id, public_id) on delete cascade on update cascade, - foreign key (scope_id, iam_user_id) -- fk2 + foreign key (iam_user_scope_id, iam_user_id) -- fk2 references iam_user (scope_id, public_id) on delete set null on update cascade, @@ -1386,6 +1401,36 @@ begin; end; $$ language plpgsql; + -- update_iam_user_auth_account is a before update trigger on the auth_account + -- table. If the new.iam_user_id column is different from the old.iam_user_id + -- column, update_iam_user_auth_account retrieves the scope id of the iam user + -- and sets new.iam_user_scope_id to that value. If the new.iam_user_id column + -- is null and the old.iam_user_id column is not null, + -- update_iam_user_auth_account sets the iam_user_scope_id to null. + create or replace function + update_iam_user_auth_account() + returns trigger + as $$ + begin + if new.iam_user_id is distinct from old.iam_user_id then + if new.iam_user_id is null then + new.iam_user_scope_id = null; + else + select iam_user.scope_id into new.iam_user_scope_id + from iam_user + where iam_user.public_id = new.iam_user_id; + end if; + end if; + return new; + end; + $$ language plpgsql; + + + create trigger update_iam_user_auth_account + before update of iam_user_id on auth_account + for each row + execute procedure update_iam_user_auth_account(); + commit; `), @@ -1652,11 +1697,12 @@ begin; ┌──────────────────────────┐ ┌──────────────────────────┐ ┌───────────────────────────────┐ │ auth_account │ │ auth_password_account │ │ auth_password_credential │ ├──────────────────────────┤ ├──────────────────────────┤ ├───────────────────────────────┤ - │ public_id (pk) │ │ public_id (pk,fk2) │ │ private_id (pk) │ - │ scope_id (fk1,fk2) │ ◀fk2 │ scope_id (fk1,fk2) │ ◀fk2 │ password_method_id (fk1,fk2) │ - │ auth_method_id (fk1) │┼┼──────○┼│ auth_method_id (fk1,fk2) │┼┼──────○┼│ password_conf_id (fk1) │ - │ iam_user_id (fk2) │ │ ... │ │ password_account_id (fk2) │ - └──────────────────────────┘ └──────────────────────────┘ └───────────────────────────────┘ + │ public_id (pk) │ │ public_id (pk,fk2) │ │ private_id (pk) │ + │ scope_id (fk1) │ ◀fk2 │ scope_id (fk1,fk2) │ ◀fk2 │ password_method_id (fk1,fk2) │ + │ auth_method_id (fk1) │┼┼──────○┼│ auth_method_id (fk1,fk2) │┼┼──────○┼│ password_conf_id (fk1) │ + │ iam_user_scope_id (fk2) │ │ ... │ │ password_account_id (fk2) │ + │ iam_user_id (fk2) │ └──────────────────────────┘ └───────────────────────────────┘ + └──────────────────────────┘ An auth_method is a base type. An auth_password_method is an auth_method subtype. For every row in auth_password_method there is one row in auth_method diff --git a/internal/db/migrations/postgres/07_auth.down.sql b/internal/db/migrations/postgres/07_auth.down.sql index f841266499..8853c864a9 100644 --- a/internal/db/migrations/postgres/07_auth.down.sql +++ b/internal/db/migrations/postgres/07_auth.down.sql @@ -1,5 +1,6 @@ begin; + drop function update_iam_user_auth_account; drop function insert_auth_account_subtype; drop function insert_auth_method_subtype; diff --git a/internal/db/migrations/postgres/07_auth.up.sql b/internal/db/migrations/postgres/07_auth.up.sql index 8c45f3eb5d..876dba1c2a 100644 --- a/internal/db/migrations/postgres/07_auth.up.sql +++ b/internal/db/migrations/postgres/07_auth.up.sql @@ -19,11 +19,12 @@ begin; ┌────────────────┐ ┌──────────────────────────┐ │ iam_user │ │ auth_account │ ├────────────────┤ ├──────────────────────────┤ - │ public_id (pk) │ │ public_id (pk) │ - │ scope_id (fk) │ ◀fk2 │ scope_id (fk1,fk2) │ - │ │┼○──────○┼│ auth_method_id (fk1) │ - │ │ │ iam_user_id (fk2) │ - └────────────────┘ └──────────────────────────┘ + │ public_id (pk) │ │ public_id (pk) │ + │ scope_id (fk) │ ◀fk2 │ scope_id (fk1) │ + │ │┼○──────○┼│ auth_method_id (fk1) │ + │ │ │ iam_user_scope_id (fk2) │ + └────────────────┘ │ iam_user_id (fk2) │ + └──────────────────────────┘ An iam_scope can have 0 to many iam_users. An iam_scope can have 0 to many auth_methods. @@ -72,13 +73,26 @@ begin; scope_id wt_scope_id not null, iam_user_id wt_public_id, + -- The auth_account can only be assigned to an iam_user in the same scope as + -- the auth_method the auth_account belongs to. A separate column for + -- iam_user's scope id is needed because using the scope_id column in the + -- foreign key constraint causes an error when the iam_user is deleted but + -- the auth_account still exists. This is a valid scenario since the + -- lifetime of the auth_account is tied to the auth_method not the iam_user. + iam_user_scope_id wt_scope_id, + constraint user_and_auth_account_in_same_scope + check( + (iam_user_id is null and iam_user_scope_id is null) + or + (iam_user_id is not null and (iam_user_scope_id = scope_id)) + ), -- including scope_id in fk1 and fk2 ensures the scope_id of the owning -- auth_method and the scope_id of the owning iam_user are the same foreign key (scope_id, auth_method_id) -- fk1 references auth_method (scope_id, public_id) on delete cascade on update cascade, - foreign key (scope_id, iam_user_id) -- fk2 + foreign key (iam_user_scope_id, iam_user_id) -- fk2 references iam_user (scope_id, public_id) on delete set null on update cascade, @@ -119,4 +133,34 @@ begin; end; $$ language plpgsql; + -- update_iam_user_auth_account is a before update trigger on the auth_account + -- table. If the new.iam_user_id column is different from the old.iam_user_id + -- column, update_iam_user_auth_account retrieves the scope id of the iam user + -- and sets new.iam_user_scope_id to that value. If the new.iam_user_id column + -- is null and the old.iam_user_id column is not null, + -- update_iam_user_auth_account sets the iam_user_scope_id to null. + create or replace function + update_iam_user_auth_account() + returns trigger + as $$ + begin + if new.iam_user_id is distinct from old.iam_user_id then + if new.iam_user_id is null then + new.iam_user_scope_id = null; + else + select iam_user.scope_id into new.iam_user_scope_id + from iam_user + where iam_user.public_id = new.iam_user_id; + end if; + end if; + return new; + end; + $$ language plpgsql; + + + create trigger update_iam_user_auth_account + before update of iam_user_id on auth_account + for each row + execute procedure update_iam_user_auth_account(); + commit; diff --git a/internal/db/migrations/postgres/12_auth_password.up.sql b/internal/db/migrations/postgres/12_auth_password.up.sql index 772ddf1035..7a0a75d394 100644 --- a/internal/db/migrations/postgres/12_auth_password.up.sql +++ b/internal/db/migrations/postgres/12_auth_password.up.sql @@ -19,11 +19,12 @@ begin; ┌──────────────────────────┐ ┌──────────────────────────┐ ┌───────────────────────────────┐ │ auth_account │ │ auth_password_account │ │ auth_password_credential │ ├──────────────────────────┤ ├──────────────────────────┤ ├───────────────────────────────┤ - │ public_id (pk) │ │ public_id (pk,fk2) │ │ private_id (pk) │ - │ scope_id (fk1,fk2) │ ◀fk2 │ scope_id (fk1,fk2) │ ◀fk2 │ password_method_id (fk1,fk2) │ - │ auth_method_id (fk1) │┼┼──────○┼│ auth_method_id (fk1,fk2) │┼┼──────○┼│ password_conf_id (fk1) │ - │ iam_user_id (fk2) │ │ ... │ │ password_account_id (fk2) │ - └──────────────────────────┘ └──────────────────────────┘ └───────────────────────────────┘ + │ public_id (pk) │ │ public_id (pk,fk2) │ │ private_id (pk) │ + │ scope_id (fk1) │ ◀fk2 │ scope_id (fk1,fk2) │ ◀fk2 │ password_method_id (fk1,fk2) │ + │ auth_method_id (fk1) │┼┼──────○┼│ auth_method_id (fk1,fk2) │┼┼──────○┼│ password_conf_id (fk1) │ + │ iam_user_scope_id (fk2) │ │ ... │ │ password_account_id (fk2) │ + │ iam_user_id (fk2) │ └──────────────────────────┘ └───────────────────────────────┘ + └──────────────────────────┘ An auth_method is a base type. An auth_password_method is an auth_method subtype. For every row in auth_password_method there is one row in auth_method