docs(quick-260911-gwh): Etappe 2 auf Endstand bringen -- sechster Fall, Befund K erfuellt, Ledger, Anleitung
- .planning/WINDOWS.md: drei neue offene Eintraege (#30 Startpfad des Mailmoduls, #31 verschluckte Leere favorites, #32 verschluckte Leere settings); Platzhalter WINDOWS #TBD-GWH in settings.service.ts und mail.module.ts durch #30 ersetzt - docs/mandantentrennung-zugriffsklassifikation.md: Uebersichtszeilen favorites (0/8, war 7/0) und settings (1/3, war 4/0) neu gemessen; Summenzeile 68/178 (Endstand Etappe 2); Bestandsaufnahme (favoriteLink gebunden, smtpConfig gemischt, neue Zeile widgetInstance/gebunden); Klassen-Verteilung 65 Paare (33/17/13/2); Hintergrunddienst-Abschnitt mit sechstem Fall (mail.module.ts, WINDOWS #30) und erfuellter Befund-K-Bedingung; neuer Punkt in "Was diese Etappe NICHT entscheidet" - docs/mandantentrennung-etappe2-fehlerrichtung.md: Nachtraege unter Befund K in (t4) und im Uebergaben-Absatz von (d4) -- Reihenfolgebedingung erfuellt; ## Etappe 2 -- Abschluss mit den derivierten Endzahlen - docs/anleitung-entwicklung.md: 23 RLS-Tabellen statt sieben, FavoriteLink nicht mehr als Tabelle ohne Regel, tenantPrisma statt manuellem tenantId-Filter als gelebter Stil - rls-access-inventory.spec.ts wieder gruen (11/11), volle Suite 994/994, Werkzeug 137/137; zwei Dokument-Falsifizierungen durchgefuehrt und zurueckgenommen (siehe SUMMARY) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
This commit is contained in:
@@ -308,21 +308,25 @@ Prisma-Client mehr und veröffentlicht keinen auf dem Anfrageobjekt.
|
||||
> Express-Middleware mit identischer Logik — beides wurde mit 260911-e2s entfernt, nachdem eine
|
||||
> Volltextsuche keinen Leser dieser Eigenschaft außerhalb der beiden Dateien fand.
|
||||
|
||||
`app.current_tenant` wird von **Postgres Row-Level-Security** ausgewertet. RLS-Policies sind aber
|
||||
**nicht** auf allen Tabellen aktiv — aktuell nur auf `User`, `PasswordResetToken`, `LdapConfig`,
|
||||
`LdapFieldMapping`, `Group`, `GroupMembership` und `ModuleGrant` (siehe die Migrationen
|
||||
`20260618112133_rls_policies` und `20260804130918_groups_rls_policies`). Alle übrigen
|
||||
mandantenbezogenen Tabellen — u. a. `DkvVehicleMaster`, `DkvInvoiceHistory`, `CalendarSource`,
|
||||
`Tender`, `TenderSavedSearch`, `FavoriteLink` — tragen zwar eine `tenantId`-Spalte, aber **keine**
|
||||
RLS-Policy.
|
||||
`app.current_tenant` wird von **Postgres Row-Level-Security** ausgewertet. RLS-Policies liegen
|
||||
seit `20260909140000_rls_remaining_tenant_tables` auf 23 Tabellen (4 aus
|
||||
`20260618112133_rls_policies`, 3 aus `20260804130918_groups_rls_policies`, 16 aus der
|
||||
`_rls_remaining_tenant_tables`-Migration selbst — `grep -c "ENABLE ROW LEVEL SECURITY"` über die
|
||||
drei Migrationen, zur Ausführungszeit nachzählen), darunter `FavoriteLink` und `SmtpConfig`. Ohne
|
||||
eigene `tenantId`-Spalte bzw. bewusst plattformweit bleiben `Module`, `Tenant`, `Tender`,
|
||||
`TenderSource` und `TenderSourcePollConfig` (siehe die Bestandsaufnahme in
|
||||
`docs/mandantentrennung-zugriffsklassifikation.md`, Klasse `keine-mandantengebundene-tabelle`, für
|
||||
die vollständige, maschinell geprüfte Liste — von dort ableiten, nicht raten).
|
||||
|
||||
**Was ein Entwickler nie vergessen darf:** Bei jeder Query gegen eine Tabelle ohne RLS-Policy muss
|
||||
`tenantId` **manuell** in die `where`-Klausel — die Datenbank filtert hier nichts von selbst. Das
|
||||
ist im Code auch der gelebte Stil: `DkvService.loadConfig()`
|
||||
(`apps/api/src/dkv/dkv.service.ts`) etwa nutzt den plain, UNGEBUNDENEN `PrismaService` und
|
||||
filtert explizit mit `where: { tenantId }`. Wer bei einer solchen Tabelle das `tenantId`-Filter
|
||||
vergisst, liest oder schreibt mandantenübergreifend — ohne dass RLS das auffängt. Bei den sieben
|
||||
RLS-geschützten Tabellen greift die DB-seitige Absicherung zusätzlich, vorausgesetzt die Query
|
||||
**Was ein Entwickler nie vergessen darf:** jeder Zugriff auf eine mandantengebundene Tabelle läuft
|
||||
dienst-intern über einen mit `forTenant()` gebundenen Klienten `tenantPrisma`
|
||||
(`apps/api/src/prisma/prisma-tenant.extension.ts`) — die zusätzlichen `where`-Filter über
|
||||
`userId` bleiben bestehen, wo die Regel selbst keine Benutzerdimension kennt (siehe
|
||||
`docs/mandantentrennung-etappe2-fehlerrichtung.md`). Bei den Tabellen ohne eigene `tenantId`
|
||||
(oben) filtert die Anwendung stattdessen — wo relevant — über den zutreffenden Bezug (z. B.
|
||||
plattformweiter Katalog, kein Mandantenfilter nötig); siehe
|
||||
`docs/mandantentrennung-zugriffsklassifikation.md` für den vollständigen Stand je Datei/Modell.
|
||||
Bei den RLS-geschützten Tabellen greift die DB-seitige Absicherung zusätzlich, vorausgesetzt die Query
|
||||
läuft tatsächlich über einen dienst-intern per `forTenant()` gebundenen Client und nicht über
|
||||
den globalen, ungebundenen `PrismaService`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user