docs(quick-260909-eor): alle 227 Datenbankzugriffe klassifiziert und maschinell abgesichert
WINDOWS #18/#20, Aufgabe 3: docs/mandantentrennung-zugriffsklassifikation.md haelt fuer jede der 227 this.prisma.*-Fundstellen (32 Dateien, zusammengefasst zu 59 Datei-Modell-Paaren) eine Klasse fest — muss-mandantengebunden (31), keine-mandantengebundene-tabelle (16), bewusst-uebergreifend (3, mit ausgeschriebenem Grund) oder beides (9, der Hintergrunddienst-Sonderfall: uebergreifend lesen, je Zeile mandantengebunden schreiben — betrifft ldap.service.ts, tender-digest.scheduler.ts, tender-matching.service.ts). rls-access-inventory.spec.ts ermittelt die Fundstellen bei jedem Testlauf neu aus dem Quelltext und vergleicht sie gegen die Tabelle im Dokument — Datei und Modellname als Schluessel, keine Zeilennummer. Scheitert nachweislich, sobald eine Fundstelle fehlt oder ein Eintrag verwaist (per Testlauf geprueft, danach zurueckgesetzt). Zwei belegte Befunde im Dokument festgehalten: req.tenantPrisma wird gesetzt, aber nirgends gelesen; WINDOWS #19 (nullbares tenantId bei SearchProvider/ TenderRssFeedSource) bleibt benannter Blocker fuer Etappe 3. docs/mandantentrennung-datenbankrolle.md verweist jetzt auf das neue Dokument und korrigiert die ueberholte Zahl 182 auf den nachgemessenen Stand (227/59). WINDOWS.md #18/#19 um Nachtrag auf diesen Plan ergaenzt; #20 (der in Aufgabe 1 gemessene und behobene forTenant()-Verbindungsdefekt) als "fixed" markiert. Deviation (Rule 3, blockierend fuer die Bestandsaufnahme-Pruefung): auth.service.ts-Kommentar umformuliert, der zuvor woertlich "this.prisma.user.findUnique" als erklaerenden Text enthielt und dadurch einen Eigentreffer der grep-basierten Inventur-Pruefung erzeugte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# Mandantentrennung — Zugriffsklassifikation
|
||||
|
||||
Dieses Dokument gehört zusammen mit `docs/mandantentrennung-datenbankrolle.md`
|
||||
zur Vorbereitung der Mandantentrennung auf Datenbankebene (WINDOWS #18/#20).
|
||||
Während die Datenbankrolle-Anleitung beschreibt, **wie** die Umstellung
|
||||
technisch abläuft, hält dieses Dokument fest, **welche** der 227
|
||||
`this.prisma.*`-Fundstellen in `apps/api/src` beim Umbau in den folgenden
|
||||
Etappen angefasst werden müssen und welche bewusst unverändert bleiben.
|
||||
|
||||
Die Bestandsaufnahme unten wird **maschinell aus dem Quelltext ermittelt**
|
||||
(nicht von Hand zusammengeschrieben) und durch
|
||||
`apps/api/src/prisma/rls-access-inventory.spec.ts` bei jedem Testlauf gegen
|
||||
den tatsächlichen Stand geprüft. Verschiebt sich eine Zeile, bleibt die
|
||||
Prüfung grün — Vergleichsschlüssel sind Datei und Prisma-Modellname, keine
|
||||
Zeilennummer. Kommt eine neue Fundstelle hinzu oder verschwindet eine
|
||||
bestehende, schlägt die Prüfung fehl, bis dieses Dokument nachgezogen wird.
|
||||
|
||||
## Die drei Klassen
|
||||
|
||||
- **`muss-mandantengebunden`** — berührt auf Rechnung genau eines Mandanten
|
||||
eine Tabelle mit `tenantId` (oder eine Tabelle, die ihre Mandantenregel
|
||||
über einen Join auf eine solche Tabelle bezieht). Muss in Etappe 2 auf
|
||||
`forTenant()` umgestellt werden.
|
||||
- **`bewusst-uebergreifend`** — muss über Mandanten hinweg sehen, mit
|
||||
ausgeschriebenem Grund. Braucht in Etappe 3 eine sichtbare Kennzeichnung
|
||||
(Systemkontext), aber keinen `forTenant()`-Umbau.
|
||||
- **`keine-mandantengebundene-tabelle`** — betrifft eines der acht Modelle
|
||||
ohne `tenantId` bzw. eine bewusst plattformweite Tabelle (D-03). Kein
|
||||
Umbau nötig.
|
||||
- **`beides`** — ein vierter, im Plan ausdrücklich verlangter Sonderfall:
|
||||
ein Hintergrunddienst, der zurecht über alle Mandanten hinweg eine Liste
|
||||
aufbaut (bewusst übergreifend), aber *innerhalb* der Schleife je Mandant
|
||||
binden muss (muss mandantengebunden werden). Beide Anteile sind in
|
||||
derselben Datei vorhanden; die Fundstelle bekommt hier eine
|
||||
Sammelklassifikation, die Aufteilung auf Zeilenebene steht in der
|
||||
Begründung.
|
||||
|
||||
## Zwei belegte Befunde
|
||||
|
||||
**`req.tenantPrisma` wird gesetzt, aber nirgends gelesen.**
|
||||
`tenant.middleware.ts:44` und `tenant.guard.ts:41` setzen
|
||||
`req.tenantPrisma = forTenant(this.prisma, tenantId)`. Eine Volltextsuche
|
||||
über `apps/api/src` nach `tenantPrisma` außerhalb dieser beiden Dateien und
|
||||
ihrer Tests findet keine lesende Stelle — kein Controller greift darauf zu.
|
||||
Die Verdrahtung besteht, wird aber nicht genutzt. Für Etappe 2 ist zu
|
||||
entscheiden, ob die Controller künftig darüber gehen (dann bräuchte es keinen
|
||||
zweiten `forTenant()`-Aufruf je Service-Methode) oder ob der Weg ersatzlos
|
||||
entfällt. Dieser Plan entscheidet das nicht, hält den Befund nur fest.
|
||||
|
||||
**WINDOWS #19 — nullbares `tenantId` bei `SearchProvider` und
|
||||
`TenderRssFeedSource`.** Beide Modelle tragen ein nullbares `tenantId`
|
||||
(`SearchProvider` für admin-gepflegte Vorgabe-Suchmaschinen, wobei laut
|
||||
05-02-Entscheidung die tatsächlichen Vorgaben als Konstanten und nicht als
|
||||
DB-Zeilen mit `tenantId = NULL` geführt werden — die Spalte ist nullbar,
|
||||
ob es heute tatsächlich `NULL`-Zeilen gibt, ist damit eine offene Frage für
|
||||
Etappe 3, nicht eine hier beantwortete; `TenderRssFeedSource` für
|
||||
plattformweite RSS-Quellen wie den geseedeten `service.bund.de`-Feed, D-06).
|
||||
Die aktuelle Policy `"tenantId" = current_tenant_id()` vergleicht `NULL`
|
||||
nie gleich — nach dem Scharfschalten wären plattformweite Zeilen für JEDEN
|
||||
Mandanten unsichtbar, nicht nur für fremde. Das ist heute ohne Wirkung
|
||||
(Schalter aus, #18), muss aber in Etappe 3 zusammen mit den restlichen
|
||||
Fundstellen gelöst werden: die Policy braucht für den Lesezugriff
|
||||
`tenantId IS NULL OR tenantId = current_tenant_id()`, während Schreibzugriffe
|
||||
weiterhin einen Mandanten verlangen.
|
||||
|
||||
## Übersicht je Bereich (Zeilentreffer, `this.prisma.*` ohne Specs)
|
||||
|
||||
Gemessen mit `grep -ro "this\.prisma\.[a-zA-Z]*" apps/api/src/<bereich> | grep -v spec | wc -l`
|
||||
am 2026-09-09, **nach** den Änderungen aus Aufgabe 1/2 dieses Plans:
|
||||
|
||||
| Bereich | Treffer | Hinweis |
|
||||
|---|---|---|
|
||||
| tenders | 62 | unverändert gegenüber measured_baseline |
|
||||
| groups | 37 | unverändert |
|
||||
| ldap | 21 | unverändert |
|
||||
| dkv | 21 | unverändert |
|
||||
| user | 17 | unverändert |
|
||||
| module-registry | 17 | unverändert |
|
||||
| dashboard | 13 | unverändert |
|
||||
| auth | 8 | **war 13 in measured_baseline** — Aufgabe 2 hat 3 Lesezugriffe durch `auth_lookup_*()`-Funktionsaufrufe (`$queryRaw`, kein `this.prisma.<Modell>`) ersetzt und 5 Schreibzugriffe auf `forTenant()`-gebundene Aufrufe (`tenantPrisma.*`, ebenfalls kein `this.prisma.<Modell>`) umgestellt |
|
||||
| calendar | 12 | unverändert |
|
||||
| tenant | 8 | unverändert |
|
||||
| favorites | 7 | unverändert |
|
||||
| settings | 4 | unverändert |
|
||||
| **Summe** | **227** | war 232 in measured_baseline, Delta = die 5 in Aufgabe 2 verschwundenen `auth`-Treffer minus ein bereits vorher fehlerhaft mitgezähltes Kommentarvorkommen in der neuen Kopfzeile von `validateUser()`, das bewusst umformuliert wurde, um einen Eigentreffer der Bestandsaufnahme-Prüfung zu vermeiden |
|
||||
|
||||
## Klassen-Verteilung (nach (Datei, Modell)-Fundstellen, 59 Paare)
|
||||
|
||||
| Klasse | Anzahl Paare |
|
||||
|---|---|
|
||||
| muss-mandantengebunden | 31 |
|
||||
| keine-mandantengebundene-tabelle | 16 |
|
||||
| beides | 9 |
|
||||
| bewusst-uebergreifend | 3 |
|
||||
| **Summe** | **59** |
|
||||
|
||||
## Der Hintergrunddienst als Falle — drei `beides`-Fälle
|
||||
|
||||
Ein Planer, der über alle Mandanten iteriert, liest zu Recht übergreifend —
|
||||
muss aber *innerhalb* der Schleife je Mandant binden. Drei Dateien sind
|
||||
betroffen:
|
||||
|
||||
- **`ldap.service.ts`** (AD-Abgleich): iteriert nicht selbst über alle
|
||||
Mandanten (der Sync läuft je Aufruf für einen übergebenen Mandanten), aber
|
||||
innerhalb der Sync-Methoden bleiben Lesezugriffe auf `group`, `ldapConfig`
|
||||
und `user` teils ungebunden, obwohl der Mandant zu diesem Zeitpunkt bereits
|
||||
bekannt ist — die 4 echten `forTenant()`-Aufrufstellen (Zeilen 762, 905,
|
||||
1179, 1342) decken nur einen Teil der Lese-/Schreibpfade ab. Das ist der im
|
||||
Plankontext benannte Kern von WINDOWS #20: genau dieser Löschzweig
|
||||
(~Zeile 1559) deutet Leere nach dem Scharfschalten als "Gruppe im
|
||||
Verzeichnis verschwunden".
|
||||
- **`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
|
||||
Job selbst — so von Anfang an entworfen, Pitfall 1 in den Kommentaren der
|
||||
Datei). Innerhalb der Verteilung je Treffer ist der Mandant aus der Zeile
|
||||
bekannt und der Versand muss darauf gebunden laufen.
|
||||
- **`tender-matching.service.ts`** (Ausschreibungs-Sofortmeldung): dieselbe
|
||||
Form — `tenderMatch`/`tenderSavedSearch`/`user` werden für die
|
||||
Sofort-Benachrichtigung über alle betroffenen Mandanten hinweg gelesen,
|
||||
der Versand je Treffer ist mandantengebunden.
|
||||
|
||||
## Bestandsaufnahme
|
||||
|
||||
Maschinell ermittelt, `rls-access-inventory.spec.ts` hält Vollständigkeit
|
||||
nach. Spalten: Datei, Modell (Prisma-Modellname wie in `this.prisma.<Modell>`
|
||||
verwendet), Klasse, Begründung.
|
||||
|
||||
| Datei | Modell | Klasse | Begründung |
|
||||
|---|---|---|---|
|
||||
| apps/api/src/auth/auth.service.ts | user | muss-mandantengebunden | `getMe`, `changePassword`, `adminResetPassword` suchen über die Benutzerkennung aus dem Sitzungsnachweis — der Mandant ist dort bereits bekannt (Aufgabe 2 fasst sie bewusst nicht an, siehe SUMMARY). |
|
||||
| apps/api/src/calendar/calendar.service.ts | calendarSource | muss-mandantengebunden | Kalenderquellen eines Nutzers, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/dashboard/dashboard.service.ts | dashboardLayout | muss-mandantengebunden | Widget-Anordnung eines Nutzers, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/dashboard/dashboard.service.ts | module | keine-mandantengebundene-tabelle | Modulkatalog ist plattformweit, kein `tenantId` (Migration 20260909140000, Gruppe b). |
|
||||
| apps/api/src/dashboard/dashboard.service.ts | searchProvider | muss-mandantengebunden | `tenantId` nullbar (WINDOWS #19) — heutige, tatsächlich gespeicherte Zeilen sind nutzerangelegt und tragen einen Mandanten; Vorgabe-Anbieter kommen laut 05-02 aus Konstanten, nicht aus der DB. |
|
||||
| apps/api/src/dashboard/dashboard.service.ts | widgetInstance | muss-mandantengebunden | Platzierte Dashboard-Widgets eines Nutzers, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/dkv/dkv.service.ts | dkvInvoiceHistory | muss-mandantengebunden | DKV-Rechnungshistorie je Mandant, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/dkv/dkv.service.ts | dkvModuleConfig | muss-mandantengebunden | Postfach-/Zugangsdaten des DKV-Moduls je Mandant. |
|
||||
| apps/api/src/dkv/dkv.service.ts | dkvVehicleMaster | muss-mandantengebunden | Fahrzeugstammdaten des DKV-Moduls je Mandant. |
|
||||
| apps/api/src/favorites/favorites.service.ts | favoriteLink | muss-mandantengebunden | Favoriten-Links eines Nutzers, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/groups/groups.service.ts | group | muss-mandantengebunden | Gruppen sind je Mandant, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/groups/groups.service.ts | groupMembership | muss-mandantengebunden | Kein eigenes `tenantId`, RLS über Join auf `Group` (Migration 20260618112133-Nachfolger) — braucht trotzdem `forTenant()`, damit der Join-Kontext gesetzt ist. |
|
||||
| apps/api/src/groups/groups.service.ts | moduleGrant | muss-mandantengebunden | Modulfreigaben je Mandant, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/groups/groups.service.ts | user | muss-mandantengebunden | Nutzerverwaltung innerhalb eines Mandanten. |
|
||||
| apps/api/src/groups/module-grants.service.ts | group | muss-mandantengebunden | Wie groups.service.ts. |
|
||||
| apps/api/src/groups/module-grants.service.ts | groupMembership | muss-mandantengebunden | Kein eigenes `tenantId`, RLS über Join auf `Group`. |
|
||||
| apps/api/src/groups/module-grants.service.ts | moduleGrant | muss-mandantengebunden | Modulfreigaben je Mandant. |
|
||||
| apps/api/src/groups/module-grants.service.ts | tenantModuleActivation | muss-mandantengebunden | Welche Module ein Mandant aktiviert hat, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/groups/module-grants.service.ts | user | muss-mandantengebunden | Zielbenutzer eines Grants innerhalb des Mandanten. |
|
||||
| apps/api/src/ldap/ldap-config.service.ts | ldapConfig | muss-mandantengebunden | LDAP-Konfiguration je Mandant, `tenantId`-Spalte (unique) vorhanden. |
|
||||
| apps/api/src/ldap/ldap-config.service.ts | ldapFieldMapping | muss-mandantengebunden | Kein eigenes `tenantId`, RLS über Join auf `LdapConfig`. |
|
||||
| apps/api/src/ldap/ldap.service.ts | group | beides | AD-Abgleich: 4 echte `forTenant()`-Aufrufstellen decken einen Teil ab, weitere `this.prisma.group`-Zugriffe innerhalb der Sync-Methoden bleiben ungebunden, obwohl der Mandant zu diesem Zeitpunkt bekannt ist (siehe Abschnitt "Der Hintergrunddienst als Falle"). |
|
||||
| apps/api/src/ldap/ldap.service.ts | ldapConfig | beides | Dieselbe Begründung wie `group` — Konfigurationszugriffe innerhalb der Sync-Methoden. |
|
||||
| apps/api/src/ldap/ldap.service.ts | user | beides | Dieselbe Begründung — der Löschzweig um Zeile 1559 (WINDOWS #20) ist der konkrete Risikofall. |
|
||||
| apps/api/src/module-registry/module-access.service.ts | module | keine-mandantengebundene-tabelle | Modulkatalog ist plattformweit, kein `tenantId`. |
|
||||
| apps/api/src/module-registry/module-access.service.ts | moduleGrant | muss-mandantengebunden | Modulfreigaben je Mandant. |
|
||||
| apps/api/src/module-registry/module-access.service.ts | tenantModuleActivation | muss-mandantengebunden | Aktivierung je Mandant, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/module-registry/module-registry.service.ts | module | keine-mandantengebundene-tabelle | Modulkatalog ist plattformweit. |
|
||||
| apps/api/src/module-registry/module-registry.service.ts | tenantModuleActivation | muss-mandantengebunden | Aktivierung je Mandant. |
|
||||
| apps/api/src/settings/settings.service.ts | smtpConfig | muss-mandantengebunden | SMTP-Zugangsdaten je Mandant, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/tenant/tenant.controller.ts | tenant | keine-mandantengebundene-tabelle | `Tenant` ist die Mandantentabelle selbst — hat keine eigene `tenantId`-Spalte, kann sie per Definition nicht haben (Migration 20260909140000, Gruppe b). |
|
||||
| apps/api/src/tenant/tenant.service.ts | tenant | keine-mandantengebundene-tabelle | Dieselbe Begründung. |
|
||||
| apps/api/src/tenders/adapters/email-alert.adapter.ts | tenderEmailConfig | bewusst-uebergreifend | `fetchTenders()` liest bewusst jede aktive `TenderEmailConfig`-Zeile über ALLE Mandanten in einer Abfrage (Plattform-Scheduler, ein Tick pro Postfach, D-13/D-01) — ausführlich im Dateikopf begründet, darf laut Kommentar niemals in `forTenant()` verpackt werden. |
|
||||
| apps/api/src/tenders/adapters/rss.adapter.ts | tenderRssFeedSource | bewusst-uebergreifend | Fan-out über jeden aktiven Feed, plattformweit UND persönlich, in einer Abfrage (Zeilen 55–83 im Dateikopf begründet) — dieselbe Scheduler-Ebene wie beim E-Mail-Adapter. |
|
||||
| apps/api/src/tenders/tender-dedup.service.ts | tender | keine-mandantengebundene-tabelle | Explizit im Dateikopf: "platform-global, RLS-exempt tables. Never wrap these queries in forTenant()." (D-03) |
|
||||
| apps/api/src/tenders/tender-dedup.service.ts | tenderSource | keine-mandantengebundene-tabelle | Dieselbe Begründung. |
|
||||
| apps/api/src/tenders/tender-digest.scheduler.ts | tenderMatch | beides | Ein einziger globaler Cron-Job liest über ALLE Mandanten (bewusst übergreifend, Pitfall-1-Kommentar im Dateikopf), der Versand je Treffer ist an dessen Mandanten gebunden. |
|
||||
| apps/api/src/tenders/tender-digest.scheduler.ts | tenderNotificationPref | beides | Dieselbe Begründung — Präferenzen werden über alle Mandanten gelesen, aber je Zeile mandantenbezogen ausgewertet. |
|
||||
| apps/api/src/tenders/tender-digest.scheduler.ts | user | beides | E-Mail-Adressen für den Versand werden über alle Mandanten gelesen, der eigentliche Versand ist je Treffer mandantengebunden. |
|
||||
| apps/api/src/tenders/tender-email-config.service.ts | tenderEmailConfig | muss-mandantengebunden | Nutzer-CRUD für die eigene Postfachanbindung (Phase 17, D-01) — anders als der Fan-out-Adapter oben, hier ist der Mandant aus der Anfrage bekannt. |
|
||||
| apps/api/src/tenders/tender-fingerprint-backfill.service.ts | tender | keine-mandantengebundene-tabelle | Einmaliges Backfill-Skript über den plattformweiten `Tender`-Katalog (D-03). |
|
||||
| apps/api/src/tenders/tender-ingestion.service.ts | tender | keine-mandantengebundene-tabelle | Explizit im Dateikopf: "Multi-tenant safety (D-03, T-10-09): uses the plain, non-tenant-scoped ... queries ... these are platform-global". |
|
||||
| apps/api/src/tenders/tender-ingestion.service.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | Plattformweiter Poll-Status, kein `tenantId` (Migration 20260909140000, Gruppe b). |
|
||||
| apps/api/src/tenders/tender-matching.service.ts | tender | keine-mandantengebundene-tabelle | Liest den plattformweiten Katalog (D-03), um Treffer zu berechnen — kein `tenantId`. |
|
||||
| apps/api/src/tenders/tender-matching.service.ts | tenderMatch | beides | Sofortmeldung: Treffer über alle betroffenen Mandanten gelesen, Versand je Treffer mandantengebunden — siehe Abschnitt "Der Hintergrunddienst als Falle". |
|
||||
| apps/api/src/tenders/tender-matching.service.ts | tenderSavedSearch | beides | Dieselbe Begründung — gespeicherte Suchprofile aller Mandanten werden gegen neue Treffer geprüft, der Versand ist je Profil mandantengebunden. |
|
||||
| apps/api/src/tenders/tender-matching.service.ts | user | beides | Dieselbe Begründung — E-Mail-Adressen für die Sofortmeldung. |
|
||||
| apps/api/src/tenders/tender-notification-pref.service.ts | tenderNotificationPref | muss-mandantengebunden | Nutzer-CRUD für die eigenen Benachrichtigungseinstellungen — Mandant aus der Anfrage bekannt. |
|
||||
| apps/api/src/tenders/tender-rss-feed.service.ts | tenderRssFeedSource | muss-mandantengebunden | Nutzer-CRUD für die eigenen RSS-Quellen (anders als der Fan-out in `adapters/rss.adapter.ts`) — Mandant aus der Anfrage bekannt. WINDOWS #19 betrifft die nullbaren plattformweiten Zeilen, nicht diesen CRUD-Pfad. |
|
||||
| apps/api/src/tenders/tender-saved-search.service.ts | tenderSavedSearch | muss-mandantengebunden | Nutzer-CRUD für die eigenen gespeicherten Suchprofile — Mandant aus der Anfrage bekannt. |
|
||||
| apps/api/src/tenders/tender-scheduler.service.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | Plattformweiter DÖE-Poll-Status, kein `tenantId` — im Dateikopf explizit als "genuine platform-wide singleton" begründet. |
|
||||
| apps/api/src/tenders/tenders.controller.ts | tender | keine-mandantengebundene-tabelle | Lesezugriff auf den plattformweiten Katalog (D-03). |
|
||||
| apps/api/src/tenders/tenders.controller.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | Plattformweiter Poll-Status, admin-verwaltet, kein `tenantId`. |
|
||||
| apps/api/src/tenders/tenders.module.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | Singleton-Bestückung beim Boot — im Dateikopf explizit als "global, RLS-exempt (D-03)" begründet. |
|
||||
| apps/api/src/tenders/tender-triage.service.ts | tenderTriage | muss-mandantengebunden | Favorisierungs-/Ablehnungsstatus eines Nutzers, `tenantId`-Spalte vorhanden. |
|
||||
| apps/api/src/user/admin-seed.service.ts | tenant | keine-mandantengebundene-tabelle | Legt beim ersten Start den Standard-Mandanten selbst an — `Tenant` hat keine `tenantId`-Spalte. |
|
||||
| apps/api/src/user/admin-seed.service.ts | user | bewusst-uebergreifend | Erstanlage des Administrators beim ersten Start: läuft einmalig beim Boot, BEVOR irgendein Mandantenkontext existiert, um den allerersten Mandanten samt Admin-Nutzer anzulegen — es gibt zu diesem Zeitpunkt strukturell keinen Mandanten, an den gebunden werden könnte. |
|
||||
| apps/api/src/user/user.controller.ts | user | muss-mandantengebunden | Nutzerverwaltung innerhalb des Mandanten des anfragenden Admins. |
|
||||
| apps/api/src/user/user.service.ts | user | muss-mandantengebunden | Dieselbe Begründung. |
|
||||
|
||||
## Was diese Etappe NICHT entscheidet
|
||||
|
||||
- Ob Controller künftig über `req.tenantPrisma` statt eines erneuten
|
||||
`forTenant()`-Aufrufs im Service gehen (offener Befund oben).
|
||||
- Wie die WINDOWS-#19-Policy für `SearchProvider`/`TenderRssFeedSource`
|
||||
am Ende genau lautet — nur, dass sie vor dem Scharfschalten gelöst sein
|
||||
muss.
|
||||
- Die Reihenfolge und Zuschnitt der Etappe-2-Pläne — dafür ist die
|
||||
Klassen-Verteilung oben der Arbeitsvorrat, siehe `<next_stages>` im
|
||||
Plan `260909-eor-PLAN.md`.
|
||||
Reference in New Issue
Block a user