forked from wangziqi/gongxue-base
189 lines
6.4 KiB
SQL
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.';
|