diff --git a/migrations/18_grant_reporting_ro.sql b/migrations/18_grant_reporting_ro.sql new file mode 100644 index 0000000..ee16da3 --- /dev/null +++ b/migrations/18_grant_reporting_ro.sql @@ -0,0 +1,24 @@ +-- 18_grant_reporting_ro.sql +-- Read-only access to the reporting.* layer for grafana_ro. +-- +-- grafana_ro is the read-only role the STAGING dashboard_api connects as (it reads +-- the prod DB but must be physically unable to write — see +-- docs/STAGING_FLEETOPS_ARCHITECTURE.md §6). It already reads tracksolid.* (Grafana +-- + the migration-07 analytics views), but was never granted SELECT on the +-- reporting.* map/analytics layer (migration 11) — the prod dashboard_api connects +-- as the app/superuser role, so the gap went unnoticed until the read-only staging +-- instance hit "permission denied for view v_filter_drivers / v_daily_summary". +-- +-- This grants USAGE + SELECT across reporting.* and sets DEFAULT PRIVILEGES so any +-- future reporting view/table is auto-readable by grafana_ro (no re-grant needed). +-- Read-only only: no INSERT/UPDATE/DELETE, so grafana_ro still cannot write or +-- REFRESH. Guarded + idempotent -> safe to re-apply. + +DO $grants$ +BEGIN + IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'grafana_ro') THEN + GRANT USAGE ON SCHEMA reporting TO grafana_ro; + GRANT SELECT ON ALL TABLES IN SCHEMA reporting TO grafana_ro; -- includes views + ALTER DEFAULT PRIVILEGES IN SCHEMA reporting GRANT SELECT ON TABLES TO grafana_ro; + END IF; +END $grants$; diff --git a/run_migrations.py b/run_migrations.py index 41a7b27..3514363 100644 --- a/run_migrations.py +++ b/run_migrations.py @@ -41,6 +41,7 @@ MIGRATIONS = [ "15_map_exclude_cost_centres.sql", # hide personal/management/mtn vehicles from the live map "16_live_feed_vehicle_type.sql", # add vehicle_type + fleet_segment to fn_live_positions feed "17_fleetops_fuel_view.sql", # reporting.v_fuel_daily — FleetOps GET /analytics/fuel source + "18_grant_reporting_ro.sql", # grant SELECT on reporting.* to grafana_ro (staging read-only role) ] # ── Tables that must exist before the service is allowed to start ─────────────