Files
gongxue-base/scripts/deploy/sql/bootstrap-backend-runtime-roles.sql
2026-07-12 19:26:57 +08:00

189 lines
6.4 KiB
SQL

do $$
begin
if not exists (select 1 from pg_roles where rolname = 'tiku_api') then
create role tiku_api login;
end if;
if not exists (select 1 from pg_roles where rolname = 'tiku_worker') then
create role tiku_worker login;
end if;
end
$$;
alter role tiku_api
login nosuperuser noinherit nocreatedb nocreaterole noreplication bypassrls;
alter role tiku_worker
login nosuperuser noinherit nocreatedb nocreaterole noreplication bypassrls;
create schema if not exists extensions;
revoke create on schema extensions from public, tiku_api, tiku_worker;
grant usage on schema extensions to tiku_api, tiku_worker;
-- Install or relocate required extensions before normal migrations. Otherwise
-- citext/ltree can be created later by supabase_admin with PostgreSQL's default
-- PUBLIC EXECUTE and the migration role cannot close that RPC surface.
do $$
declare
extension_name name;
extension_schema name;
extension_relocatable boolean;
begin
foreach extension_name in array array[
'pgcrypto'::name,
'citext'::name,
'ltree'::name,
'pg_trgm'::name
]
loop
select namespace.nspname, extension.extrelocatable
into extension_schema, extension_relocatable
from pg_extension extension
join pg_namespace namespace on namespace.oid = extension.extnamespace
where extension.extname = extension_name;
if not found then
execute format('create extension %I with schema extensions', extension_name);
elsif extension_schema <> 'extensions' then
if not extension_relocatable then
raise exception 'Required extension % cannot be relocated from schema %', extension_name, extension_schema;
end if;
execute format('alter extension %I set schema extensions', extension_name);
end if;
end loop;
end
$$;
alter role tiku_api set search_path = pg_catalog, public, extensions;
alter role tiku_worker set search_path = pg_catalog, public, extensions;
-- Official Supabase images can preinstall relocatable extensions such as
-- citext and ltree in public. Their functions are owned by supabase_admin, so
-- the normal migration role cannot remove PostgreSQL's default PUBLIC EXECUTE.
-- Seal that inherited RPC surface while a real superuser is available.
do $$
declare
required_role name;
begin
foreach required_role in array array['anon'::name, 'authenticated'::name]
loop
if not exists (select 1 from pg_roles where rolname = required_role) then
raise exception 'Required Supabase Data API role % is missing', required_role;
end if;
end loop;
end
$$;
revoke execute on all functions in schema public
from public, anon, authenticated, tiku_api, tiku_worker;
revoke execute on all functions in schema extensions
from public, anon, authenticated, tiku_api, tiku_worker;
-- Supabase's internal services use dedicated trusted database roles. Preserve
-- their extension execution when those roles exist, without reopening the
-- surface to the client-facing anon/authenticated roles.
do $$
declare
trusted_role name;
begin
foreach trusted_role in array array[
'postgres'::name,
'service_role'::name,
'dashboard_user'::name,
'supabase_auth_admin'::name,
'supabase_storage_admin'::name,
'supabase_realtime_admin'::name,
'supabase_functions_admin'::name
]
loop
if exists (select 1 from pg_roles where rolname = trusted_role) then
execute format('grant execute on all functions in schema extensions to %I', trusted_role);
end if;
end loop;
end
$$;
-- citext and ltree operators, casts and tree helpers call extension-owned
-- functions. pg_trgm index support is similarly needed by backend search.
-- Backend roles receive only those pure type/search helpers; pgcrypto stays
-- unavailable and Data API client roles receive no extension execution.
do $$
declare
extension_function record;
begin
for extension_function in
select procedure_row.oid::regprocedure::text as signature
from pg_proc procedure_row
join pg_depend dependency
on dependency.classid = 'pg_proc'::regclass
and dependency.objid = procedure_row.oid
and dependency.refclassid = 'pg_extension'::regclass
and dependency.deptype = 'e'
join pg_extension extension on extension.oid = dependency.refobjid
where extension.extname in ('citext', 'ltree', 'pg_trgm')
loop
execute format(
'revoke execute on function %s from public, anon, authenticated, tiku_api, tiku_worker',
extension_function.signature
);
execute format(
'grant execute on function %s to tiku_api, tiku_worker',
extension_function.signature
);
end loop;
end
$$;
-- Extension upgrades may add more public functions under the extension owner.
-- PostgreSQL's function default ACL is global, so this must not use IN SCHEMA.
do $$
declare
owner_name name;
begin
for owner_name in
select bootstrap_owner.owner_name
from (
select current_user::name as owner_name
union
select distinct owner_role.rolname
from pg_proc function_row
join pg_namespace namespace on namespace.oid = function_row.pronamespace
join pg_roles owner_role on owner_role.oid = function_row.proowner
where namespace.nspname in ('public', 'extensions')
) bootstrap_owner
loop
execute format(
'alter default privileges for role %I revoke execute on functions from public, anon, authenticated, tiku_api, tiku_worker',
owner_name
);
end loop;
end
$$;
-- Remove all inherited or SET ROLE paths. Runtime roles receive object ACLs
-- directly from the normal migration and never inherit another database role.
do $$
declare
runtime_role name;
parent_role name;
begin
foreach runtime_role in array array['tiku_api'::name, 'tiku_worker'::name]
loop
for parent_role in
select parent.rolname
from pg_auth_members membership
join pg_roles member on member.oid = membership.member
join pg_roles parent on parent.oid = membership.roleid
where member.rolname = runtime_role
loop
execute format('revoke %I from %I', parent_role, runtime_role);
end loop;
end loop;
end
$$;
comment on role tiku_api is
'Trusted API-only runtime. Cluster attributes are provisioned by the database administrator; object ACLs are managed by migration 202607120013.';
comment on role tiku_worker is
'Trusted background-worker runtime. Cluster attributes are provisioned by the database administrator; object ACLs are managed by migration 202607120013.';