-- AwoooP RLS role bootstrap. -- -- IMPORTANT: -- - Do not put this file under apps/api/migrations; Gitea auto-migration should -- not attempt CREATE ROLE / BYPASSRLS with the limited migrator account. -- - Run manually as postgres or a CREATEROLE-capable operator after review. -- - This script does not create passwords and does not change application -- DATABASE_URL. It creates NOLOGIN group roles and grants awooop_app to the -- current production app connection role (`awoooi`). -- -- Suggested command on 188: -- sudo -u postgres psql -d awoooi_prod -v ON_ERROR_STOP=1 \ -- -f /path/to/awooop-rls-role-bootstrap.sql -- -- Post-check: -- bash scripts/ops/awooop-rls-preflight.sh --exact-counts BEGIN; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'awooop_app') THEN EXECUTE 'CREATE ROLE awooop_app NOLOGIN'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'awooop_platform_admin') THEN EXECUTE 'CREATE ROLE awooop_platform_admin NOLOGIN'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'awooop_migration') THEN EXECUTE 'CREATE ROLE awooop_migration NOLOGIN'; END IF; END $$; ALTER ROLE awooop_platform_admin BYPASSRLS; ALTER ROLE awooop_migration BYPASSRLS; -- Current production API connects as `awoooi`. Until DATABASE_URL is split to a -- dedicated LOGIN role, make that role a member of the RLS-constrained group so -- policies written as `FOR ALL TO awooop_app` apply without secret rotation. GRANT awooop_app TO awoooi; -- Keep existing migration account usable without changing its password or -- DATABASE_URL. The group role is NOLOGIN; operators may SET ROLE during manual -- RLS migrations if needed. DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'awoooi_migrator') THEN EXECUTE 'GRANT awooop_migration TO awoooi_migrator'; END IF; END $$; -- Minimum grants for existing target tables that already have project_id. RLS -- policies remain a separate staged migration and are not enabled here. GRANT USAGE ON SCHEMA public TO awooop_app; DO $$ DECLARE table_name text; target_tables text[] := ARRAY[ 'incidents', 'knowledge_entries', 'playbooks', 'audit_logs', 'budget_ledger', 'awooop_projects', 'awooop_contract_revisions', 'awooop_run_state', 'awooop_mcp_tool_registry', 'awooop_mcp_grants', 'awooop_mcp_credential_refs', 'awooop_mcp_gateway_audit', 'awooop_conversation_event', 'awooop_outbound_message' ]; BEGIN FOREACH table_name IN ARRAY target_tables LOOP IF to_regclass('public.' || table_name) IS NOT NULL THEN EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON public.%I TO awooop_app', table_name); END IF; END LOOP; END $$; GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO awooop_app; COMMIT;