-- dashboard_ro_role.sql — dedicated read-only LOGIN role for dashboard_api. -- -- Run as the postgres SUPERUSER (CREATE ROLE), NOT via run_migrations.py (which -- connects as the app role and may lack CREATEROLE). Apply with -- scripts/bootstrap_dashboard_ro.sh, which supplies the password as the psql -- variable :ro_pw from a host-only 0600 file — so no secret lives in this repo. -- -- Purpose: a least-privilege role that can serve the FULL dashboard_api read -- surface, so it backs BOTH the staging instance now (stage 1) AND the live prod -- connection later (stage 2 — migrate fleetapi.rahamafresh.com off the app role). -- It therefore grants exactly what the API reads: -- * SELECT on reporting.* and tracksolid.* (tables + views) -- * SELECT on the reporting.v_trips MATERIALIZED VIEW — matviews are NOT -- covered by GRANT ... ON ALL TABLES, so it must be named explicitly -- * EXECUTE on the reporting.fn_* map functions (fn_live_positions, etc.) -- * DEFAULT PRIVILEGES so future objects created by the migration role are -- auto-readable ("dynamic" — no re-grant when we add views) -- Read-only: no INSERT/UPDATE/DELETE and not the matview owner, so dashboard_ro -- can never write or REFRESH. Idempotent -> safe to re-apply (also rotates pw). \set ON_ERROR_STOP on DO $role$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dashboard_ro') THEN CREATE ROLE dashboard_ro LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE; END IF; END $role$; ALTER ROLE dashboard_ro WITH LOGIN PASSWORD :'ro_pw'; GRANT CONNECT ON DATABASE tracksolid_db TO dashboard_ro; GRANT USAGE ON SCHEMA reporting, tracksolid TO dashboard_ro; GRANT SELECT ON ALL TABLES IN SCHEMA reporting TO dashboard_ro; -- tables + views GRANT SELECT ON ALL TABLES IN SCHEMA tracksolid TO dashboard_ro; -- tables + views GRANT SELECT ON reporting.v_trips TO dashboard_ro; -- MATERIALIZED VIEW (not in ALL TABLES) GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA reporting TO dashboard_ro; -- "dynamic": future objects created by the migration role (tracksolid_owner) -- are auto-granted. NOTE: matviews are still never covered — a new matview needs -- its own explicit GRANT SELECT (as above for v_trips). ALTER DEFAULT PRIVILEGES FOR ROLE tracksolid_owner IN SCHEMA reporting GRANT SELECT ON TABLES TO dashboard_ro; ALTER DEFAULT PRIVILEGES FOR ROLE tracksolid_owner IN SCHEMA tracksolid GRANT SELECT ON TABLES TO dashboard_ro; ALTER DEFAULT PRIVILEGES FOR ROLE tracksolid_owner IN SCHEMA reporting GRANT EXECUTE ON FUNCTIONS TO dashboard_ro;