Skip to content

Commit

Permalink
chore: split bootstrap user switch script to global vs. in-database objs
Browse files Browse the repository at this point in the history
  • Loading branch information
soedirgo committed Oct 29, 2024
1 parent 1211077 commit 1e6746c
Showing 1 changed file with 125 additions and 111 deletions.
236 changes: 125 additions & 111 deletions ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,17 @@ set session authorization supabase_tmp;
-- to handle snowflakes that happened in the past
revoke supabase_admin from authenticator;
-- #incident-2024-09-12-project-upgrades-are-temporarily-disabled
do $$
begin
if exists (select from pg_extension where extname = 'timescaledb') then
execute(format('select %s.timescaledb_pre_restore()', (select pronamespace::regnamespace from pg_proc where proname = 'timescaledb_pre_restore')));
if exists (select from pg_authid where rolname = 'pg_read_all_data') then
execute('grant pg_read_all_data to postgres');
end if;
end
$$;
grant pg_signal_backend to postgres;
-- Swap postgres & supabase_admin on global objects (roles, role settings, etc.).
do $$
declare
postgres_rolpassword text := (select rolpassword from pg_authid where rolname = 'postgres');
Expand All @@ -128,7 +131,84 @@ declare
join pg_authid a on a.oid = s.setrole
where a.rolname in ('postgres', 'supabase_admin')
);
event_triggers jsonb[] := (select coalesce(array_agg(jsonb_build_object('name', evtname)), '{}') from pg_event_trigger where evtowner = 'postgres'::regrole);
rec record;
obj jsonb;
begin
set local search_path = '';
alter role postgres rename to supabase_admin_;
alter role supabase_admin rename to postgres;
alter role supabase_admin_ rename to supabase_admin;
-- role grants
for rec in
select * from pg_auth_members
loop
execute(format('revoke %s from %s;', rec.roleid::regrole, rec.member::regrole));
execute(format(
'grant %s to %s %s granted by %s;',
case
when rec.roleid = 'postgres'::regrole then 'supabase_admin'
when rec.roleid = 'supabase_admin'::regrole then 'postgres'
else rec.roleid::regrole
end,
case
when rec.member = 'postgres'::regrole then 'supabase_admin'
when rec.member = 'supabase_admin'::regrole then 'postgres'
else rec.member::regrole
end,
case
when rec.admin_option then 'with admin option'
else ''
end,
case
when rec.grantor = 'postgres'::regrole then 'supabase_admin'
when rec.grantor = 'supabase_admin'::regrole then 'postgres'
else rec.grantor::regrole
end
));
end loop;
-- role passwords
execute(format('alter role postgres password %L;', postgres_rolpassword));
execute(format('alter role supabase_admin password %L;', supabase_admin_rolpassword));
-- role settings
foreach obj in array role_settings
loop
execute(format('alter role %I %s reset all',
case when obj->>'role' = 'postgres' then 'supabase_admin' else 'postgres' end,
case when obj->>'database' is null then '' else format('in database %I', obj->>'database') end
));
end loop;
foreach obj in array role_settings
loop
for rec in
select split_part(value, '=', 1) as key, substr(value, strpos(value, '=') + 1) as value
from jsonb_array_elements_text(obj->'configs')
loop
execute(format('alter role %I %s set %I to %s',
obj->>'role',
case when obj->>'database' is null then '' else format('in database %I', obj->>'database') end,
rec.key,
-- https://github.com/postgres/postgres/blob/70d1c664f4376fd3499e3b0c6888cf39b65d722b/src/bin/pg_dump/dumputils.c#L861
case
when rec.key in ('local_preload_libraries', 'search_path', 'session_preload_libraries', 'shared_preload_libraries', 'temp_tablespaces', 'unix_socket_directories')
then rec.value
else quote_literal(rec.value)
end
));
end loop;
end loop;
reassign owned by postgres to supabase_admin;
end
$$;
-- Swap postgres & supabase_admin on in-database objects (schemas, tables, functions, etc.).
do $$
declare
event_triggers jsonb[] := (select coalesce(array_agg(jsonb_build_object('name', evtname)), '{}') from pg_event_trigger where evtowner = 'supabase_admin'::regrole);
user_mappings jsonb[] := (
select coalesce(array_agg(jsonb_build_object('oid', um.oid, 'role', a.rolname, 'server', s.srvname, 'options', um.umoptions)), '{}')
from pg_user_mapping um
Expand Down Expand Up @@ -219,72 +299,9 @@ begin
alter event trigger pgsodium_trg_mask_update disable;
end if;
alter role postgres rename to supabase_admin_;
alter role supabase_admin rename to postgres;
alter role supabase_admin_ rename to supabase_admin;
-- role grants
for rec in
select * from pg_auth_members
loop
execute(format('revoke %s from %s;', rec.roleid::regrole, rec.member::regrole));
execute(format(
'grant %s to %s %s granted by %s;',
case
when rec.roleid = 'postgres'::regrole then 'supabase_admin'
when rec.roleid = 'supabase_admin'::regrole then 'postgres'
else rec.roleid::regrole
end,
case
when rec.member = 'postgres'::regrole then 'supabase_admin'
when rec.member = 'supabase_admin'::regrole then 'postgres'
else rec.member::regrole
end,
case
when rec.admin_option then 'with admin option'
else ''
end,
case
when rec.grantor = 'postgres'::regrole then 'supabase_admin'
when rec.grantor = 'supabase_admin'::regrole then 'postgres'
else rec.grantor::regrole
end
));
end loop;
-- role passwords
execute(format('alter role postgres password %L;', postgres_rolpassword));
execute(format('alter role supabase_admin password %L;', supabase_admin_rolpassword));
-- role settings
foreach obj in array role_settings
loop
execute(format('alter role %I %s reset all',
case when obj->>'role' = 'postgres' then 'supabase_admin' else 'postgres' end,
case when obj->>'database' is null then '' else format('in database %I', obj->>'database') end
));
end loop;
foreach obj in array role_settings
loop
for rec in
select split_part(value, '=', 1) as key, substr(value, strpos(value, '=') + 1) as value
from jsonb_array_elements_text(obj->'configs')
loop
execute(format('alter role %I %s set %I to %s',
obj->>'role',
case when obj->>'database' is null then '' else format('in database %I', obj->>'database') end,
rec.key,
-- https://github.com/postgres/postgres/blob/70d1c664f4376fd3499e3b0c6888cf39b65d722b/src/bin/pg_dump/dumputils.c#L861
case
when rec.key in ('local_preload_libraries', 'search_path', 'session_preload_libraries', 'shared_preload_libraries', 'temp_tablespaces', 'unix_socket_directories')
then rec.value
else quote_literal(rec.value)
end
));
end loop;
end loop;
reassign owned by postgres to supabase_admin;
if exists (select from pg_extension where extname = 'timescaledb') then
execute(format('select %s.timescaledb_pre_restore()', (select pronamespace::regnamespace from pg_proc where proname = 'timescaledb_pre_restore')));
end if;
-- databases
for rec in
Expand Down Expand Up @@ -352,10 +369,7 @@ begin
loop
if obj->>'role' in ('postgres', 'supabase_admin') or rec.grantee::regrole in ('postgres', 'supabase_admin') then
execute(format('alter default privileges for role %I %s revoke %s on %s from %s'
, case when obj->>'role' = 'postgres' then 'supabase_admin'
when obj->>'role' = 'supabase_admin' then 'postgres'
else obj->>'role'
end
, obj->>'role'
, case when obj->>'schema' is null then ''
else format('in schema %I', obj->>'schema')
end
Expand All @@ -366,16 +380,11 @@ begin
when obj->>'objtype' = 'T' then 'types'
when obj->>'objtype' = 'n' then 'schemas'
end
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin'
when rec.grantee = 'supabase_admin'::regrole then 'postgres'
when rec.grantee = 0 then 'public'
else rec.grantee::regrole::text
end
, case when rec.grantee = 0 then 'public' else rec.grantee::regrole::text end
));
end if;
end loop;
end loop;
foreach obj in array default_acls
loop
for rec in
Expand All @@ -384,7 +393,10 @@ begin
loop
if obj->>'role' in ('postgres', 'supabase_admin') or rec.grantee::regrole in ('postgres', 'supabase_admin') then
execute(format('alter default privileges for role %I %s grant %s on %s to %s %s'
, obj->>'role'
, case when obj->>'role' = 'postgres' then 'supabase_admin'
when obj->>'role' = 'supabase_admin' then 'postgres'
else obj->>'role'
end
, case when obj->>'schema' is null then ''
else format('in schema %I', obj->>'schema')
end
Expand All @@ -395,7 +407,11 @@ begin
when obj->>'objtype' = 'T' then 'types'
when obj->>'objtype' = 'n' then 'schemas'
end
, case when rec.grantee = 0 then 'public' else rec.grantee::regrole::text end
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin'
when rec.grantee = 'supabase_admin'::regrole then 'postgres'
when rec.grantee = 0 then 'public'
else rec.grantee::regrole::text
end
, case when rec.is_grantable then 'with grant option' else '' end
));
end if;
Expand All @@ -405,15 +421,15 @@ begin
-- schemas
foreach obj in array schemas
loop
if obj->>'owner' = 'postgres' then
if obj->>'owner' = 'supabase_admin' then
execute(format('alter schema %s owner to postgres;', (obj->>'oid')::regnamespace));
end if;
for rec in
select grantor, grantee, privilege_type, is_grantable
from aclexplode((obj->>'acl')::aclitem[])
where grantee::regrole in ('postgres', 'supabase_admin')
loop
execute(format('revoke %s on schema %s from %I', rec.privilege_type, (obj->>'oid')::regnamespace, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end));
execute(format('revoke %s on schema %s from %I', rec.privilege_type, (obj->>'oid')::regnamespace, rec.grantee::regrole));
end loop;
end loop;
foreach obj in array schemas
Expand All @@ -423,22 +439,26 @@ begin
from aclexplode((obj->>'acl')::aclitem[])
where grantee::regrole in ('postgres', 'supabase_admin')
loop
execute(format('grant %s on schema %s to %s %s', rec.privilege_type, (obj->>'oid')::regnamespace, rec.grantee::regrole, case when rec.is_grantable then 'with grant option' else '' end));
execute(format('grant %s on schema %s to %s %s'
, rec.privilege_type
, (obj->>'oid')::regnamespace
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end
, case when rec.is_grantable then 'with grant option' else '' end));
end loop;
end loop;
-- types
foreach obj in array types
loop
if obj->>'owner' = 'postgres' then
if obj->>'owner' = 'supabase_admin' then
execute(format('alter type %s owner to postgres;', (obj->>'oid')::regtype));
end if;
for rec in
select grantor, grantee, privilege_type, is_grantable
from aclexplode((obj->>'acl')::aclitem[])
where grantee::regrole in ('postgres', 'supabase_admin')
loop
execute(format('revoke %s on type %s from %I', rec.privilege_type, (obj->>'oid')::regtype, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end));
execute(format('revoke %s on type %s from %I', rec.privilege_type, (obj->>'oid')::regtype, rec.grantee::regrole));
end loop;
end loop;
foreach obj in array types
Expand All @@ -448,14 +468,18 @@ begin
from aclexplode((obj->>'acl')::aclitem[])
where grantee::regrole in ('postgres', 'supabase_admin')
loop
execute(format('grant %s on type %s to %s %s', rec.privilege_type, (obj->>'oid')::regtype, rec.grantee::regrole, case when rec.is_grantable then 'with grant option' else '' end));
execute(format('grant %s on type %s to %s %s'
, rec.privilege_type
, (obj->>'oid')::regtype
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end
, case when rec.is_grantable then 'with grant option' else '' end));
end loop;
end loop;
-- functions
foreach obj in array functions
loop
if obj->>'owner' = 'postgres' then
if obj->>'owner' = 'supabase_admin' then
execute(format('alter routine %s(%s) owner to postgres;', (obj->>'oid')::regproc, pg_get_function_identity_arguments((obj->>'oid')::regproc)));
end if;
for rec in
Expand All @@ -471,7 +495,7 @@ begin
end
, (obj->>'oid')::regproc
, pg_get_function_identity_arguments((obj->>'oid')::regproc)
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end
, rec.grantee::regrole
));
end loop;
end loop;
Expand All @@ -490,7 +514,7 @@ begin
end
, (obj->>'oid')::regproc
, pg_get_function_identity_arguments((obj->>'oid')::regproc)
, rec.grantee::regrole
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end
, case when rec.is_grantable then 'with grant option' else '' end
));
end loop;
Expand All @@ -501,15 +525,15 @@ begin
loop
-- obj->>'oid' (text) needs to be casted to oid first for some reason
if obj->>'owner' = 'postgres' then
if obj->>'owner' = 'supabase_admin' then
execute(format('alter table %s owner to postgres;', (obj->>'oid')::oid::regclass));
end if;
for rec in
select grantor, grantee, privilege_type, is_grantable
from aclexplode((obj->>'acl')::aclitem[])
where grantee::regrole in ('postgres', 'supabase_admin')
loop
execute(format('revoke %s on table %s from %I', rec.privilege_type, (obj->>'oid')::oid::regclass, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end));
execute(format('revoke %s on table %s from %I', rec.privilege_type, (obj->>'oid')::oid::regclass, rec.grantee::regrole));
end loop;
end loop;
foreach obj in array relations
Expand All @@ -521,35 +545,25 @@ begin
from aclexplode((obj->>'acl')::aclitem[])
where grantee::regrole in ('postgres', 'supabase_admin')
loop
execute(format('grant %s on table %s to %s %s', rec.privilege_type, (obj->>'oid')::oid::regclass, rec.grantee::regrole, case when rec.is_grantable then 'with grant option' else '' end));
execute(format('grant %s on table %s to %s %s'
, rec.privilege_type
, (obj->>'oid')::oid::regclass
, case when rec.grantee = 'postgres'::regrole then 'supabase_admin' else 'postgres' end
, case when rec.is_grantable then 'with grant option' else '' end));
end loop;
end loop;
if exists (select from pg_event_trigger where evtname = 'pgsodium_trg_mask_update') then
alter event trigger pgsodium_trg_mask_update enable;
end if;
end
$$;
do $$
begin
if exists (select from pg_extension where extname = 'timescaledb') then
execute(format('select %s.timescaledb_post_restore()', (select pronamespace::regnamespace from pg_proc where proname = 'timescaledb_post_restore')));
end if;
end
$$;
alter database postgres connection limit -1;
-- #incident-2024-09-12-project-upgrades-are-temporarily-disabled
do $$
begin
if exists (select from pg_authid where rolname = 'pg_read_all_data') then
execute('grant pg_read_all_data to postgres');
if exists (select from pg_event_trigger where evtname = 'pgsodium_trg_mask_update') then
alter event trigger pgsodium_trg_mask_update enable;
end if;
end
$$;
grant pg_signal_backend to postgres;
alter database postgres connection limit -1;
set session authorization supabase_admin;
drop role supabase_tmp;
Expand Down

0 comments on commit 1e6746c

Please sign in to comment.