Ein normales GRANT SELECT ON ALL TABLES IN SCHEMA app TO reader; wirkt nur auf Tabellen, die in diesem Moment existieren. Sobald die nächste Migration eine neue Tabelle anlegt, fängt der Reader-User dort wieder bei Null an. Genau dafür gibt es ALTER DEFAULT PRIVILEGES: einmal eingerichtet, gilt die Regel automatisch für alle künftigen Objekte.

Das Problem in einem Beispiel

SQL GRANT alleine ist Stichtags-Schnappschuss
SET ROLE app_owner;
CREATE TABLE orders (id serial primary key);
GRANT SELECT ON ALL TABLES IN SCHEMA app TO reader;

-- Spaeter, in einer neuen Migration:
CREATE TABLE customers (id serial primary key);

SET ROLE reader;
SELECT * FROM customers;
-- ERROR: permission denied for table customers

reader hatte SELECT auf alle Tabellen, die zum Zeitpunkt des GRANT existierten — orders. Die später angelegte customers ist davon nicht betroffen.

Die Lösung: ALTER DEFAULT PRIVILEGES

SQL Defaults fuer kuenftige Objekte
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;

Übersetzt: „Ab jetzt — sobald jemand in Schema app eine neue Tabelle anlegt, soll reader automatisch SELECT bekommen.”

Damit greift die Regel auf alle zukünftig erzeugten Tabellen. Die existierenden bleiben unverändert; für die brauchst du weiterhin den klassischen GRANT SELECT ON ALL TABLES ….

Faustregel: bei einem neuen Read-Only-User immer beides ausführen:

SQL Komplettes Setup
-- 1. Bestehendes:
GRANT USAGE ON SCHEMA app TO reader;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO reader;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA app TO reader;

-- 2. Zukuenftiges:
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON SEQUENCES TO reader;

Worauf wirken Default Privileges?

Du kannst Defaults für die folgenden Objekttypen setzen:

ObjekttypMögliche Privilegien
TABLES (inkl. Views, Foreign Tables)SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
SEQUENCESUSAGE, SELECT, UPDATE
FUNCTIONS (inkl. Procedures)EXECUTE
TYPESUSAGE
SCHEMAS (ab PG 15)USAGE, CREATE

Der wichtigste Stolperstein: GRANTOR

Defaults sind immer an eine bestimmte erzeugende Rolle gebunden — den GRANTOR. In Klartext: die Regel greift nur, wenn die neue Tabelle von derselben Rolle erstellt wird, die das ALTER DEFAULT PRIVILEGES ausgeführt hat.

SQL GRANTOR ist entscheidend
-- Als app_owner:
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;

-- Sind app_owner Migrationen, greift die Regel.

-- Wenn aber eine andere Rolle (z. B. ein Migrations-User
-- "migrator") die Tabellen erstellt:
SET ROLE migrator;
CREATE TABLE app.invoices (...);
-- reader bekommt KEIN SELECT auf invoices.

Lösung: explizit für die richtige Rolle setzen.

SQL
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app
    GRANT SELECT ON TABLES TO reader;

Wer mehrere Rollen Tabellen erzeugen lässt, braucht das ALTER DEFAULT PRIVILEGES FOR ROLE … für jede dieser Rollen. Praktischer ist, das im Migrations-Setup zu vereinheitlichen: eine Owner-Rolle für alle Migrationen, einmal Defaults setzen, fertig.

Pro Schema vs. global

Ohne IN SCHEMA gilt die Regel datenbankweit — für jede zukünftige Tabelle in jedem Schema:

SQL
ALTER DEFAULT PRIVILEGES
    GRANT SELECT ON TABLES TO reader;

Das ist häufig zu großzügig. In den meisten Setups gibt es ein App-Schema, ein paar Werkzeug-Schemas (migrations, tmp) und das public-Schema. Pro-Schema-Defaults sind expliziter und sicherer.

Defaults inspizieren

Im psql:

SQL
myapp=# \ddp
                          Default access privileges
   Owner    | Schema | Type  |     Access privileges
------------+--------+-------+---------------------------
 app_owner  | app    | table | reader=r/app_owner
 app_owner  | app    | seq   | reader=r/app_owner

Oder direkt im Katalog:

SQL
SELECT pg_get_userbyid(defaclrole)   AS grantor,
       nspname                       AS schema,
       defaclobjtype                 AS object_type,
       defaclacl                     AS privileges
FROM pg_default_acl
LEFT JOIN pg_namespace ON pg_namespace.oid = defaclnamespace;

defaclobjtype-Werte: r = relation (Tabelle), S = sequence, f = function, T = type, n = schema.

Zurücknehmen

Symmetrisch:

SQL
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    REVOKE SELECT ON TABLES FROM reader;

Wichtig: das wirkt nur auf die Defaults selbst (für künftige Objekte). Schon vergebene Privilegien auf existierende Tabellen bleiben — die musst du separat mit klassischem REVOKE zurückziehen.

Besonderheiten

Defaults sind keine retroaktive Wirkung.

Sie greifen ab dem Moment, ab dem du sie setzt — und nur auf Objekte, die danach entstehen. Wer nur ALTER DEFAULT PRIVILEGES setzt und das normale GRANT … ON ALL … weglässt, hat existierende Tabellen unangetastet gelassen. Beide gehören zusammen.

Pro GRANTOR — die haeufigste „warum greift das nicht?“-Falle.

Wenn deine Migrationen unter migrator laufen, deine Default-Privileges aber als app_owner gesetzt wurden, passiert nichts. \ddp zeigt dir, welcher GRANTOR welche Defaults pflegt — wenn deine erstellende Rolle nicht in der Tabelle steht, ist das die Erklärung.

DROP OWNED BY entfernt auch Defaults der Rolle.

Wenn app_owner gedroppt wird (nach REASSIGN OWNED und DROP OWNED), verschwinden auch alle Default-Privilege-Definitionen, die unter seinem Namen liefen. Beim Aufräumen einer Rolle also vorher dokumentieren, welche Defaults sie pflegte — sonst muss das Setup beim nächsten Owner manuell wiederhergestellt werden.

In CI/Test-DBs Defaults gleich nach dem Anlegen setzen.

Wenn deine Test-DB nach jedem Lauf neu erstellt wird (etwa via docker-compose down -v), sind auch die Defaults weg. Setze sie als Teil des Bootstrap-Skripts (/docker-entrypoint-initdb.d/), sonst sind die ersten Migrationen ohne Reader-Rechte und Tests scheitern erst beim Read-Schritt.

Schema-Defaults gibt es erst seit PG 15.

ALTER DEFAULT PRIVILEGES … GRANT … ON SCHEMAS … ist neu. In früheren Versionen mussten Schemas einzeln per GRANT rechtefiziert werden. Wer noch auf älteren Versionen unterwegs ist, sollte beim Upgrade die Default-Privilege-Definitionen ergänzen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Server-Administration

Zur Übersicht