feat(jts-03): module-grants.service.ts binden, beide Dokumente schliessen
Alle fuenf Methoden von ModuleGrantsService (assertTargetBelongsToTenant,
grant, revoke, getMatrix, getUserAccess) laufen jetzt ueber forTenant(); bei
den beiden Datenlieferungen teilen sich alle parallel abgesetzten
Teilabfragen denselben gebundenen Client. Die Mandanten-Gegenpruefung vor
jedem Erteilen bleibt ausdruecklich bestehen und bekommt einen Verweis auf
Befund F/T-JTS-03: die Regel auf ModuleGrant prueft nur die
Mandantenkennung der Zeile, nicht die referenzierte Gruppe. Der veraltete
Kommentar ueber der Mitgliedschaftsabfrage im Benutzer-Detail ("kein
forTenant hier") ist durch den neuen Stand ersetzt.
module-grants.service.spec.ts bekommt denselben Bindungsnachweis-Mock wie
groups.service.spec.ts (zwei unterscheidbare Clients ueber demselben
Speicher) und sechs neue Bindungsnachweise; alle 28 Bestandstests bleiben
gruen.
Beide Dokumente geschlossen: die Bereichsuebersicht fuer groups ist neu
gemessen (0 ungebunden, 31 gebunden — ein dokumentierter methodischer
Bodensatz, da die einfache Rohtrefferzaehlung die neun ueber `tx` gebundenen
Zugriffe innerhalb der drei Transaktionen nicht sieht), die
Klassen-Verteilung auf 62 Paare aktualisiert, und der als offen gefuehrte
Befund D aus dem ldap-Abschnitt der Fehlerrichtung ist mit Verweis auf
diesen Durchlauf als erledigt vermerkt (Nachtrag, nicht Neuschrieb). 743
Tests und die Typpruefung gruen; Schema, Migrationen und alle vier
Compose-Dateien unveraendert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
This commit is contained in:
@@ -146,6 +146,15 @@ interpretieren:
|
||||
ohne Standardgruppe zurück. `groups` ist ohnehin als nächster Bereich der
|
||||
Etappe 2 vorgesehen.
|
||||
|
||||
**Nachtrag (260909-jts, Aufgabe 3): GESCHLOSSEN.** Der Bereich `groups`
|
||||
ist umgestellt — `reassignDefaultBeforeDelete` und `ensureDefaultGroup`
|
||||
laufen seit Aufgabe 2 dieses Plans vollständig über `forTenant()` bzw.
|
||||
`withTenantTransaction()` (siehe Abschnitt "Bereich groups" unten und
|
||||
`docs/mandantentrennung-zugriffsklassifikation.md`, Zeile
|
||||
`groups.service.ts`/`group`, Stand `gebunden`). Die Reihenfolgebedingung
|
||||
für Etappe 4 ist damit erfüllt. Der Befund oben bleibt unverändert stehen
|
||||
— er beschreibt korrekt den Zustand zum Zeitpunkt der ldap-Umstellung.
|
||||
|
||||
- **Die offene Architekturfrage `req.tenantPrisma`.** `tenant.middleware.ts`
|
||||
und `tenant.guard.ts` setzen `req.tenantPrisma = forTenant(...)`, aber
|
||||
kein Controller liest diesen Wert je. Dieser Durchlauf entscheidet NICHT,
|
||||
|
||||
@@ -80,12 +80,24 @@ Bestandsaufnahme unten.
|
||||
Gemessen mit
|
||||
`grep -ro "this\.prisma\.[a-zA-Z]*" apps/api/src/<bereich> | grep -v spec | wc -l`
|
||||
bzw. `grep -ro "tenantPrisma\.[a-zA-Z]*\." apps/api/src/<bereich> | grep -v spec | wc -l`
|
||||
am 2026-09-09, **nach** den Änderungen aus Aufgabe 2/3 dieses Plans (260909-ipc):
|
||||
am 2026-09-09, **nach** den Änderungen aus Aufgabe 2/3 dieses Plans (260909-jts):
|
||||
|
||||
**Methodische Lücke, seit 260909-jts sichtbar:** die zweite Zählung sucht
|
||||
ausschließlich den Namen `tenantPrisma` — die Konvention, die `ldap` und
|
||||
(bis auf die drei Transaktionen) auch `groups` verwenden. Die drei
|
||||
`withTenantTransaction()`-Aufrufe in `groups.service.ts` binden zusätzliche
|
||||
neun Modellzugriffe über den Namen `tx` (den Transaktionsparameter), die
|
||||
diese einfache Rohtrefferzählung strukturell NICHT sieht — anders als die
|
||||
maschinelle, namensunabhängige Erkennung in `rls-access-inventory.spec.ts`
|
||||
(Befund B), die auch diese Form erfasst. Die Zahl 31 unten ist deshalb der
|
||||
Bodensatz, nicht die vollständige Zahl gebundener Zugriffe in `groups`; die
|
||||
Bestandsaufnahme unten (Spalte "Stand", je (Datei, Modell)-Paar) ist die
|
||||
autoritative Quelle.
|
||||
|
||||
| Bereich | Ungebunden | Gebunden | Hinweis |
|
||||
|---|---|---|---|
|
||||
| tenders | 62 | 0 | unverändert |
|
||||
| groups | 37 | 0 | unverändert |
|
||||
| groups | 0 | 31 | **war 37/0** — Aufgabe 2/3 (260909-jts) haben `groups.service.ts` (12 Methoden) und `module-grants.service.ts` (5 Methoden) vollständig auf `forTenant()`/`withTenantTransaction()` umgestellt. Die neun zusätzlichen, über `tx` gebundenen Zugriffe innerhalb der drei Transaktionen zählt dieses einfache Muster nicht mit (siehe Methodenhinweis oben) |
|
||||
| ldap | 4 | 26 | **war 21/0** — Aufgabe 2/3 (260909-ipc) haben `ldap-config.service.ts` (5 Methoden) und `ldap.service.ts` (6 Methoden, 11 Abfragen) auf `forTenant()` umgestellt. Die 4 verbleibenden ungebundenen Treffer sind bewusst: `getAllActiveConfigs`/`onApplicationBootstrap` (Befund B) und `resolveEmailForWrite` (Befund A, T-IPC-04) |
|
||||
| dkv | 21 | 0 | unverändert |
|
||||
| user | 17 | 0 | unverändert |
|
||||
@@ -96,26 +108,29 @@ am 2026-09-09, **nach** den Änderungen aus Aufgabe 2/3 dieses Plans (260909-ipc
|
||||
| tenant | 8 | 0 | unverändert |
|
||||
| favorites | 7 | 0 | unverändert |
|
||||
| settings | 4 | 0 | unverändert |
|
||||
| **Summe** | **210** | **31** | Ungebunden: war 227 vor dieser Etappe (260909-eor-Stand), Delta = die 17 in Aufgabe 2/3 umgestellten `ldap`-Rohtreffer. Gebunden: war 5 (nur `auth`), jetzt zusätzlich 26 in `ldap` |
|
||||
| **Summe** | **173** | **62** | Ungebunden: war 210 vor dieser Etappe (260909-ipc-Stand), Delta = die 37 in Aufgabe 2/3 (260909-jts) umgestellten `groups`-Rohtreffer. Gebunden: war 31, jetzt zusätzlich 31 in `groups` (Bodensatz — siehe Methodenhinweis oben) |
|
||||
|
||||
## Klassen-Verteilung (nach (Datei, Modell)-Fundstellen, 61 Paare)
|
||||
## Klassen-Verteilung (nach (Datei, Modell)-Fundstellen, 62 Paare)
|
||||
|
||||
Stand 260909-ipc (Aufgabe 2): 59 Paare aus der urspruenglichen Zaehlung plus
|
||||
zwei bisher unentdeckte, weil bereits gebundene Fundstellen
|
||||
(`auth.service.ts`/`passwordResetToken`, `ldap.service.ts`/`groupMembership`),
|
||||
die erst die um gebundene Zugriffe erweiterte Erkennung (Befund G) sichtbar
|
||||
macht — sie waren nie Teil der 227 `this.prisma.*`-Rohtrefferzahl, weil sie
|
||||
schon vor diesem Plan über `forTenant()` liefen. Dazu die Korrektur von
|
||||
(`ldap-config.service.ts`, `ldapConfig`) von `muss-mandantengebunden` auf
|
||||
`beides` (Befund B).
|
||||
Stand 260909-jts (Aufgabe 3): 61 Paare aus dem vorherigen Durchlauf
|
||||
(260909-ipc) plus ein bisher vollstaendig unsichtbares Paar
|
||||
(`groups.service.ts`/`tenantModuleActivation`), das erst die um
|
||||
Transaktionsparameter erweiterte Erkennung (Befund B, Aufgabe 2 dieses
|
||||
Plans) sichtbar macht — der einzige Zugriff auf dieses Modell in dieser
|
||||
Datei lief bis dahin ausschliesslich ueber den Rueckgabeparameter der
|
||||
interaktiven Transaktion in `ensureDefaultGroup` und war weder ueber
|
||||
`this.prisma.<Modell>` noch ueber `<gebundener Client>.<Modell>` erfassbar.
|
||||
Die Zahl ist der Ausgabe der Pruefung in
|
||||
`apps/api/src/prisma/rls-access-inventory.spec.ts` entnommen, nicht
|
||||
geschaetzt.
|
||||
|
||||
| Klasse | Anzahl Paare |
|
||||
|---|---|
|
||||
| muss-mandantengebunden | 32 |
|
||||
| muss-mandantengebunden | 33 |
|
||||
| keine-mandantengebundene-tabelle | 16 |
|
||||
| beides | 10 |
|
||||
| bewusst-uebergreifend | 3 |
|
||||
| **Summe** | **61** |
|
||||
| **Summe** | **62** |
|
||||
|
||||
## Der Hintergrunddienst als Falle — drei `beides`-Fälle
|
||||
|
||||
@@ -134,16 +149,17 @@ betroffen:
|
||||
`resolveEmailForWrite` bewusst ungebunden (Befund A, T-IPC-04 — siehe
|
||||
Bestandsaufnahme unten). Der als gefährlichster Punkt benannte
|
||||
Löschzweig (`syncBoundGroupsForTenant`, WINDOWS #20) war bereits seit
|
||||
Etappe 1 gebunden. Offen bleibt eine Reihenfolgebedingung für Etappe 4,
|
||||
NICHT Teil dieser Umstellung: die Übergabe unmittelbar vor der Löschung —
|
||||
`this.groupsService.reassignDefaultBeforeDelete(tenantId, group.id)` und
|
||||
`ensureDefaultGroup(tenantId)` — liegt in `groups.service.ts` und ist
|
||||
nicht gebunden. Nach dem Scharfschalten würde `reassignDefaultBeforeDelete`
|
||||
still `false` melden (kein Ersatzkandidat sichtbar), der Standard-Marker
|
||||
wandert nicht mit, und der Mandant bliebe nach einer Gruppenlöschung ohne
|
||||
Standardgruppe zurück — der Bereich `groups` muss deshalb vor Etappe 4
|
||||
umgestellt sein (siehe `docs/mandantentrennung-etappe2-fehlerrichtung.md`,
|
||||
Abschnitt (e), Befund D).
|
||||
Etappe 1 gebunden. **Die seinerzeit offen geführte Reihenfolgebedingung
|
||||
für Etappe 4 — die Übergabe unmittelbar vor der Löschung
|
||||
(`this.groupsService.reassignDefaultBeforeDelete(tenantId, group.id)` und
|
||||
`ensureDefaultGroup(tenantId)` in `groups.service.ts`) — ist mit
|
||||
260909-jts (Aufgabe 2) GESCHLOSSEN:** beide Methoden laufen seither über
|
||||
`forTenant()`/`withTenantTransaction()` (siehe Bestandsaufnahme unten,
|
||||
`groups.service.ts`/`group`, Stand `gebunden`). Nachtrag, nicht
|
||||
Neuschrieb: der ursprüngliche Befund bleibt oben lesbar, weil er den
|
||||
Zustand zum Zeitpunkt der ldap-Umstellung korrekt beschreibt und für
|
||||
spätere Etappen als Beleg dient, siehe auch
|
||||
`docs/mandantentrennung-etappe2-fehlerrichtung.md`, Abschnitt (e), Befund D.
|
||||
- **`tender-digest.scheduler.ts`** (Ausschreibungs-Digest): liest
|
||||
`tenderMatch`/`tenderNotificationPref`/`user` bewusst über ALLE Mandanten
|
||||
in einem `findMany` (ein einziger globaler Cron-Job, kein Mandant im
|
||||
@@ -181,11 +197,11 @@ verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit
|
||||
| apps/api/src/groups/groups.service.ts | moduleGrant | muss-mandantengebunden | gebunden | Modulfreigaben je Mandant, `tenantId`-Spalte vorhanden. Seit 260909-jts gebunden, einschliesslich des Zugriffs innerhalb des Standardgruppen-Aufbaus (Befund B). |
|
||||
| apps/api/src/groups/groups.service.ts | tenantModuleActivation | muss-mandantengebunden | gebunden | Welche Module ein Mandant aktiviert hat. Bis 260909-jts vollstaendig unsichtbar (Befund B, 260909-jts-PLAN.md): der einzige Zugriff dieser Datei lief innerhalb der interaktiven Transaktion von `ensureDefaultGroup` ueber den Rueckgabeparameter (`tx.tenantModuleActivation.findMany`) — weder `this.prisma.<Modell>` noch `<gebundener Client>.<Modell>` sahen das, weil `tx` weder `this.prisma` noch aus einer `forTenant(`-Zuweisung stammte. Die um Transaktionsparameter erweiterte Erkennung aus Aufgabe 2 macht dieses Paar erstmals sichtbar; die Stelle ist seit derselben Aufgabe gebunden (ueber `withTenantTransaction()`). |
|
||||
| apps/api/src/groups/groups.service.ts | user | muss-mandantengebunden | gebunden | Nutzerverwaltung innerhalb eines Mandanten. Seit 260909-jts gebunden; `addUserToDefaultGroup` prueft seither zusaetzlich, dass der Zielbenutzer zum Mandanten gehoert (Befund E, T-JTS-02) — die Regel auf `GroupMembership` prueft nachweislich nur die Gruppenseite. |
|
||||
| apps/api/src/groups/module-grants.service.ts | group | muss-mandantengebunden | ungebunden | Wie groups.service.ts. |
|
||||
| apps/api/src/groups/module-grants.service.ts | groupMembership | muss-mandantengebunden | ungebunden | Kein eigenes `tenantId`, RLS über Join auf `Group`. |
|
||||
| apps/api/src/groups/module-grants.service.ts | moduleGrant | muss-mandantengebunden | ungebunden | Modulfreigaben je Mandant. |
|
||||
| apps/api/src/groups/module-grants.service.ts | tenantModuleActivation | muss-mandantengebunden | ungebunden | Welche Module ein Mandant aktiviert hat, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/groups/module-grants.service.ts | user | muss-mandantengebunden | ungebunden | Zielbenutzer eines Grants innerhalb des Mandanten. |
|
||||
| apps/api/src/groups/module-grants.service.ts | group | muss-mandantengebunden | gebunden | Wie groups.service.ts. Seit 260909-jts (Aufgabe 3) gebunden — `assertTargetBelongsToTenant` erzeugt seinen eigenen Kontext, `getMatrix` teilt sich einen Kontext mit den beiden anderen parallelen Teilabfragen. |
|
||||
| apps/api/src/groups/module-grants.service.ts | groupMembership | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS über Join auf `Group`. Seit 260909-jts gebunden; der `where`-Filter über die Beziehung zur Gruppe bleibt zusätzlich stehen, weil die Regel auf dieser Tabelle nachweislich nur die Gruppenseite prüft (Aufgabe 1). |
|
||||
| apps/api/src/groups/module-grants.service.ts | moduleGrant | muss-mandantengebunden | gebunden | Modulfreigaben je Mandant. Seit 260909-jts gebunden; die Mandanten-Gegenprüfung vor jedem Erteilen (`assertTargetBelongsToTenant`) bleibt zusätzlich bestehen, weil die Regel auf dieser Tabelle nur die Mandantenkennung der Zeile prüft, nicht die referenzierte Gruppe (Befund F, T-JTS-03, Aufgabe 1). |
|
||||
| apps/api/src/groups/module-grants.service.ts | tenantModuleActivation | muss-mandantengebunden | gebunden | Welche Module ein Mandant aktiviert hat, `tenantId`-Spalte vorhanden. Seit 260909-jts gebunden. |
|
||||
| apps/api/src/groups/module-grants.service.ts | user | muss-mandantengebunden | gebunden | Zielbenutzer eines Grants innerhalb des Mandanten. Seit 260909-jts gebunden; die Mandanten-Gegenprüfung bleibt bestehen. |
|
||||
| apps/api/src/ldap/ldap-config.service.ts | ldapConfig | beides | gemischt | `getConfig`, `createConfig` und `updateConfig` laufen ueber `forTenant()`, gebunden an den aus der Anfrage bereits bekannten Mandanten. `getAllActiveConfigs()` (Planer-Lesezugriff ueber ALLE Mandanten) und die Start-Nachverschluesselung in `onApplicationBootstrap` bleiben bewusst uebergreifend: beide laufen, bevor bzw. unabhaengig davon, ob ein einzelner Mandantenkontext feststeht (Befund B, 260909-ipc-PLAN.md). Korrektur der Klasse von `muss-mandantengebunden`: das Paar ist tatsaechlich `beides`, keine Verhaltensaenderung. |
|
||||
| apps/api/src/ldap/ldap-config.service.ts | ldapFieldMapping | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS ueber Join auf `LdapConfig`. `addFieldMapping` und `removeFieldMapping` nehmen den Mandanten jetzt als Parameter entgegen und laufen vollstaendig ueber `forTenant()` (T-IPC-01, 260909-ipc-PLAN.md) — schliesst zugleich die Fremdzugriffsluecke beim Loeschen einer Feldzuordnung ueber ihre Kennung. |
|
||||
| apps/api/src/ldap/ldap.service.ts | group | beides | gebunden | AD-Abgleich: `listGroups` (die "bereits importiert"-Markierung) und `importGroupsByDn` (die Idempotenzpruefung ueber `ldapObjectGuid`) sind mit Aufgabe 3 (260909-ipc) auf `forTenant()` umgestellt — zusammen mit den bereits vorher gebundenen Stellen (Anlage, Mitgliedschafts- und Gruppenabgleich) ist damit jeder `group`-Zugriff dieser Datei gebunden. Die Klasse bleibt `beides`, weil ein zukuenftiger uebergreifender Lesezugriff (z. B. ein neuer Planer-Pfad) hier ebenso legitim waere wie bei `ldapConfig` unten — nicht, weil heute noch ein ungebundener Zugriff bestuende. |
|
||||
@@ -235,7 +251,11 @@ verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit
|
||||
`ldap` (260909-ipc) hat sich für den Dienst-internen Weg entschieden —
|
||||
`forTenant(this.prisma, tenantId)` wird in jeder umgestellten Methode neu
|
||||
erzeugt, wie es die vier Bestandsstellen in `ldap.service.ts` und die drei
|
||||
in `auth.service.ts` bereits vormachten. Die Frage bleibt für alle
|
||||
in `auth.service.ts` bereits vormachten. Der Bereich `groups` (260909-jts)
|
||||
hat sich für denselben dienst-internen Weg entschieden — jede Methode in
|
||||
`groups.service.ts` und `module-grants.service.ts` erzeugt ihren eigenen
|
||||
`forTenant()`- bzw. `withTenantTransaction()`-Aufruf, gebundene Clients
|
||||
werden nicht zwischen Methoden weitergereicht. Die Frage bleibt für alle
|
||||
übrigen Bereiche der Etappe 2 offen.
|
||||
- Wie die WINDOWS-#19-Policy für `SearchProvider`/`TenderRssFeedSource`
|
||||
am Ende genau lautet — nur, dass sie vor dem Scharfschalten gelöst sein
|
||||
|
||||
Reference in New Issue
Block a user