-- ============================================================= -- DWH ROLES AUDIT -- Target Database: tracksolid_dwh -- Purpose: Assert that the n8n DWH pipeline's role contract holds: -- - dwh_owner exists (writes bronze + dwh_control) -- - grafana_ro exists (reads bronze + silver + gold + dwh_control) -- - grafana_ro has CONNECT on the database -- - grafana_ro has USAGE on every schema it needs -- Applies after: 260423_dwh_ddl_v1.sql, 261001_dwh_control.sql -- Idempotent: pure assertion, no CREATE ROLE or GRANT statements. -- -- Why this file exists: 260423 creates both roles and grants bronze/silver/gold; -- 261001 grants dwh_control. This file is a single checkpoint that verifies -- those prior migrations were applied in the right order, and fails loudly -- if anything is missing before the pipeline goes live. -- -- Password rotation and sslmode=require enforcement are out-of-band: -- rotate via ALTER ROLE ... PASSWORD ... in a psql superuser session, -- enforce SSL via the n8n credential (sslmode=require) — not SQL-level. -- ============================================================= BEGIN; DO $$ DECLARE missing TEXT := ''; r RECORD; BEGIN -- 1. Roles exist IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dwh_owner') THEN missing := missing || E'\n - role dwh_owner missing (expected from 260423)'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'grafana_ro') THEN missing := missing || E'\n - role grafana_ro missing (expected from 260423)'; END IF; -- 2. grafana_ro CONNECT on this database IF NOT has_database_privilege('grafana_ro', current_database(), 'CONNECT') THEN missing := missing || format(E'\n - grafana_ro lacks CONNECT on database %s', current_database()); END IF; -- 3. grafana_ro USAGE on every schema the pipeline / dashboards touch FOR r IN SELECT unnest(ARRAY['bronze','silver','gold','dwh_control']) AS schema_name LOOP IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = r.schema_name) THEN missing := missing || format(E'\n - schema %s missing (expected from 260423/261001)', r.schema_name); ELSIF NOT has_schema_privilege('grafana_ro', r.schema_name, 'USAGE') THEN missing := missing || format(E'\n - grafana_ro lacks USAGE on schema %s', r.schema_name); END IF; END LOOP; IF length(missing) > 0 THEN RAISE EXCEPTION E'DWH roles audit FAILED:%s', missing; END IF; RAISE NOTICE 'DWH roles audit OK: dwh_owner + grafana_ro present with expected grants.'; END$$; COMMIT;