Day 10 of 10 · Harbour Mutual

Security, roles and backup

Protect the data: create roles and grant each the least it needs, hide personal data behind a view, let each broker see only their own clients with row-level security, and keep a copy before a risky change.

Theory for today: Data, privacy and regulation, Distribution, Transactions and security.

The lesson

Until now you have worked as postgres, the superuser, who may do anything. Nobody should work like that in production. Today you decide who can see and change what (see Data, privacy and regulation).

Roles

A role is a user or a group: CREATE ROLE name LOGIN for a person or program that connects, NOLOGIN for a group whose rights others inherit (GRANT group_role TO person;). A new role can do almost nothing until it is granted rights.

CREATE ROLE reporting NOLOGIN;

GRANT USAGE ON SCHEMA public TO reporting;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reporting;
USAGE on the schema lets the role see into it; SELECT lets it read. GRANT ... ON ALL TABLES covers the tables that exist now; ALTER DEFAULT PRIVILEGES covers future ones. Try it in the playground

REVOKE takes a right away. Rights can be as fine as single columns: GRANT SELECT (claim_id, status) ON claims TO someone;.

Testing as another role

SET ROLE name makes the rest of the session act as that role (the superuser may become anyone); RESET ROLE comes back. In this lab your role is reset after every run, so put SET ROLE and the queries to test in the same run.

SET ROLE reporting;

SELECT COUNT(*) FROM claims;
Reading works. Try it in the playground
SET ROLE reporting;

UPDATE claims SET reserve = 0;
permission denied for table claims: exactly what we want from a reporting tool. Try it in the playground

Views that minimise

A view runs with its owner's rights. Grant a role access to a view and not to its table, and the role sees only the columns and rows the view exposes: the database's way of doing data minimisation.

Row-level security

Row-level security (RLS) filters rows per role. Enable it on a table, then add policies: each says which rows a role may see (USING) or write (WITH CHECK). A role with no policy on the table sees no rows at all. The superuser and the table's owner are not filtered.

ALTER TABLE policies ENABLE ROW LEVEL SECURITY;

CREATE POLICY own_clients ON policies
    FOR SELECT TO broker_portal
    USING (broker_id = CURRENT_SETTING('app.broker_id')::INT);
The portal says which broker is logged in with SET app.broker_id = '3';, a setting of its session; the policy compares each row against it. Try it in the playground

Backups and snapshots

On a server, pg_dump writes a database, or chosen tables, to a file that psql or pg_restore can load again: pg_dump --format=custom --file=harbour.dump harbour. It runs outside SQL, so not in this browser. Inside SQL, before a risky change, take a snapshot: CREATE TABLE name AS SELECT ... keeps a copy you can compare with, or restore from. And wrap the change in a transaction: BEGIN; ... check ... COMMIT; or ROLLBACK;.

Grant roles, not people, and grant the least that works. It is far easier to add a right someone turns out to need than to discover one they should never have had.

Briefs from the business

0 of 5 done

Write SQL to answer each brief, run it, then check your answer against Harbour Mutual's. Your database is the same one as in the playground.

Starting PostgreSQL in your browser…

Brief 10.1 · A reporting role

The month-end reporting tool needs to read all of Harbour Mutual's tables and change none. Create a role reporting (it does not log in itself) with exactly that.

Tables: all tables

Brief 10.2 · Pricing without names

The pricing team studies customers by age and region, and under data minimisation it must not see names or birth dates. Create a view customer_profile with customer_id, region_id, age_band (the customer's age on 31 December 2025, rounded down to ten years: 30 for anyone 30 to 39) and customer_since, and a role pricing that can read the view but not the customers table.

Tables: customers, customer_profile (yours)

Brief 10.3 · Brokers see their own clients

Brokers get a portal on Harbour Mutual's policies. Create a role broker_portal that can read policies, and a row-level security policy so that it sees only the policies of the broker whose id is in the setting app.broker_id. The reporting role must still see every policy.

Tables: policies

Brief 10.4 · A snapshot before the reserve review

At year end the claims team will review every open claim's reserve. Before they start, keep a copy: a table reserves_2025_12_31 with the claim_id and reserve of every open claim, as they are now.

Tables: claims, reserves_2025_12_31 (yours)

Brief 10.5 · The access review

Every year the auditors ask who can read or change what. List every right granted on tables and views to the roles reporting, pricing and broker_portal: the role, the table and the privilege.

Tables: information_schema.role_table_grants

An unhandled error has occurred. Reload 🗙

Reconnecting…

Your connection to Data Analytics SQL Lab dropped, probably a Wi-Fi blip. Hold on while it reconnects. Your work is kept.

The connection could not be restored. The page will reload in a few seconds.