129 lines
5.0 KiB
PL/PgSQL
129 lines
5.0 KiB
PL/PgSQL
-- AwoooP RLS Canary Wave 1: empty/low-risk tables only
|
|
-- Date: 2026-05-12
|
|
--
|
|
-- Scope:
|
|
-- These tables had exact production row_count=0 in the latest preflight:
|
|
-- - awooop_contract_revisions
|
|
-- - awooop_conversation_event
|
|
-- - awooop_mcp_credential_refs
|
|
-- - awooop_mcp_gateway_audit
|
|
-- - awooop_mcp_grants
|
|
-- - budget_ledger
|
|
--
|
|
-- Safety:
|
|
-- - fail-closed policy only; no NULL/empty-string app.project_id bypass.
|
|
-- - aborts if any target is missing project_id, has NULL project_id, or has rows.
|
|
-- - run with a migration/operator role, not through the production app role.
|
|
-- - do not run until scripts/ops/awooop-rls-access-audit.py and
|
|
-- scripts/ops/awooop-rls-manual-script-audit.py are green.
|
|
|
|
BEGIN;
|
|
|
|
SET LOCAL lock_timeout = '5s';
|
|
SET LOCAL statement_timeout = '30s';
|
|
|
|
CREATE TEMP TABLE _awooop_rls_wave1_targets (
|
|
table_name text PRIMARY KEY
|
|
) ON COMMIT DROP;
|
|
|
|
INSERT INTO _awooop_rls_wave1_targets (table_name)
|
|
VALUES
|
|
('awooop_contract_revisions'),
|
|
('awooop_conversation_event'),
|
|
('awooop_mcp_credential_refs'),
|
|
('awooop_mcp_gateway_audit'),
|
|
('awooop_mcp_grants'),
|
|
('budget_ledger');
|
|
|
|
DO $$
|
|
DECLARE
|
|
target_table text;
|
|
target_oid regclass;
|
|
total_rows bigint;
|
|
null_project_rows bigint;
|
|
BEGIN
|
|
FOR target_table IN SELECT table_name FROM _awooop_rls_wave1_targets ORDER BY table_name LOOP
|
|
SELECT to_regclass(format('public.%I', target_table)) INTO target_oid;
|
|
IF target_oid IS NULL THEN
|
|
RAISE EXCEPTION 'RLS canary target table does not exist: %', target_table;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (
|
|
SELECT 1
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public'
|
|
AND table_name = target_table
|
|
AND column_name = 'project_id'
|
|
) THEN
|
|
RAISE EXCEPTION 'RLS canary target missing project_id: %', target_table;
|
|
END IF;
|
|
|
|
EXECUTE format(
|
|
'SELECT COUNT(*), COUNT(*) FILTER (WHERE project_id IS NULL) FROM %I',
|
|
target_table
|
|
)
|
|
INTO total_rows, null_project_rows;
|
|
|
|
IF null_project_rows <> 0 THEN
|
|
RAISE EXCEPTION 'RLS canary target has NULL project_id rows: %, nulls=%',
|
|
target_table, null_project_rows;
|
|
END IF;
|
|
|
|
IF total_rows <> 0 THEN
|
|
RAISE EXCEPTION 'RLS canary wave1 only accepts empty tables: %, rows=%',
|
|
target_table, total_rows;
|
|
END IF;
|
|
END LOOP;
|
|
END
|
|
$$;
|
|
|
|
ALTER TABLE awooop_contract_revisions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_contract_revisions FORCE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS awooop_contract_revisions_tenant ON awooop_contract_revisions;
|
|
CREATE POLICY awooop_contract_revisions_tenant ON awooop_contract_revisions
|
|
FOR ALL TO awooop_app
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
ALTER TABLE awooop_conversation_event ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_conversation_event FORCE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS awooop_conversation_event_tenant ON awooop_conversation_event;
|
|
CREATE POLICY awooop_conversation_event_tenant ON awooop_conversation_event
|
|
FOR ALL TO awooop_app
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
ALTER TABLE awooop_mcp_credential_refs ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_mcp_credential_refs FORCE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS awooop_mcp_credential_refs_tenant ON awooop_mcp_credential_refs;
|
|
CREATE POLICY awooop_mcp_credential_refs_tenant ON awooop_mcp_credential_refs
|
|
FOR ALL TO awooop_app
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
ALTER TABLE awooop_mcp_gateway_audit ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_mcp_gateway_audit FORCE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS awooop_mcp_gateway_audit_tenant ON awooop_mcp_gateway_audit;
|
|
CREATE POLICY awooop_mcp_gateway_audit_tenant ON awooop_mcp_gateway_audit
|
|
FOR ALL TO awooop_app
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
ALTER TABLE awooop_mcp_grants ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_mcp_grants FORCE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS awooop_mcp_grants_tenant ON awooop_mcp_grants;
|
|
CREATE POLICY awooop_mcp_grants_tenant ON awooop_mcp_grants
|
|
FOR ALL TO awooop_app
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
ALTER TABLE budget_ledger ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE budget_ledger FORCE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS budget_ledger_tenant ON budget_ledger;
|
|
CREATE POLICY budget_ledger_tenant ON budget_ledger
|
|
FOR ALL TO awooop_app
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
COMMIT;
|