#!/bin/bash # ============================================================================= # Läuft EINMALIG beim allerersten Start des Postgres-Containers (leeres # Datenverzeichnis). Richtet ein: # - die benötigten Erweiterungen (PostGIS, pgvector, pgcrypto, citext, # unaccent, pg_trgm) # - den Anwendungsbenutzer "florian_app" OHNE BYPASSRLS # - Vorgabeprivilegien, damit künftige Tabellen automatisch nutzbar sind # # Wichtig zum Verständnis der Mandantentrennung: # Der Eigentümer der Tabellen (POSTGRES_USER) umgeht Row Level Security — # das ist gewollt, damit Migrationen und das Seed-Skript arbeiten können. # Die Anwendung verbindet sich ausschließlich als "florian_app"; für den # greift RLS, und ohne gesetztes app.current_organisation sieht er nichts. # ============================================================================= set -euo pipefail APP_ROLE="florian_app" APP_PASSWORD="${POSTGRES_APP_PASSWORD:?POSTGRES_APP_PASSWORD muss gesetzt sein}" # Einfache Anführungszeichen im Passwort für SQL verdoppeln. APP_PASSWORD_SQL="${APP_PASSWORD//\'/\'\'}" echo "[florian-init] Richte Erweiterungen und Anwendungsrolle ein ..." # Erweiterungen. Reines SQL, keine Variablen nötig. psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-'EOSQL' CREATE EXTENSION IF NOT EXISTS postgis; -- Koordinaten, Einsatzorte CREATE EXTENSION IF NOT EXISTS pgcrypto; -- gen_random_uuid(), digest() CREATE EXTENSION IF NOT EXISTS citext; -- E-Mail ohne Gross/Klein CREATE EXTENSION IF NOT EXISTS unaccent; -- Suche ueber Umlaute hinweg CREATE EXTENSION IF NOT EXISTS pg_trgm; -- unscharfe Namenssuche CREATE EXTENSION IF NOT EXISTS vector; -- semantische Suche im Wiki EOSQL # Anwendungsrolle anlegen. Bewusst OHNE DO-Block: psql ersetzt seine Variablen # nicht innerhalb dollar-quotierter Zeichenketten. Stattdessen erzeugt die # Abfrage den CREATE-Befehl als Text, den \gexec dann ausführt — und zwar nur, # wenn die Rolle noch nicht existiert. psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL SELECT format( 'CREATE ROLE %I LOGIN PASSWORD %L NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS', '${APP_ROLE}', '${APP_PASSWORD_SQL}' ) WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${APP_ROLE}') \gexec ALTER ROLE "${APP_ROLE}" PASSWORD '${APP_PASSWORD_SQL}'; ALTER ROLE "${APP_ROLE}" SET timezone TO 'Europe/Berlin'; EOSQL # Rechtevergabe. Hier funktionieren psql-Variablen, weil kein DO-Block im Spiel # ist. psql -v ON_ERROR_STOP=1 \ --username "$POSTGRES_USER" \ --dbname "$POSTGRES_DB" \ -v app_role="$APP_ROLE" \ -v datenbank="$POSTGRES_DB" \ -v owner="$POSTGRES_USER" <<-'EOSQL' GRANT CONNECT ON DATABASE :"datenbank" TO :"app_role"; GRANT USAGE ON SCHEMA public TO :"app_role"; -- Rechte auf alles, was heute schon existiert (bei Erstinstallation leer). GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO :"app_role"; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :"app_role"; -- Und auf alles, was der Eigentuemer kuenftig anlegt (Prisma-Migrationen). ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO :"app_role"; ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO :"app_role"; ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO :"app_role"; -- Die Anwendung darf keine Tabellen anlegen oder loeschen. REVOKE CREATE ON SCHEMA public FROM PUBLIC; REVOKE CREATE ON SCHEMA public FROM :"app_role"; ALTER DATABASE :"datenbank" SET timezone TO 'Europe/Berlin'; EOSQL echo "[florian-init] Anwendungsrolle '$APP_ROLE' eingerichtet (RLS wirksam)."