From 6492a48cedab42b6a6645a4f44c67640ffa850f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Mon, 4 Mar 2024 17:32:01 +0000 Subject: [PATCH 1/6] feat: Setup `metabase_read_only` role --- .../1709565833346_run_sql_migration/down.sql | 10 ++++++++++ .../migrations/1709565833346_run_sql_migration/up.sql | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql create mode 100644 hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql new file mode 100644 index 0000000000..830ae4e8a6 --- /dev/null +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql @@ -0,0 +1,10 @@ +-- Revoke select permissions from views used by Metabase +REVOKE SELECT ON public.analytics_summary FROM metabase_read_only; +REVOKE SELECT ON public.feedback_summary FROM metabase_read_only; +REVOKE SELECT ON public.submission_services_summary FROM metabase_read_only; + +-- Revoke usage on schema +REVOKE USAGE ON SCHEMA public FROM metabase_read_only; + +-- Drop the role +DROP ROLE IF EXISTS metabase_read_only; diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql new file mode 100644 index 0000000000..1e41baf9b4 --- /dev/null +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql @@ -0,0 +1,10 @@ +-- Create the role +CREATE ROLE metabase_read_only; + +-- Grant usage on schema +GRANT USAGE ON SCHEMA public TO metabase_read_only; + +-- Grant select permissions on views used by Metabase +GRANT SELECT ON public.analytics_summary TO metabase_read_only; +GRANT SELECT ON public.feedback_summary TO metabase_read_only; +GRANT SELECT ON public.submission_services_summary TO metabase_read_only; \ No newline at end of file From 5dfa3c4cc9214e295bb81b9797fd6617dd37d0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Mon, 4 Mar 2024 17:34:38 +0000 Subject: [PATCH 2/6] feat: Setup `metabase_read_only` role --- .../migrations/1709565833346_run_sql_migration/down.sql | 5 +++++ .../migrations/1709565833346_run_sql_migration/up.sql | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql index 830ae4e8a6..bab445011e 100644 --- a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql @@ -3,6 +3,11 @@ REVOKE SELECT ON public.analytics_summary FROM metabase_read_only; REVOKE SELECT ON public.feedback_summary FROM metabase_read_only; REVOKE SELECT ON public.submission_services_summary FROM metabase_read_only; +-- Revoke select permissions from tables used by Metabase +REVOKE SELECT ON public.flows FROM metabase_read_only; +REVOKE SELECT ON public.published_flows FROM metabase_read_only; +REVOKE SELECT ON public.teams FROM metabase_read_only; + -- Revoke usage on schema REVOKE USAGE ON SCHEMA public FROM metabase_read_only; diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql index 1e41baf9b4..fe6c708c9e 100644 --- a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql @@ -4,6 +4,11 @@ CREATE ROLE metabase_read_only; -- Grant usage on schema GRANT USAGE ON SCHEMA public TO metabase_read_only; +-- Grant select permissions on public tables used by Metabase (useful for joins) +GRANT SELECT ON public.flows TO metabase_read_only; +GRANT SELECT ON public.published_flows TO metabase_read_only; +GRANT SELECT ON public.teams TO metabase_read_only; + -- Grant select permissions on views used by Metabase GRANT SELECT ON public.analytics_summary TO metabase_read_only; GRANT SELECT ON public.feedback_summary TO metabase_read_only; From b27deee0262c76d2a48a386949e9ccbab3629526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Mon, 4 Mar 2024 17:36:54 +0000 Subject: [PATCH 3/6] docs: Explain how new role will work --- .../how-to-grant-metabase-permissions.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 doc/how-to/how-to-grant-metabase-permissions.md diff --git a/doc/how-to/how-to-grant-metabase-permissions.md b/doc/how-to/how-to-grant-metabase-permissions.md new file mode 100644 index 0000000000..9bde8702b9 --- /dev/null +++ b/doc/how-to/how-to-grant-metabase-permissions.md @@ -0,0 +1,52 @@ +# How to grant Metabase permissions + +## What is Metabase? +[Metabase](https://www.metabase.com/) is an open source BI service which we self-host as part of PlanX. It allows teams to view and self-serve analytics dashboards related to their flows, applications, and users. + +Metabase is set up and running on both Staging and Production environments, but only the Production instance (with Production data) has dashboards maintained and curated for teams. + +## Context +Metabase accesses our staging and production databases through the `metabase_read_only` role which has `SELECT` (read-only) access to a subset of tables and views. This ensures that sensitive user data cannot be inadvertently exposed via Metabase, and any new tables added to the PlanX database have to be explicitly exposed via this role. + +The permissions granted for the `metabase_read_only` role are applied via Hasura migrations. This ensures that we have a version-controlled history of this role, and it's access level is documented in code. This is not controlled via Pulumi (IaC) as this is not used for local development or test environments ("Pizzas"), which would necessitate a second method for these environments. + +## How is this role used? + +### Staging & Production +The `metabase_read_only` role is granted to the `metabase_user` database user, which is manually set up on both staging and production with the following SQL - + +```sql +CREATE USER metabase_user WITH PASSWORD `$PASSWORD`; +GRANT metabase_read_only TO metabase_user; +``` + +The password for Staging and Production databases can be found the OSL 1Password account. + +The username and password for Metabase are not controlled via IaC - they are manually entered via the Metabase "Admin" dashboard (`Admin Setting > Databases > "staging" | "production" > Username / Password fields`). + +### Locally & Pizzas +If you wish to run Metabase locally using the "analytics" Docker profile (`pnpm analytics` from project root), you will need to manually run the above SQL on your local database with a password of your choice. Alternatively, you can use the root DB username/password. + +The Metabase service does not run on Pizzas. + +## Metabase permissions +Metabase also operates it's own permissions model, which allows more fine-grained control over tables. This allows "Administrator" users access to all tables granted to the Postgres `metabase_read_only` role, but other users can only access summary views. + +## Process +The process for exposing a new table / view to Metabase is as follows - + +### Tables +Generally, we'd favour exposing views of data via Metabase. This means only certain columns can be exposed, and data can be formatted in a more user-friendly manner. + +If you need to expose a new table (e.g. public data) access can be granted via a Hasura migration, e.g. - + +```sql +GRANT SELECT ON public.flows TO metabase_read_only; +``` + +### Views +When adding a new view, you will need to grant the `metabase_read_only` role `SELECT` access the view. Access should be applied via Hasura migrations, e.g. - + +```sql +GRANT SELECT ON public.YOUR_NEW_VIEW TO metabase_read_only; +``` From 4dd9b14006d1d158539d2a27c7b339771b5eaa91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Tue, 5 Mar 2024 09:20:19 +0000 Subject: [PATCH 4/6] docs: Add context on IaC setup and `MB_DB_CONNECTION_URI\' env var --- doc/how-to/how-to-grant-metabase-permissions.md | 2 ++ infrastructure/application/index.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/doc/how-to/how-to-grant-metabase-permissions.md b/doc/how-to/how-to-grant-metabase-permissions.md index 9bde8702b9..1cce89ccfa 100644 --- a/doc/how-to/how-to-grant-metabase-permissions.md +++ b/doc/how-to/how-to-grant-metabase-permissions.md @@ -24,6 +24,8 @@ The password for Staging and Production databases can be found the OSL 1Password The username and password for Metabase are not controlled via IaC - they are manually entered via the Metabase "Admin" dashboard (`Admin Setting > Databases > "staging" | "production" > Username / Password fields`). +Please note - this is separate to the role used to read/write Metabase internal application data (such as dashboard and queries). This role is setup in IaC [here](https://github.com/theopensystemslab/planx-new/blob/main/infrastructure/application/index.ts#L100). For more information, please see [the Metabase docs](https://www.metabase.com/docs/latest/installation-and-operation/configuring-application-database). + ### Locally & Pizzas If you wish to run Metabase locally using the "analytics" Docker profile (`pnpm analytics` from project root), you will need to manually run the above SQL on your local database with a password of your choice. Alternatively, you can use the root DB username/password. diff --git a/infrastructure/application/index.ts b/infrastructure/application/index.ts index e203d5923b..6149575a7d 100644 --- a/infrastructure/application/index.ts +++ b/infrastructure/application/index.ts @@ -97,6 +97,10 @@ export = async () => { superuser: false, }); const metabasePgPassword = config.requireSecret("metabasePgPassword"); + + // Setup role and database for internal Metabase application data, such as dashboards and queries + // This is separate to the postgres/public one used to hold PlanX application data + // Docs: https://www.metabase.com/docs/latest/installation-and-operation/configuring-application-database const role = new postgres.Role( "metabase", { From 2cd3917e941f0234ea9b1c02941ed67ab3e6c977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Wed, 6 Mar 2024 10:38:36 +0000 Subject: [PATCH 5/6] chore: Remove tables not referenced in Metabase --- .../migrations/1709565833346_run_sql_migration/down.sql | 5 ----- .../migrations/1709565833346_run_sql_migration/up.sql | 5 ----- 2 files changed, 10 deletions(-) diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql index bab445011e..830ae4e8a6 100644 --- a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql @@ -3,11 +3,6 @@ REVOKE SELECT ON public.analytics_summary FROM metabase_read_only; REVOKE SELECT ON public.feedback_summary FROM metabase_read_only; REVOKE SELECT ON public.submission_services_summary FROM metabase_read_only; --- Revoke select permissions from tables used by Metabase -REVOKE SELECT ON public.flows FROM metabase_read_only; -REVOKE SELECT ON public.published_flows FROM metabase_read_only; -REVOKE SELECT ON public.teams FROM metabase_read_only; - -- Revoke usage on schema REVOKE USAGE ON SCHEMA public FROM metabase_read_only; diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql index fe6c708c9e..1e41baf9b4 100644 --- a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql @@ -4,11 +4,6 @@ CREATE ROLE metabase_read_only; -- Grant usage on schema GRANT USAGE ON SCHEMA public TO metabase_read_only; --- Grant select permissions on public tables used by Metabase (useful for joins) -GRANT SELECT ON public.flows TO metabase_read_only; -GRANT SELECT ON public.published_flows TO metabase_read_only; -GRANT SELECT ON public.teams TO metabase_read_only; - -- Grant select permissions on views used by Metabase GRANT SELECT ON public.analytics_summary TO metabase_read_only; GRANT SELECT ON public.feedback_summary TO metabase_read_only; From 39d43291c42c53d78c2bfa55832b6d9fda72cd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Wed, 13 Mar 2024 11:14:22 +0000 Subject: [PATCH 6/6] chore: Grant access to analytics and analytics_logs --- .../migrations/1709565833346_run_sql_migration/down.sql | 4 ++++ .../migrations/1709565833346_run_sql_migration/up.sql | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql index 830ae4e8a6..2d6d0e9244 100644 --- a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/down.sql @@ -1,3 +1,7 @@ +-- Revoke select permissions from tables used by Metabase +REVOKE SELECT ON public.analytics FROM metabase_read_only; +REVOKE SELECT ON public.analytics_logs FROM metabase_read_only; + -- Revoke select permissions from views used by Metabase REVOKE SELECT ON public.analytics_summary FROM metabase_read_only; REVOKE SELECT ON public.feedback_summary FROM metabase_read_only; diff --git a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql index 1e41baf9b4..ade696cb4b 100644 --- a/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql +++ b/hasura.planx.uk/migrations/1709565833346_run_sql_migration/up.sql @@ -4,6 +4,10 @@ CREATE ROLE metabase_read_only; -- Grant usage on schema GRANT USAGE ON SCHEMA public TO metabase_read_only; +-- (Temp) Grant select permissions on tables used by Metabase in current SQL queries +GRANT SELECT ON public.analytics TO metabase_read_only; +GRANT SELECT ON public.analytics_logs TO metabase_read_only; + -- Grant select permissions on views used by Metabase GRANT SELECT ON public.analytics_summary TO metabase_read_only; GRANT SELECT ON public.feedback_summary TO metabase_read_only;