Roles and privileges

PostgreSQL · Practical · 12 min

Least-privilege roles limit the damage of a leaked password or an SQL injection bug.

In PostgreSQL users and groups are both roles. A role with LOGIN can connect; a role without it acts as a group that others are granted membership in. Roles are cluster-wide, shared by all databases.

Privileges are granted on objects:

  • GRANT CONNECT ON DATABASE shop TO app;
  • GRANT USAGE ON SCHEMA app TO app; — needed to see anything inside the schema.
  • GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app;
  • GRANT USAGE ON ALL SEQUENCES IN SCHEMA app TO app; for serial columns.

ON ALL TABLES only covers tables that exist now. For future tables, use ALTER DEFAULT PRIVILEGES — it applies to objects created later by the role that runs it (or FOR ROLE owner).

A clean pattern: a owner role (app_owner, no login) owns the schema and runs migrations; group roles app_rw and app_ro hold read-write and read-only privileges; login roles for services and people are members of those groups. Avoid letting the application connect as the owner or as a superuser: owners can drop tables and superusers bypass all checks, including row-level security.

Since PostgreSQL 14 the predefined roles pg_read_all_data and pg_write_all_data make read-only analytics users easy. Use \du and \dp in psql to inspect roles and table privileges.

Example

CREATE ROLE app_owner NOLOGIN;
CREATE ROLE app_ro NOLOGIN;
CREATE ROLE app_rw NOLOGIN;
CREATE SCHEMA app AUTHORIZATION app_owner;

GRANT USAGE ON SCHEMA app TO app_ro, app_rw;
-- tables app_owner creates later get these privileges automatically
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
    GRANT SELECT ON TABLES TO app_ro;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA app
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_rw;

CREATE ROLE api LOGIN PASSWORD 'change-me' IN ROLE app_rw;
CREATE ROLE analyst LOGIN PASSWORD 'change-me' IN ROLE app_ro;

SET ROLE app_owner;
CREATE TABLE app.orders (id int PRIMARY KEY);
RESET ROLE;
SELECT has_table_privilege('analyst', 'app.orders', 'SELECT');   -- t
SELECT has_table_privilege('analyst', 'app.orders', 'INSERT');   -- f

Remember

  • Users and groups are both roles; LOGIN makes a role a user
  • Schema USAGE plus table privileges are both needed
  • ALTER DEFAULT PRIVILEGES covers tables created in the future

Watch out for

  • Granting on ALL TABLES once and wondering why new tables are inaccessible.

Check yourself

  1. A role has SELECT on app.orders but queries fail with 'permission denied for schema app'. What's missing?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.postgresql.org/docs/current/ddl-priv.html