docs(quick-260914-eym): Etappe 3c abgeschlossen — Kritikschrift, Klassifikation, Auftrag, Datenbankrolle, WINDOWS #21/#30 geschlossen, Single-Flight-Riegel als Eintrag
- Kritikschrift: neuer Abschnitt "## Systemkontext (Etappe 3c, 260914-eym)" mit (y1) woertlicher Werkzeugausgabe und pg_policies der lebenden DB, (y2) Signaltabelle beider Fehlerrichtungen samt Rueckbau-Belegen (a)-(d), (y3) Leere-als-Abwesenheit je Pfad (kein Pfad loescht), (y4) bewusst nicht geloest, (y5) bewusst nicht angefasst; Nachtraege in (d4), (s4), (b4) und im Abschluss - Klassifikation: Uebersichtstabelle mit dritter Spalte System, Werte nachgerechnet (61/179/5), Stand-Absatz 260914-eym (72 Paare, Klassen unveraendert, sieben Staende geaendert), sechs Regelschluesse im Hintergrunddienst-Abschnitt, admin-seed-Zeile mit 3c-Befund, 3c-Punkt unter "NICHT entscheidet" erledigt - Auftrag: 3c als Erledigt vermerkt (3d64567/6e2a641), zwei neue Fallen unter "Werkzeuge und Fallen" - Datenbankrolle: dritte Sitzungsvariable, Nachtrag zum Systemkontext und zur weiterhin gueltigen Vorher-Pruefung ohne-kontext-leer - Ledger (ueber gsd-tools windows): #21 fixed, #30 fixed, #37 neu (prozessweiter Single-Flight-Riegel processInbox) — open 15 / waived 1 / fixed 21 / total 37, aus den Zeilen gezaehlt Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
@@ -985,6 +985,13 @@ abgeschrieben.
|
||||
entscheidet sie nicht — er bindet dienst-intern, wie `ldap`, `groups` und
|
||||
`tenders` es vormachen.
|
||||
|
||||
**Nachtrag (260914-eym):** WINDOWS #21 ist GESCHLOSSEN — `loadActiveConfigsForScheduler()`
|
||||
liest über `forSystem()` (Systemleseregel auf DkvModuleConfig) ALLE aktiven
|
||||
Konfigurationen, der Planer registriert je Mandant einen eigenen Auftrag
|
||||
`dkv-inbox-poll:<tenantId>`; die Erwähnung des einen Auftragsnamens
|
||||
`dkv-inbox-poll` oben bleibt als historischer Stand stehen. Siehe
|
||||
`## Systemkontext (Etappe 3c, 260914-eym)`.
|
||||
|
||||
### (d5) Was dieser Durchlauf bewusst nicht anfasst
|
||||
|
||||
- **Die beiden mehrschrittigen Stellen bleiben unatomar (Befund C, TEIL
|
||||
@@ -3096,6 +3103,15 @@ unverändert und steht nicht in der Erlaubnisliste.
|
||||
`SmtpConfig`-Zeile über die Wartungsrolle lesen und den gebundenen
|
||||
`findUnique` daneben halten — dieselbe Form wie bei `dashboard`/`calendar`.
|
||||
|
||||
**Nachtrag (260914-eym):** WINDOWS #30 ist GESCHLOSSEN — nicht durch einen
|
||||
Systemkontext, sondern durch ENTFERNEN des Startpfads: die Mailer-Fabrik in
|
||||
`mail.module.ts` und die Startpfad-Methode in `settings.service.ts` sind
|
||||
gelöscht, `MailService` baut je Versand einen Transport aus
|
||||
`getDecryptedSmtpConfig(tenantId)` des Empfänger-Mandanten
|
||||
(`requestPasswordReset` reicht `user.tenantId` durch). Deshalb trägt
|
||||
SmtpConfig keine `system_read_policy`. Siehe
|
||||
`## Systemkontext (Etappe 3c, 260914-eym)`.
|
||||
|
||||
### (s5) Was dieser Durchlauf bewusst nicht anfasst
|
||||
|
||||
- `settings.controller.ts` — nur gelesen (siehe (s4)(d)).
|
||||
@@ -3214,6 +3230,11 @@ Forbidden zu NotFound.
|
||||
nichts, `tenderrssfeed-gemeinsame-zeile-ohne-benutzer-weiterhin-entfernbar`
|
||||
bestätigt das lediglich erneut.
|
||||
|
||||
**Nachtrag (260914-eym):** der erste Punkt ist eingelöst — der Systemkontext
|
||||
für Hintergrunddienste ist gebaut (`## Systemkontext (Etappe 3c, 260914-eym)`),
|
||||
`tender-digest.scheduler.ts` liest seine Kandidaten über `forSystem()` und
|
||||
bleibt in der Schleife gebunden.
|
||||
|
||||
### (b5) Was dieser Durchlauf bewusst nicht anfasst
|
||||
|
||||
- Die vier Tabellen mit `userId`-Spalte, die KEINE persönlichen Daten tragen
|
||||
@@ -3226,6 +3247,221 @@ Forbidden zu NotFound.
|
||||
- Der Schalter (`DATABASE_URL` → Rolle `tessera`, BYPASSRLS) — bleibt AUS.
|
||||
- Compose-/Umgebungsdateien — unangetastet.
|
||||
|
||||
## Systemkontext (Etappe 3c, 260914-eym)
|
||||
|
||||
Helfer-, Datenbank- und Dienstumbau: Migration `20260914120000_rls_system_context_read`
|
||||
bringt die Funktion `is_system_context()` und je betroffener Tabelle eine
|
||||
zusätzliche, NUR lesende Regel `system_read_policy … FOR SELECT` auf
|
||||
DkvModuleConfig, LdapConfig, LdapFieldMapping, TenderMatch und
|
||||
TenderSavedSearch. `forSystem(prisma)` ist der Schwesterhelfer von
|
||||
`forTenant()` (gleiche Array-Form-Bauart, setzt `app.system_context = 'true'`
|
||||
und die beiden anderen Sitzungsvariablen ausdrücklich leer; `forTenant()`
|
||||
und `withTenantTransaction()` setzen umgekehrt `app.system_context = ''`).
|
||||
Vier Dateien rufen ihn an fünf Stellen (Erlaubnisliste
|
||||
`FORSYSTEM_ALLOWED_CALL_SITES` im Detektor, exakte Zahl je Datei): der
|
||||
DKV-Planer-Startpfad (jetzt ein Cron-Auftrag je aktivem Mandanten,
|
||||
WINDOWS #21), beide Leser in `ldap-config.service.ts`, die Kandidatenabfrage
|
||||
des Digest, die Profilabfrage des Abgleichs. Der Mail-Startpfad ist nicht
|
||||
umgestellt, sondern ENTFERNT (Transport je Versand nach Mandant des
|
||||
Empfängers, WINDOWS #30); `admin-seed.service.ts` liest außerhalb seiner
|
||||
Schleife nur `Tenant` (keine Regel) und ist unverändert. Der Schalter bleibt
|
||||
AUS — nichts hiervon wirkt, bis Etappe 4 scharfschaltet.
|
||||
|
||||
### (y1) Die Messung
|
||||
|
||||
Wörtliche Werkzeugausgabe der vier Funktionsfälle (`rls-scratch-check.mjs`,
|
||||
Wegwerf-Rolle ohne BYPASSRLS, Funktion aus der Migration geschnitten):
|
||||
|
||||
```
|
||||
is-system-context-ungesetzt-false: bestanden — ohne gesetzte Variable: is_system_context() = false (Rohwert null) — die Vorher-Pruefung ohne-kontext-leer in rls-preflight.mjs bleibt gueltig
|
||||
is-system-context-leer-false: bestanden — nach set_config('app.system_context', '', true): false
|
||||
is-system-context-true-true: bestanden — nach set_config('app.system_context', 'true', true): true
|
||||
is-system-context-fremdwert-false: bestanden — nach set_config('app.system_context', 'yes', true): false
|
||||
```
|
||||
|
||||
Je Tabelle die drei Kern-Kennungen (zu wenig / zu viel / Erben) über den
|
||||
GENERIERTEN Client, Wegwerf-Tabellen mit allen skalaren Spalten, Regeln
|
||||
wortgleich aus ihren Migrationen geschnitten:
|
||||
|
||||
```
|
||||
dkvmoduleconfig-systemkontext-sieht-beide-mandanten: bestanden — system.dkvModuleConfig.findMany() liefert 2 Zeile(n) aus Mandanten ["TENANT-A","TENANT-B"]
|
||||
dkvmoduleconfig-systemkontext-insert-abgewiesen-42501: bestanden — system.dkvModuleConfig.create wirft PrismaClientUnknownRequestError, SQLSTATE 42501: ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Post […]
|
||||
dkvmoduleconfig-fortenant-a-nach-systemkontext-nur-a: bestanden — bound(TENANT-A).dkvModuleConfig.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 1 Zeile(n) aus ["TENANT-A"]
|
||||
ldapconfig-systemkontext-sieht-beide-mandanten: bestanden — system.ldapConfig.findMany() liefert 2 Zeile(n) aus Mandanten ["TENANT-A","TENANT-B"]
|
||||
ldapconfig-systemkontext-insert-abgewiesen-42501: bestanden — system.ldapConfig.create wirft PrismaClientUnknownRequestError, SQLSTATE 42501: ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresError […]
|
||||
ldapconfig-fortenant-a-nach-systemkontext-nur-a: bestanden — bound(TENANT-A).ldapConfig.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 1 Zeile(n) aus ["TENANT-A"]
|
||||
ldapfieldmapping-systemkontext-sieht-beide-mandanten: bestanden — system.ldapFieldMapping.findMany() liefert 2 Zeile(n) aus Mandanten ["TENANT-A","TENANT-B"]
|
||||
ldapfieldmapping-systemkontext-insert-abgewiesen-42501: bestanden — system.ldapFieldMapping.create wirft PrismaClientUnknownRequestError, SQLSTATE 42501: ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(Po […]
|
||||
ldapfieldmapping-fortenant-a-nach-systemkontext-nur-a: bestanden — bound(TENANT-A).ldapFieldMapping.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 1 Zeile(n) aus ["TENANT-A"]
|
||||
tendermatch-systemkontext-sieht-beide-mandanten: bestanden — system.tenderMatch.findMany() liefert 2 Zeile(n) aus Mandanten ["TENANT-A","TENANT-B"]
|
||||
tendermatch-systemkontext-insert-abgewiesen-42501: bestanden — system.tenderMatch.create wirft PrismaClientUnknownRequestError, SQLSTATE 42501: ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresErro […]
|
||||
tendermatch-fortenant-a-nach-systemkontext-nur-a: bestanden — bound(TENANT-A).tenderMatch.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 1 Zeile(n) aus ["TENANT-A"]
|
||||
tendersavedsearch-systemkontext-sieht-beide-mandanten: bestanden — system.tenderSavedSearch.findMany() liefert 2 Zeile(n) aus Mandanten ["TENANT-A","TENANT-B"]
|
||||
tendersavedsearch-systemkontext-insert-abgewiesen-42501: bestanden — system.tenderSavedSearch.create wirft PrismaClientUnknownRequestError, SQLSTATE 42501: ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError( […]
|
||||
tendersavedsearch-fortenant-a-nach-systemkontext-nur-a: bestanden — bound(TENANT-A).tenderSavedSearch.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 1 Zeile(n) aus ["TENANT-A"]
|
||||
```
|
||||
|
||||
Die #27-Form unter Systemkontext (der Pfad von `getAllActiveConfigs()`):
|
||||
|
||||
```
|
||||
ldapconfig-systemkontext-include-fieldmappings-beider-mandanten: bestanden — system.ldapConfig.findMany({ where: { isActive: true }, include: { fieldMappings: true } }) liefert 2 Zeile(n): ["TENANT-A:1","TENANT-B:1"] (Mandant:Anzahl Zuordnungen)
|
||||
```
|
||||
|
||||
Schlusszeile: `Alle 253 Pruefungen bestanden.` (Baseline vor diesem Lauf: 203; nach Aufgabe 1: 216).
|
||||
|
||||
`pg_policies` der LEBENDEN Datenbank nach `prisma migrate deploy` (36
|
||||
Migrationen, `pg_proc` kennt `is_system_context`, 34 Regeln gesamt, alle
|
||||
PERMISSIVE; Form Tabelle#Regelname#Befehl#USING#WITH CHECK, beide Regeln je
|
||||
Tabelle):
|
||||
|
||||
```
|
||||
DkvModuleConfig#system_read_policy#SELECT#is_system_context()#
|
||||
DkvModuleConfig#tenant_isolation_policy#ALL#("tenantId" = current_tenant_id())#
|
||||
LdapConfig#system_read_policy#SELECT#is_system_context()#
|
||||
LdapConfig#tenant_isolation_policy#ALL#("tenantId" = current_tenant_id())#
|
||||
LdapFieldMapping#system_read_policy#SELECT#is_system_context()#
|
||||
LdapFieldMapping#tenant_isolation_policy#ALL#("ldapConfigId" IN ( SELECT "LdapConfig".id FROM "LdapConfig" WHERE ("LdapConfig"."tenantId" = current_tenant_id())))#
|
||||
TenderMatch#system_read_policy#SELECT#is_system_context()#
|
||||
TenderMatch#tenant_isolation_policy#ALL#("tenantId" = current_tenant_id())#
|
||||
TenderSavedSearch#system_read_policy#SELECT#is_system_context()#
|
||||
TenderSavedSearch#tenant_isolation_policy#ALL#(("tenantId" = current_tenant_id()) AND ((current_user_id() IS NULL) OR ("userId" = current_user_id())))#
|
||||
```
|
||||
|
||||
Endstand nach Aufgabe 2: Tests 1054 bestanden / 64 Dateien (Baseline
|
||||
1028 / 62), Typprüfung sauber, Werkzeug 253.
|
||||
|
||||
### (y2) Signaltabelle — beide Fehlerrichtungen je Regel
|
||||
|
||||
| Fehlerrichtung | Erwartung | Gemessen | Befund |
|
||||
|---|---|---|---|
|
||||
| Zu streng: der Systemkontext sähe nichts (ein Systemleser OHNE Regel liefert nach dem Scharfschalten 0 Zeilen und schweigt — T-EYM-04) | Systemkontext liefert beide Mandanten | `<tabelle>-ungebunden-null-zeilen` UND `<tabelle>-systemkontext-sieht-beide-mandanten` als Paar (fünf Tabellen), dazu `ldapconfig-systemkontext-include-fieldmappings-beider-mandanten` | NICHT der Fall — jede der fünf Tabellen ist geöffnet; Rückbau (b) unten zeigt, dass das Werkzeug der Datei folgt |
|
||||
| Zu locker: der Systemkontext könnte schreiben (T-EYM-02) | INSERT/UPDATE/DELETE scheitern an der Mandantenregel | `<tabelle>-systemkontext-insert-abgewiesen-42501`, `…-updatemany-count-0`, `…-deletemany-count-0` (fünf Tabellen) | NICHT der Fall — die Regel ist `FOR SELECT`; Rückbau (a): `FOR SELECT` entfernt → der Insert GELINGT, `cmd` wird `ALL` |
|
||||
| Zu locker: ein Anfrageweg ruft `forSystem` (T-EYM-01) | Spec rot | `FORSYSTEM_ALLOWED_CALL_SITES` mit exakter Zahl je Datei; Rückbau (d): Zahl 0 → zwei Zusicherungen rot, Fremddatei → drei rot | Wachhund greift; keine Ausnahmeliste für die Zuweisungsform |
|
||||
| Erben: eine Verbindung trägt `app.system_context` in eine spätere `forTenant`-Abfrage (T-EYM-03) | `forTenant(A)` nach `forSystem` sieht nur A; `is_system_context()` unter `forTenant` ist false | `<tabelle>-fortenant-a-nach-systemkontext-nur-a`, `<tabelle>-is-system-context-unter-fortenant-false` (fünf Tabellen) | NICHT der Fall — `local=true` (erstes Netz) UND ausdrücklicher Reset (zweites Netz); Rückbau (c) unten belegt, dass der Reset allein trägt |
|
||||
|
||||
**Rückbau (a)** — in der Migrationsdatei bei `"TenderMatch"` das `FOR SELECT`
|
||||
entfernt (Regel wird `ALL`), Werkzeug:
|
||||
|
||||
```
|
||||
tendermatch-systemkontext-insert-abgewiesen-42501: FEHLGESCHLAGEN — system.tenderMatch.create({"id":"tm-system-schreibversuch","tenderId":"tender-2","savedSearchId":"ss-a","userId":"user-a","tenantId":"TENANT-A"}) ist NICHT fehlgeschlagen — angelegt: "tm-system-schreibversuch"
|
||||
tendermatch-systemkontext-updatemany-count-0: FEHLGESCHLAGEN — system.tenderMatch.updateMany({ where: {}, data: {"notifiedChannel":"SYSTEM-SCHREIBVERSUCH"} }) liefert count=3
|
||||
tendermatch-systemkontext-deletemany-count-0: FEHLGESCHLAGEN — system.tenderMatch.deleteMany({}) liefert count=3; Zeilen danach (Wartungsrolle): 0
|
||||
tendermatch-fortenant-a-nach-systemkontext-nur-a: FEHLGESCHLAGEN — bound(TENANT-A).tenderMatch.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 0 Zeile(n) aus []
|
||||
tendermatch-pg-policies-genau-eine-system-read-policy-select: FEHLGESCHLAGEN — pg_policies fuer "TenderMatch" (system_read_policy): [{"policyname":"system_read_policy","cmd":"ALL","permissive":"PERMISSIVE","qual":"is_system_context()"}]
|
||||
5 von 253 Pruefungen fehlgeschlagen.
|
||||
```
|
||||
|
||||
**Rückbau (b)** — die `system_read_policy` für `"TenderSavedSearch"` aus der
|
||||
Migrationsdatei entfernt. Die innere Routine bricht für diese Tabelle mit
|
||||
einer eigenen roten Kennung ab (Muster `runSingleRulePersonalTableCheck`:
|
||||
nicht raten, wenn die Regel fehlt) — deshalb 245 statt 253 Prüfungen, nicht
|
||||
die im Plan erwarteten zwei roten Kennungen `…-sieht-beide-mandanten`/`…-pg-policies-…`;
|
||||
die lebende Datenbank blieb währenddessen bei 34 Regeln und einer
|
||||
`system_read_policy` auf TenderSavedSearch (per `pg_policies` gelesen):
|
||||
|
||||
```
|
||||
tendersavedsearch-system-read-policy-aus-migration-gefunden: FEHLGESCHLAGEN — CREATE POLICY system_read_policy ON "TenderSavedSearch" nicht in der Systemkontext-Migration (20260914120000) gefunden
|
||||
1 von 245 Pruefungen fehlgeschlagen.
|
||||
```
|
||||
|
||||
**Rückbau (c)** — im Werkzeug `forSystemQuery`/`buildInlineSystemClient` auf
|
||||
`set_config(…, false)` gestellt: `Alle 253 Pruefungen bestanden.` — alle
|
||||
`…-fortenant-a-nach-systemkontext-nur-a` BLEIBEN grün, weil der Reset in
|
||||
`buildInlineExtendedClient` trägt. Zusätzlich den Reset dort entfernt:
|
||||
|
||||
```
|
||||
dkvmoduleconfig-fortenant-a-nach-systemkontext-nur-a: FEHLGESCHLAGEN — bound(TENANT-A).dkvModuleConfig.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 2 Zeile(n) aus ["TENANT-A","TENANT-B"]
|
||||
ldapconfig-fortenant-a-nach-systemkontext-nur-a: FEHLGESCHLAGEN — bound(TENANT-A).ldapConfig.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 2 Zeile(n) aus ["TENANT-A","TENANT-B"]
|
||||
ldapfieldmapping-fortenant-a-nach-systemkontext-nur-a: FEHLGESCHLAGEN — bound(TENANT-A).ldapFieldMapping.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 2 Zeile(n) aus ["TENANT-A","TENANT-B"]
|
||||
tendermatch-fortenant-a-nach-systemkontext-nur-a: FEHLGESCHLAGEN — bound(TENANT-A).tenderMatch.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 2 Zeile(n) aus ["TENANT-A","TENANT-B"]
|
||||
tendersavedsearch-fortenant-a-nach-systemkontext-nur-a: FEHLGESCHLAGEN — bound(TENANT-A).tenderSavedSearch.findMany() unmittelbar nach system.findMany() auf demselben Client liefert 2 Zeile(n) aus ["TENANT-A","TENANT-B"]
|
||||
5 von 253 Pruefungen fehlgeschlagen.
|
||||
```
|
||||
|
||||
**Rückbau (d)** — Detektor, Zahl für `tender-matching.service.ts` auf 0:
|
||||
|
||||
```
|
||||
AssertionError: apps/api/src/tenders/tender-matching.service.ts: gemessen 1 forSystem(-Aufruf(e), erlaubt sind genau 0: expected [ Array(1) ] to deeply equal []
|
||||
AssertionError: apps/api/src/tenders/tender-matching.service.ts: Erlaubnisliste nennt 0, gemessen 1 — der Eintrag ist ueberholt: expected [ Array(1) ] to deeply equal []
|
||||
```
|
||||
|
||||
Fremddatei `admin-seed.service.ts` vorübergehend mit `forSystem(` versehen:
|
||||
|
||||
```
|
||||
AssertionError: apps/api/src/user/admin-seed.service.ts: 2 forSystem(-Aufruf(e), Datei steht NICHT in FORSYSTEM_ALLOWED_CALL_SITES — ein Anfrageweg darf den Systemkontext nie rufen: expected [ Array(1) ] to deeply equal []
|
||||
AssertionError: apps/api/src/user/admin-seed.service.ts: 1 forSystem(-Aufruf(e) ausserhalb der Zuweisungsform: expected [ Array(1) ] to deeply equal []
|
||||
AssertionError: apps/api/src/user/admin-seed.service.ts: 1 include:/select:/_count:-Angabe(n) ausserhalb eines erkannten Modellaufrufs: expected [ Array(1) ] to deeply equal []
|
||||
```
|
||||
|
||||
Alle Rückbauten zurückgenommen (`git checkout` bzw. Kopie mit gleichem
|
||||
Hash), Werkzeug danach erneut 253, Detektor 30/30.
|
||||
|
||||
### (y3) Welcher Code Leere als Abwesenheit deutet
|
||||
|
||||
Die Frage aus dem Auftrag (`docs/mandantentrennung-etappe3-auftrag.md`, 3c):
|
||||
deutet einer der sechs Pfade eine LEERE Systemkontext-Antwort als "es gibt
|
||||
nichts" und löscht oder deaktiviert daraufhin? Je Pfad mit Datei und Stelle:
|
||||
|
||||
- **dkv-scheduler** (`apps/api/src/dkv/dkv-scheduler.service.ts`,
|
||||
`onModuleInit`): leere Liste → Protokollzeile `no active config found`,
|
||||
`return` — kein Auftrag, nichts gelöscht, nichts deaktiviert.
|
||||
- **ldap-sync.scheduler** (`apps/api/src/ldap/ldap-sync.scheduler.ts`, ruft
|
||||
`getAllActiveConfigs()`): leere Liste → kein Sync-Lauf. Der gefährliche
|
||||
Löschzweig in `apps/api/src/ldap/ldap.service.ts` (Deaktivieren/Entfernen
|
||||
nicht mehr im Verzeichnis gefundener Nutzer) liegt INNERHALB eines je
|
||||
Mandant gebundenen Sync-Laufs (`forTenant(this.prisma, tenantId)`), den
|
||||
eine leere Konfigurationsliste gar nicht erst startet — Leere auf der
|
||||
Systemkontext-Ebene erreicht diesen Zweig strukturell nicht.
|
||||
- **ldap onApplicationBootstrap** (`ldap-config.service.ts`): leere Liste →
|
||||
`legacy.length === 0` → `return` — die Nachverschlüsselung ist Nichtstun.
|
||||
- **tender-digest** (`tender-digest.scheduler.ts`, `runDigest`): leere
|
||||
Kandidatenliste → `if (!candidates.length) return;` — kein Versand,
|
||||
`notifiedAt` bleibt NULL (wiederholbar).
|
||||
- **tender-matching** (`tender-matching.service.ts`, `matchDelta`): leere
|
||||
Profilliste → die Schleife läuft nicht, keine Treffer, keine Sofortmeldung.
|
||||
- **admin-seed** (`apps/api/src/user/admin-seed.service.ts`): leere
|
||||
Mandantenliste → keine Reparatur; `Tenant` trägt keine Regel, ein
|
||||
Systemkontext ist dort gar nicht nötig (gemessen: einziger Lesezugriff
|
||||
außerhalb der Schleife ist `tenant.findMany`).
|
||||
|
||||
Fazit: KEIN Pfad löscht oder deaktiviert auf Leere. Die verbleibende Gefahr
|
||||
war das STUMME Nichtstun nach dem Scharfschalten — genau die schließt die
|
||||
Systemleseregel (Paar `…-ungebunden-null-zeilen` / `…-sieht-beide-mandanten`).
|
||||
|
||||
### (y4) Was dieser Durchlauf bewusst nicht löst
|
||||
|
||||
- Der Single-Flight-Riegel `processing` in `DkvService.processInbox` ist EIN
|
||||
prozessweites Boolean, nicht je Mandant. Seit je aktivem Mandanten ein
|
||||
eigener Cron-Auftrag läuft, können sich zwei Ticks verschiedener Mandanten
|
||||
überschneiden — der zweite bricht still ab und wartet bis zum nächsten
|
||||
Intervall (Verzögerung, kein Datenverlust; mit einem Mandanten
|
||||
unverändert). Neuer WINDOWS-Eintrag (#37) mit Lösungsweg (Riegel je
|
||||
Mandant, `Set<tenantId>`, Test "zwei Mandanten gleichzeitig, beide werden
|
||||
bedient"). Der Tick bleibt in diesem Durchlauf unangetastet (Auftrag).
|
||||
- `sendWelcomeEmail` hat weiterhin null Aufrufer; `@nestjs-modules/mailer`
|
||||
bleibt in `package.json`/Lockfile installiert, ist aber unbenutzt —
|
||||
Aufräumen, kein Defekt, kein Lockfile-Eingriff in diesem Durchlauf.
|
||||
- Der Sonderfall "Nutzer mit Treffern unter zwei Mandanten" im Digest
|
||||
(`distinct: ['userId']` liefert nur eine tenantId je Nutzer) bleibt wie in
|
||||
(t4) beschrieben ungelöst.
|
||||
- `rls-preflight.mjs` bekommt in Etappe 4 eine Prüfung
|
||||
`mit-systemkontext-sichtbar` — hier nicht gebaut, weil das Werkzeug unter
|
||||
der Wegwerf-Rolle dasselbe bereits misst (`…-sieht-beide-mandanten`).
|
||||
|
||||
### (y5) Was dieser Durchlauf bewusst nicht anfasst
|
||||
|
||||
- Der Schalter (`DATABASE_URL` → Rolle `tessera`, BYPASSRLS) — bleibt AUS;
|
||||
Compose-/Umgebungsdateien unangetastet (Gate gegen `5e0e408` in jeder
|
||||
Aufgabe).
|
||||
- `schema.prisma`, bestehende Migrationen (Prüfsumme), `package.json`,
|
||||
Lockfile.
|
||||
- Die drei SECURITY-DEFINER-Anmeldefunktionen — unangetastet.
|
||||
- `admin-seed.service.ts` — unverändert (nur dokumentiert, siehe (y3)).
|
||||
- `rls-preflight.mjs` — `ohne-kontext-leer` bleibt gültig, weil
|
||||
`is_system_context()` ohne Variable false ist (`is-system-context-ungesetzt-false`).
|
||||
- Der Tick `DkvService.processInbox` (je Mandant gebunden seit 260909-mir)
|
||||
und die 3b-Regeln der zehn persönlichen Tabellen.
|
||||
|
||||
## Etappe 2 — Abschluss
|
||||
|
||||
Etappe 2 der Mandantentrennung ist mit diesem Lauf (260911-gwh) vollständig:
|
||||
@@ -3353,8 +3589,17 @@ jetzt erfüllte Befund-K-Bedingung, die entfällt):
|
||||
- Das Verstummen des Mail-Startpfads (dieser Lauf, (s4)(a)) — EIGENES
|
||||
Signal, NICHT an #21 angeschlossen.
|
||||
|
||||
**Nachtrag (260914-eym):** Etappe 3c ist abgeschlossen — Systemkontext für
|
||||
die Hintergrunddienste (`## Systemkontext (Etappe 3c, 260914-eym)`); die
|
||||
beiden Signale #21 (DKV-Planer) und #30 (Mail-Startpfad) aus dieser Liste
|
||||
sind geschlossen, das eine durch Auftrag je Mandant über den Systemkontext,
|
||||
das andere durch Entfernen des Startpfads. Die übrigen Vorher-Prüfungen für
|
||||
Etappe 4 bleiben wie oben; hinzu kommt `mit-systemkontext-sichtbar` (siehe
|
||||
(y4)).
|
||||
|
||||
## Verweis
|
||||
|
||||
Die Bestandsaufnahme, welche Fundstelle den hier beschriebenen Übergang
|
||||
bereits vollzogen hat (Stand-Spalte `gebunden`/`ungebunden`/`gemischt`),
|
||||
steht in `docs/mandantentrennung-zugriffsklassifikation.md`.
|
||||
steht in `docs/mandantentrennung-zugriffsklassifikation.md` (seit 260914-eym
|
||||
mit dem vierten Stand-Wert `system-gebunden`).
|
||||
|
||||
Reference in New Issue
Block a user