-- --------------------------------------------------------------------------- -- HANDGESCHRIEBEN. Prisma erzeugt hier zusaetzlich Loeschbefehle fuer die -- zusammengesetzten Fremdschluessel (Mandantengrenze), die Trigramm-Indizes -- (Archivsuche), die partiellen eindeutigen Indizes und alle Trigger: es kennt -- sie nicht, weil sie sich im Schema nicht ausdruecken lassen. Diese -- Loeschbefehle sind bewusst gestrichen. -- -- tests/datenbankregeln.test.ts prueft, dass alle diese Objekte vorhanden sind – -- ein versehentlich stehengelassener Loeschbefehl faellt dort sofort auf. -- --------------------------------------------------------------------------- -- Mitarbeiter-Selbstauskunft (#035). /* * Die fuenfte Rolle. * * `ALTER TYPE ... ADD VALUE` laeuft in PostgreSQL seit 12 auch innerhalb einer * Transaktion — Prisma fuehrt jede Migration in einer. Der neue Wert kommt * **ans Ende**: die Reihenfolge eines Enums ist seine Sortierreihenfolge, und * ein `MITARBEITER` zwischen `ADMIN` und `REDAKTEUR` wuerde jede Liste, die nach * Rolle sortiert, still umstellen. */ ALTER TYPE "Rolle" ADD VALUE 'MITARBEITER'; ALTER TABLE "Benutzer" ADD COLUMN "mitarbeiterId" TEXT; /* * **Eindeutig**: eine Person hat hoechstens ein Konto, mit dem sie sich selbst * ansieht. Ohne das koennten zwei Anmeldungen auf dieselbe Person zeigen, und * ein entzogener Zugang waere nicht entzogen, solange das zweite Konto bleibt. * * `NULL` gilt in Postgres als verschieden, die vielen Konten ohne Zuordnung * stoeren hier also nicht. */ CREATE UNIQUE INDEX "Benutzer_mitarbeiterId_key" ON "Benutzer"("mitarbeiterId"); ALTER TABLE "Benutzer" ADD CONSTRAINT "Benutzer_mitarbeiterId_fkey" FOREIGN KEY ("mitarbeiterId") REFERENCES "Mitarbeiter"("id") ON DELETE SET NULL ON UPDATE CASCADE; /* * **Die eigentliche Zusage dieses Meilensteins, in der Datenbank.** * * Die Rolle `MITARBEITER` und die Zuordnung gehoeren zusammen, in beide * Richtungen: * * - Ein `MITARBEITER` **ohne** `mitarbeiterId` haette ein Recht und niemanden, * auf den es sich bezieht. Die Lesemodelle schraenken auf diese Kennung ein; * fehlte sie, waere die Einschraenkung leer — und „leer“ heisst in SQL je nach * Formulierung „nichts“ oder „alles“. Das ist genau die Art Zweideutigkeit, * die hier nicht entstehen darf. * - Eine `mitarbeiterId` an einem `ADMIN` waere die Umkehrung: ein Konto, das * alles sieht und zusaetzlich als eine bestimmte Person gilt. Wer das braucht, * braucht zwei Konten. * * `SET NULL` am Fremdschluessel oben wuerde diese Bedingung verletzen, wenn ein * Mitarbeiter geloescht wird — deshalb der Trigger darunter, der die Rolle * mitzieht. Geloescht wird ein Mitarbeiter ohnehin nie (CLAUDE.md: nur * `aktiv = false`), aber eine Regel, die sich auf „kommt nicht vor“ verlaesst, * ist keine Regel. */ ALTER TABLE "Benutzer" ADD CONSTRAINT "Benutzer_mitarbeiter_nur_bei_rolle" CHECK (("rolle" = 'MITARBEITER') = ("mitarbeiterId" IS NOT NULL)); /* * Verliert ein Konto seine Person, verliert es auch die Rolle. * * Ohne das schlaegt das `ON DELETE SET NULL` des Fremdschluessels am CHECK * darueber fehl, und das Loeschen eines Mitarbeiters waere unmoeglich statt * folgerichtig. `LESER` und nicht `aktiv = false`: das Konto soll sich weiter * anmelden koennen, nur nichts Fremdes sehen — und wer sich anmeldet, bekommt * bei der naechsten OIDC-Anmeldung ohnehin eine neue Rolle zugewiesen. */ CREATE OR REPLACE FUNCTION laufzettel_konto_ohne_person() RETURNS trigger AS $$ BEGIN UPDATE "Benutzer" SET "rolle" = 'LESER', "mitarbeiterId" = NULL WHERE "mitarbeiterId" = OLD."id"; RETURN OLD; END; $$ LANGUAGE plpgsql; CREATE TRIGGER "Mitarbeiter_konto_mitziehen" BEFORE DELETE ON "Mitarbeiter" FOR EACH ROW EXECUTE FUNCTION laufzettel_konto_ohne_person();