-- Grundlage für einen nackten PostgreSQL-Container. -- -- Läuft einmal, beim allerersten Start des `db`-Dienstes: das offizielle -- Postgres-Abbild führt alles unter /docker-entrypoint-initdb.d aus, solange -- das Datenverzeichnis noch leer ist. Bei jedem weiteren Start passiert hier -- nichts mehr — Änderungen daran wirken also nur auf einem frischen Band. -- -- Was hier steht, sind die Voraussetzungen, die auf Supabase von der Plattform -- kamen und die ein eigener Server selbst mitbringen muss. -- Das Passwort der Anwendungsrolle kommt aus der Umgebung, damit es nicht in -- einer Datei im Repository steht. `\set` mit Backticks ist psql-eigene -- Ersetzung — sie läuft, bevor der Server die Zeile sieht. \set app_passwort `echo "$APP_DB_PASSWORD"` select set_config('init.app_passwort', :'app_passwort', false); do $$ declare v_passwort text := current_setting('init.app_passwort', true); begin -- Lieber hier laut scheitern als eine Rolle mit leerem Passwort anlegen. -- Der Container käme sonst hoch, die Anwendung verbände sich nicht, und der -- Grund stünde nirgends. if coalesce(v_passwort, '') = '' then raise exception 'APP_DB_PASSWORD ist nicht gesetzt — ohne Passwort keine Anwendungsrolle.'; end if; -- ── Die Rolle, mit der die Anwendung arbeitet ─────────────────────── -- -- **Ohne BYPASSRLS, und das ist der Punkt.** Die Zugriffsrechte dieser -- Anwendung liegen in RLS-Policies; fehlt der Sitzungskontext, sollen sie -- nichts zurückgeben statt alles. Eine Rolle, die RLS umgeht, machte die -- ganze Absicherung wirkungslos — und niemand würde es merken, weil alles -- weiter funktioniert, nur eben zu viel. if not exists (select 1 from pg_roles where rolname = 'alpenwerk_app') then execute format('create role alpenwerk_app login password %L', v_passwort); else execute format('alter role alpenwerk_app with login password %L', v_passwort); end if; execute 'alter role alpenwerk_app with nobypassrls'; -- ── Rollen, die es auf Supabase gab ───────────────────────────────── -- -- `anon`, `authenticated` und `service_role` gehören zu Supabase, nicht zu -- dieser Anwendung. Keine einzige Policy nennt sie — sie tragen nur Rechte, -- die die Plattform pauschal vergeben hat. -- -- Angelegt werden sie trotzdem, und zwar **ohne Anmelderecht**: ein -- Datenabzug aus dem alten Bestand enthält `grant`-Anweisungen auf diese -- Namen, und ohne die Rollen bricht das Einspielen ab. Anmelden kann sich -- damit niemand, sie sind reine Platzhalter für die Wiederherstellung. if not exists (select 1 from pg_roles where rolname = 'anon') then execute 'create role anon nologin'; end if; if not exists (select 1 from pg_roles where rolname = 'authenticated') then execute 'create role authenticated nologin'; end if; if not exists (select 1 from pg_roles where rolname = 'service_role') then execute 'create role service_role nologin'; end if; end $$; select set_config('init.app_passwort', '', false); -- ── Erweiterungen ───────────────────────────────────────────────────── -- -- Nur diese beiden werden gebraucht. `uuid-ossp` stand auf Supabase zwar -- bereit, wird aber nirgends benutzt (keine Spaltenvorgabe, keine Funktion -- ruft uuid_generate_*); die Kennungen kommen aus gen_random_uuid(). -- -- Beide liegen in `public`, nicht in einem eigenen `extensions`-Schema: der -- search_path der Anwendung und aller SECURITY-DEFINER-Funktionen ist auf -- `public, pg_temp` festgenagelt, ein anderes Schema wäre dort unsichtbar. create extension if not exists pgcrypto; create extension if not exists pg_trgm; -- ── Rechte am Schema ────────────────────────────────────────────────── -- -- Die Migrationen vergeben ihre Tabellenrechte selbst. Was sie voraussetzen, -- ist das Recht, das Schema überhaupt zu betreten. grant usage on schema public to alpenwerk_app;