Legt den kompletten Unterbau an, auf dem die Fachmodule aufsetzen. Betrieb: - docker-compose.yml mit Traefik, App, Worker, PostgreSQL 17 + PostGIS, Redis, MinIO und Gotenberg; Entwicklungs-Override ohne Traefik, dafuer mit Mailpit und Adminer - Mehrstufiges Dockerfile: eigener Benutzer, tini als PID 1, Healthcheck - .env.example mit einem Kommentar zu jeder Variable Mandantentrennung: - Row Level Security auf allen mandantenpflichtigen Tabellen - Zwei getrennte Datenbankverbindungen: florian_app mit aktiver RLS fuer die Anwendung, Eigentuemer-Zugang nur fuer Migration, Seed und Anmeldung - fuerOrganisation() als einziger Weg zu Fachdaten Nachvollziehbarkeit: - AuditLog append-only per Trigger; UPDATE ist dauerhaft gesperrt, DELETE nur im gekennzeichneten Aufbewahrungslauf - Geheimnisse werden vor dem Schreiben entfernt Anmeldung: - OIDC gegen ein extern betriebenes authentik, Rollen aus Gruppen nur als Vorbelegung beim ersten Login - Lokaler E-Mail/Passwort-Login als Rueckfallweg - Terminal per PIN und RFID mit zwei Schranken (Geraet, dann Person) Weiteres: - lib/authz.ts mit 9 Rollen und 38 Einzelrechten - lib/storage.ts (MinIO), lib/audit.ts, lib/warteschlange.ts (BullMQ) - Integrationen fuer Mail, Telegram und LLM hinter abschaltbaren Interfaces - tRPC v11, Basis-Layout mit Seitenleiste und dunkler Darstellung - Seed mit einer Beispielwehr: 16 Personen, 3 Fahrzeuge, 40 Geraete - 32 Vitest-Tests (davon 8 gegen eine echte Datenbank), 10 Playwright-Tests - README, Anleitung mit Screenshots, Roadmap, Aufwandsschaetzung, Devlog, Versionierungsregeln und Changelog Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
61 lines
2.4 KiB
PL/PgSQL
61 lines
2.4 KiB
PL/PgSQL
-- =============================================================================
|
|
-- Kontrollierte Ausnahme vom Änderungsverbot des Audit-Logs.
|
|
--
|
|
-- Ausgangslage: Der Trigger aus 20260902000100_rls_policies verbietet UPDATE
|
|
-- und DELETE auf audit_log. Das ist richtig — nachträglich verändern soll den
|
|
-- Log niemand.
|
|
--
|
|
-- Es gibt aber genau einen Vorgang, der löschen können muss: der
|
|
-- Aufbewahrungsjob des Workers. Nach AUDIT_AUFBEWAHRUNG_TAGE exportiert er die
|
|
-- alten Einträge nach MinIO und räumt sie danach ab. Ohne diese Möglichkeit
|
|
-- wächst die Tabelle unbegrenzt, und das Löschkonzept nach DSGVO ließe sich
|
|
-- nicht umsetzen.
|
|
--
|
|
-- Der Ausweg ist ein ausdrücklicher Schalter, den nur der Eigentümer-Zugang
|
|
-- setzen kann und der nur für die laufende Transaktion gilt:
|
|
--
|
|
-- BEGIN;
|
|
-- SELECT set_config('app.audit_bereinigung', 'an', true);
|
|
-- DELETE FROM audit_log WHERE zeitpunkt < now() - interval '10 years';
|
|
-- COMMIT;
|
|
--
|
|
-- UPDATE bleibt unter allen Umständen verboten. Ein Eintrag kann verschwinden,
|
|
-- nachdem er gesichert wurde — verfälscht werden kann er nie.
|
|
-- =============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION audit_log_unveraenderlich()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
BEGIN
|
|
-- Änderungen sind ausnahmslos gesperrt.
|
|
IF TG_OP = 'UPDATE' THEN
|
|
RAISE EXCEPTION
|
|
'Der Audit-Log ist unveraenderlich. UPDATE ist auf audit_log nicht zulaessig.'
|
|
USING ERRCODE = 'insufficient_privilege';
|
|
END IF;
|
|
|
|
-- Löschen nur im ausdrücklich gekennzeichneten Aufbewahrungslauf.
|
|
IF TG_OP = 'DELETE' THEN
|
|
IF coalesce(current_setting('app.audit_bereinigung', true), '') = 'an' THEN
|
|
RETURN OLD;
|
|
END IF;
|
|
|
|
RAISE EXCEPTION
|
|
'Der Audit-Log ist unveraenderlich. DELETE ist nur im Aufbewahrungslauf zulaessig '
|
|
'(SET LOCAL app.audit_bereinigung = ''an'').'
|
|
USING ERRCODE = 'insufficient_privilege';
|
|
END IF;
|
|
|
|
RETURN NULL;
|
|
END;
|
|
$$;
|
|
|
|
COMMENT ON FUNCTION audit_log_unveraenderlich() IS
|
|
'Sperrt UPDATE auf audit_log dauerhaft und DELETE ausserhalb des Aufbewahrungslaufs.';
|
|
|
|
-- Die Anwendungsrolle darf den Schalter gar nicht erst setzen: sie hat kein
|
|
-- DELETE-Recht auf audit_log (siehe vorherige Migration). Der Schalter wirkt
|
|
-- also nur fuer den Eigentuemer-Zugang, den ausschliesslich Worker und
|
|
-- Migrationen benutzen.
|