docs(quick-260910-jab): Aktenstand kohaerent machen — Ledger, Klassifikation, Kritikschrift, Betriebsanleitung
- WINDOWS.md: #19 auf fixed gesetzt (Tabelle + JSON-Block), mit Beleg (Migrationsname + benannte Pruefungen) und ausdruecklicher Feststellung, dass die SearchProvider-Haelfte als widerlegte Praemisse schliesst, nicht als geloestes Problem. #18/#20/#21/#22/#23 bleiben unveraendert offen. Ein neuer Eintrag #24 haelt den fehlenden Verwaltungsweg fuer plattformweite Zeilen unter der Anwendungsrolle offen (verschwindet nicht mit #19). Die vier Kopfzahlen sind aus dem JSON-Block abgeleitet (6 offen, 17 behoben, 1 zurueckgestellt, 24 gesamt). - Klassifikation: #19-Block von offener Frage zu beantwortet, die Uebersichtszeile tenders (35/27) und Summenzeile (107/135) aus dem Quelltext neu abgeleitet, vier Bestandsaufnahme-Zeilen nachgezogen (searchProvider, groups.service.ts/user, module-grants.service.ts/ moduleGrant, tender-rss-feed.service.ts/tenderRssFeedSource), der Punkt in "Was diese Etappe NICHT entscheidet" aufgeloest. - Kritikschrift: neuer Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und WINDOWS #19" mit tatsaechlich beobachteter Ausgabe, Regelliste der lebenden Datenbank, Signaltabelle mit beiden Fehlerrichtungen je Regel, der neuen Stelle aus Befund F, der selbst ausgefuehrten Messung zur fehlenden Benutzerdimension (keine zweite Sitzungsvariable gefunden) und dem, was dieser Durchlauf nicht loest/nicht anfasst. Fuenf ueberholte Bestandsstellen mit Nachtraegen versehen (g4, t4, die Signaltabellenzeile zu den RSS-Pfaden, drei aufgezeichnete Werkzeugausgaben, m4), die alten Messprotokolle bleiben woertlich stehen. - Betriebsanleitung: die eine Stelle, die #19 als offen fuehrte, nennt jetzt den Aufloesungsstand und WINDOWS #24. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
This commit is contained in:
@@ -198,6 +198,18 @@ modulegrant-fremde-gruppe-trotz-eigener-mandantenkennung-erlaubt: bestanden —
|
||||
tenantmoduleactivation-gebunden-nur-eigene-zeile: bestanden — forTenant(TENANT-A) liefert 1 Zeile(n): ["TENANT-A"]
|
||||
```
|
||||
|
||||
**NACHTRAG (260910-jab):** die beiden fett hervorgehobenen Zeilen oben
|
||||
(`groupmembership-schreiben-fremder-benutzer-nicht-verhindert` und
|
||||
`modulegrant-fremde-gruppe-trotz-eigener-mandantenkennung-erlaubt`) sind ein
|
||||
Messprotokoll vom 2026-09-09 und bleiben UNVERÄNDERT stehen — sie belegen,
|
||||
dass die beiden Löcher existierten. Seit Migration
|
||||
`20260910120000_rls_widen_membership_grant_and_platform_read` sind beide
|
||||
Aussagen ÜBERHOLT: die Prüfungen sind umgekehrt (`groupmembership-schreiben-
|
||||
fremder-benutzer-abgelehnt`, `modulegrant-fremde-gruppe-abgelehnt`), das
|
||||
INSERT wird jetzt jeweils ABGEWIESEN statt zu gelingen. Siehe den neuen
|
||||
Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und WINDOWS #19" unten für die
|
||||
aktuell beobachtete Ausgabe.
|
||||
|
||||
Die Belegzeile, die diesen Abschnitt trägt, ist `group-ungebunden-null-zeilen`:
|
||||
der IDENTISCHE `SELECT "tenantId" FROM "Group"` ohne vorheriges `set_config`
|
||||
liefert **0 Zeilen**, nicht etwa die 2 tatsächlich vorhandenen — an der
|
||||
@@ -366,6 +378,14 @@ zum `deleteMany`-mit-`notIn` des ldap-Bereichs.
|
||||
`addUserToDefaultGroup`, `assertTargetBelongsToTenant`) bleiben deshalb
|
||||
der primäre Schutz gegen diese beiden Formen der Rechteausweitung und
|
||||
werden durch diesen Durchlauf NICHT durch die Datenbank ersetzt.
|
||||
**NACHTRAG (260910-jab):** ÜBERHOLT — seit Migration
|
||||
`20260910120000_rls_widen_membership_grant_and_platform_read` prüft
|
||||
`GroupMembership` BEIDE Seiten (T-JTS-02 geschlossen) und `ModuleGrant`
|
||||
zusätzlich beide möglichen Ziele (T-JTS-03 geschlossen, beide Zweige des
|
||||
Entweder-oder D-04). Die Anwendungsprüfungen bleiben trotzdem bestehen —
|
||||
sie sind bis zum Scharfschalten (#18) der einzige tatsächlich wirksame
|
||||
Schutz, siehe den neuen Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und
|
||||
WINDOWS #19" unten.
|
||||
|
||||
### (g5) Fortschreibung des ldap-Abschnitts
|
||||
|
||||
@@ -411,6 +431,17 @@ tendertriage-einfuegen-auf-unsichtbare-zeile-verletzt-eindeutigkeit: bestanden
|
||||
Alle 32 Pruefungen bestanden.
|
||||
```
|
||||
|
||||
**NACHTRAG (260910-jab):** die fett hervorgehobene Zeile oben
|
||||
(`tenderrssfeed-plattformzeile-unter-jedem-mandanten-unsichtbar`) ist ein
|
||||
Messprotokoll vom 2026-09-09 und bleibt UNVERÄNDERT stehen — sie belegt,
|
||||
dass WINDOWS #19 existierte. Seit Migration
|
||||
`20260910120000_rls_widen_membership_grant_and_platform_read` ist diese
|
||||
Aussage ÜBERHOLT: die Prüfung ist umgekehrt
|
||||
(`tenderrssfeed-plattformzeile-gebunden-sichtbar`), die plattformweite Zeile
|
||||
ist jetzt unter BEIDEN Mandantenkontexten SICHTBAR. Siehe den neuen
|
||||
Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und WINDOWS #19" unten für die
|
||||
aktuell beobachtete Ausgabe.
|
||||
|
||||
Die Belegzeile, die diesen Abschnitt trägt, ist
|
||||
`tendersavedsearch-ungebunden-null-zeilen`: der IDENTISCHE
|
||||
`SELECT "tenantId" FROM "TenderSavedSearch"` ohne vorheriges `set_config`
|
||||
@@ -441,6 +472,12 @@ Bereich brauchte:
|
||||
Kehrseite: ein gebundenes `INSERT` mit `tenantId = NULL` wird von der
|
||||
ausgelieferten Policy abgewiesen — `createPlatform` würde in genau diese
|
||||
Abweisung laufen, würde man es binden.
|
||||
**NACHTRAG (260910-jab):** ÜBERHOLT für `listForUser` — seit Migration
|
||||
`20260910120000_rls_widen_membership_grant_and_platform_read` bindet
|
||||
dieser Pfad (die neue Leseregel schließt Zeilen ohne Mandant ausdrücklich
|
||||
ein). `createPlatform`/`remove` bleiben unverändert ungebunden, aus dem
|
||||
jetzt ausdrücklichen Grund einer eigenen Schreibregel je Befehl (WINDOWS
|
||||
#24).
|
||||
|
||||
Eine dritte Messung trägt die Fehlerbehandlung von Aufgabe 2:
|
||||
`tendertriage-einfuegen-auf-unsichtbare-zeile-verletzt-eindeutigkeit` zeigt,
|
||||
@@ -474,7 +511,7 @@ nicht eingeführt.
|
||||
| `TenderNotificationPrefService.getForUser` | **Sonderfall**: kein Treffer bedeutet hier nicht "leer", sondern der Vorgabewert `daily` (D-01) | Ein Nutzer, der `off` gewählt hat, sieht in der Oberfläche wieder `daily` — ein zu kleines Leseergebnis setzt die Einstellung stillschweigend auf täglich zurück, statt sie leer zu lassen |
|
||||
| `TenderEmailConfigService.getConfigForApi` | Der gebundene `findUnique` liefert 0 Zeilen statt der eigenen Konfiguration | `GET /email-config` liefert `null`; die Oberfläche zeigt "kein Postfach verbunden" für einen Nutzer, der tatsächlich eines hat |
|
||||
| `TenderEmailConfigService.testConnection` | Der gebundene Rückgriff auf gespeicherte Zugangsdaten liefert 0 Zeilen statt der eigenen | Ein Verbindungstest mit leer gelassenem Formular (Rückgriff auf gespeicherte Zugangsdaten) schlägt fehl, obwohl gespeicherte Zugangsdaten existieren |
|
||||
| `TenderRssFeedSourceService.listForUser`/`createPlatform`/`remove` (bewusst UNGEBUNDEN, WINDOWS #19) | Betrifft nicht diese drei Pfade selbst — sie binden nicht und liefern deshalb weiterhin die plattformweite Zeile korrekt. Das Risiko liegt in einer KÜNFTIGEN Bindung (Etappe 3), nicht in dieser Umstellung | Würde man sie binden: leere Feed-Liste bzw. 404 beim Entfernen einer plattformweiten Quelle — hier ausdrücklich als Grenze festgehalten, nicht als heute beobachtbares Verhalten |
|
||||
| `TenderRssFeedSourceService.listForUser`/`createPlatform`/`remove` (bewusst UNGEBUNDEN, WINDOWS #19) | Betrifft nicht diese drei Pfade selbst — sie binden nicht und liefern deshalb weiterhin die plattformweite Zeile korrekt. Das Risiko liegt in einer KÜNFTIGEN Bindung (Etappe 3), nicht in dieser Umstellung | Würde man sie binden: leere Feed-Liste bzw. 404 beim Entfernen einer plattformweiten Quelle — hier ausdrücklich als Grenze festgehalten, nicht als heute beobachtbares Verhalten. **NACHTRAG (260910-jab):** ÜBERHOLT für `listForUser` — dieser Pfad ist seit Migration `20260910120000_rls_widen_membership_grant_and_platform_read` GEBUNDEN (WINDOWS #19 geschlossen, Befund F: ungebunden hätte die Reparatur ihn sonst still auf nur die plattformweiten Zeilen reduziert). `createPlatform`/`remove` bleiben unverändert ungebunden (WINDOWS #24). |
|
||||
| `TenderRssFeedSourceService.createForUser` (gebunden) | Der Zähler (`count`, gebunden) liefert 0 statt der tatsächlichen Anzahl eigener Feeds | Ein Nutzer, der bereits am Limit von 20 eigenen Feeds ist, könnte scheinbar unbegrenzt neue anlegen (harmlose Richtung: die Kappung aus T-17-10 wirkt nicht mehr, kein Datenverlust) |
|
||||
|
||||
### (t3) Welcher Code Leere als Abwesenheit deutet
|
||||
@@ -541,6 +578,18 @@ Warnung wäre Dauerlärm und verlöre ihr Signal.
|
||||
Policy-Semantik selbst (eine `tenantId IS NULL OR tenantId =
|
||||
current_tenant_id()`-Lesevariante) gehört zu Etappe 3 und wird hier nicht
|
||||
angefasst.
|
||||
**NACHTRAG (260910-jab): ÜBERHOLT — WINDOWS #19 ist geschlossen.** Migration
|
||||
`20260910120000_rls_widen_membership_grant_and_platform_read` ersetzt die
|
||||
einfache Regel durch vier nach Befehl getrennte Regeln
|
||||
(`tenant_platform_read_policy` schließt Zeilen ohne Mandant beim Lesen
|
||||
ausdrücklich ein, die drei Schreibregeln verlangen weiterhin einen
|
||||
Mandanten). `listForUser` ist seither GEBUNDEN (Befund F: ungebunden hätte
|
||||
die Reparatur ihn sonst still auf nur die plattformweiten Zeilen
|
||||
reduziert); `createPlatform`/`remove` bleiben bewusst ungebunden — beide
|
||||
Pfade lassen sich unter der Anwendungsrolle grundsätzlich nicht
|
||||
anlegen/entfernen (WINDOWS #24, eigener offener Punkt, verschwindet nicht
|
||||
mit #19). Siehe den neuen Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und
|
||||
WINDOWS #19" unten.
|
||||
- **Die übergreifenden Hälften der beiden Hintergrunddienste.** Aufgabe 3
|
||||
bindet nur die Je-Treffer-Hälften von `tender-digest.scheduler.ts` und
|
||||
`tender-matching.service.ts`; die Kandidatenabfrage
|
||||
@@ -1293,6 +1342,18 @@ freigabe-eindeutigkeitsindizes-fuehren-mit-der-mandantenkennung: bestanden — a
|
||||
Alle 66 Pruefungen bestanden.
|
||||
```
|
||||
|
||||
**NACHTRAG (260910-jab):** die fett hervorgehobene Zeile oben
|
||||
(`gruppenpfad-gebunden-schliesst-die-fremde-gruppe-aus`) ist ein
|
||||
Messprotokoll vom 2026-09-10 und bleibt UNVERÄNDERT stehen. Ihre Behauptung
|
||||
"die Regel auf ModuleGrant lässt diese Zeile durch (T-JTS-03)" ist seit
|
||||
Migration `20260910120000_rls_widen_membership_grant_and_platform_read`
|
||||
ÜBERHOLT: die Regel weist ein gebundenes Einfügen von `grant-foreign-group`
|
||||
jetzt nachweislich ab (`modulegrant-fremde-gruppe-abgelehnt`); die Zeile
|
||||
existiert in aktuellen Läufen nur noch, weil
|
||||
`modulegrant-fremde-gruppe-ueber-die-wartungsrolle-weiterhin-moeglich` sie
|
||||
über die Wartungsrolle (BYPASSRLS) anlegt. Der Meldetext dieser Prüfung ist
|
||||
im Quelltext des Werkzeugs entsprechend richtiggestellt.
|
||||
|
||||
Die Belegzeile, die diesen Abschnitt trägt, ist
|
||||
`modulegrant-ungebunden-null-zeilen`: der IDENTISCHE `SELECT "tenantId" FROM
|
||||
"ModuleGrant"` ohne vorheriges `set_config` liefert **0 Zeilen**, nicht etwa
|
||||
@@ -1426,7 +1487,7 @@ und verlöre ihr Signal.
|
||||
|
||||
### (m4) Was dieser Durchlauf bewusst nicht löst
|
||||
|
||||
- **T-JTS-02/T-JTS-03 bleiben aufgezeichnet und ungefixt.** Gemessen in
|
||||
- ~~**T-JTS-02/T-JTS-03 bleiben aufgezeichnet und ungefixt.** Gemessen in
|
||||
Aufgabe 1 (Prüfungen 6/7): die Regel auf `ModuleGrant` prüft nur die
|
||||
Mandantenkennung der Zeile selbst, nicht die referenzierte Gruppe; die
|
||||
Regel auf `GroupMembership` prüft nur die Gruppenseite. Die Bindung fügt
|
||||
@@ -1434,7 +1495,17 @@ und verlöre ihr Signal.
|
||||
schließt die fremde Gruppe gebunden aus), aber erst nach Etappe 4 — die
|
||||
Schreibseiten-Gegenprüfung in `module-grants.service.ts`
|
||||
(`assertTargetBelongsToTenant`) bleibt deshalb der heutige Schutz und wird
|
||||
durch diesen Durchlauf NICHT ersetzt.
|
||||
durch diesen Durchlauf NICHT ersetzt.~~
|
||||
**NACHTRAG (260910-jab): ÜBERHOLT — BEIDE Befunde sind geschlossen.**
|
||||
Migration `20260910120000_rls_widen_membership_grant_and_platform_read`
|
||||
lässt die Regel auf `GroupMembership` jetzt beide Seiten prüfen
|
||||
(T-JTS-02) und die Regel auf `ModuleGrant` zusätzlich beide möglichen
|
||||
Ziele (T-JTS-03, beide Zweige des Entweder-oder D-04). Die
|
||||
Schreibseiten-Gegenprüfung in `module-grants.service.ts`
|
||||
(`assertTargetBelongsToTenant`) bleibt TROTZDEM bestehen — sie ist bis
|
||||
zum Scharfschalten (#18, Schalter weiterhin aus) der einzige tatsächlich
|
||||
wirksame Schutz und wird NICHT ersetzt. Siehe den neuen Abschnitt
|
||||
"Regelschluss T-JTS-02, T-JTS-03 und WINDOWS #19" unten.
|
||||
- **Die Regel für den Modulkatalog gehört zu Etappe 3.** Gemessen in
|
||||
Aufgabe 1 (Prüfung 8/9, Befund E): `"Module"` trägt heute keinen
|
||||
Zeilenschutz, eine Bindung wäre heute wirkungslos, nicht katastrophal.
|
||||
@@ -1540,6 +1611,122 @@ nichts gefunden" unterscheidet, mit der Vorabprüfung für Etappe 4 und der
|
||||
begründeten Verwerfung einer Laufzeitwarnung — siehe (m3) oben und
|
||||
`.planning/WINDOWS.md`.
|
||||
|
||||
## Regelschluss T-JTS-02, T-JTS-03 und WINDOWS #19
|
||||
|
||||
Dieser Abschnitt weicht bewusst von der geplanten Reihenfolge ab (260910-jab,
|
||||
auf ausdrückliche Anweisung des Nutzers): die drei Regelfixe, ursprünglich
|
||||
nach Etappe 2 vorgesehen, wurden vorgezogen. Folge: die fünf noch offenen
|
||||
Bereiche der Etappe 2 messen ab jetzt gegen die NEUEN Regeln, mehrere
|
||||
bereits abgeschlossene Bereiche (`groups`, `tenders`, `module-registry` oben)
|
||||
haben gegen die ALTEN gemessen — diese Messungen stehen aufgezeichnet und
|
||||
sind mit Nachträgen an den betroffenen Stellen versehen, nicht umgeschrieben.
|
||||
|
||||
### (r1) Tatsächlich beobachtete Ausgabe und Regelliste der lebenden Datenbank
|
||||
|
||||
Lauf vom 2026-09-10 gegen `tessera-ctl-db-1`, Adresse `172.19.0.2` (nur die
|
||||
Zeilen der drei betroffenen Bereiche sowie der beiden aufsetzenden
|
||||
`module-registry`-Prüfungen; die vollständige Ausgabe umfasst 74
|
||||
Prüfungen):
|
||||
|
||||
```
|
||||
groupmembership-folgt-join-auf-group: bestanden — forTenant(TENANT-A) liefert 1 Mitgliedschaft(en): ["group-a"]
|
||||
groupmembership-schreiben-fremde-gruppe-abgelehnt: bestanden — INSERT mit fremder groupId abgewiesen: ERROR: new row violates row-level security policy for table "GroupMembership"
|
||||
groupmembership-schreiben-fremder-benutzer-abgelehnt: bestanden — INSERT mit A-eigener Gruppe, aber fremder Benutzerkennung (user-b, TENANT-B) abgewiesen: ERROR: new row violates row-level security policy for table "GroupMembership"
|
||||
groupmembership-fremder-benutzer-ueber-die-wartungsrolle-weiterhin-moeglich: bestanden — INSERT mit A-eigener Gruppe, aber fremder Benutzerkennung (user-b, TENANT-B) ist ueber die Wartungsrolle (BYPASSRLS) weiterhin GELUNGEN — der Ausschluss aus der vorigen Pruefung kommt damit nachweislich von der Regel, nicht vom Aufbau
|
||||
modulegrant-gebunden-nur-eigene-zeile: bestanden — forTenant(TENANT-A) liefert 1 Zeile(n): ["TENANT-A"]
|
||||
modulegrant-fremde-gruppe-abgelehnt: bestanden — INSERT mit korrekter eigener tenantId, aber fremder groupId (group-b, TENANT-B) abgewiesen: ERROR: new row violates row-level security policy for table "ModuleGrant"
|
||||
modulegrant-fremder-benutzer-abgelehnt: bestanden — INSERT mit korrekter eigener tenantId, aber fremder userId (user-b, TENANT-B) abgewiesen: ERROR: new row violates row-level security policy for table "ModuleGrant"
|
||||
modulegrant-fremde-gruppe-ueber-die-wartungsrolle-weiterhin-moeglich: bestanden — INSERT mit korrekter eigener tenantId, aber fremder groupId ist ueber die Wartungsrolle (BYPASSRLS) weiterhin GELUNGEN — legt zugleich die Zeile 'grant-foreign-group' an, auf der zwei Pruefungen des Bereichs module-registry aufsetzen (Befund C)
|
||||
tenderrssfeed-plattformzeile-gebunden-sichtbar: bestanden — forTenant(TENANT-A) sieht die Platform-Zeile: true, forTenant(TENANT-B) sieht sie: true
|
||||
tenderrssfeed-eigene-zeile-gebunden-weiterhin-sichtbar: bestanden — forTenant(TENANT-A) liefert ["rss-a","rss-platform"] — eigene Zeile (rss-a) sichtbar: true, fremde Zeile (rss-b, TENANT-B) sichtbar: false
|
||||
tenderrssfeed-ungebunden-nur-die-plattformzeile: bestanden — ungebundener SELECT auf "TenderRssFeedSource" liefert 1 Zeile(n): ["rss-platform"]
|
||||
tenderrssfeed-gebundenes-einfuegen-ohne-mandant-abgelehnt: bestanden — gebundenes INSERT mit tenantId=NULL abgewiesen (tenant_insert_policy)
|
||||
tenderrssfeed-gebundenes-aendern-der-plattformzeile-abgelehnt: bestanden — gebundenes UPDATE auf die plattformweite Zeile traf keine Zeile (tenant_update_policy filtert sie heraus) — url unveraendert
|
||||
tenderrssfeed-gebundenes-loeschen-der-plattformzeile-abgelehnt: bestanden — gebundenes DELETE auf die plattformweite Zeile traf 0 Zeilen (tenant_delete_policy filtert sie heraus)
|
||||
searchprovider-mandantenlose-zeile-bleibt-unter-jedem-kontext-unsichtbar: bestanden — forTenant(TENANT-A) sieht die mandantenlose Zeile: false, forTenant(TENANT-B) sieht sie: false — bewusst UNVERAENDERT (widerlegte Praemisse)
|
||||
gruppenpfad-gebunden-schliesst-die-fremde-gruppe-aus: bestanden — forTenant(TENANT-A) liefert 'grant-foreign-group' ueber denselben Drei-Tabellen-Weg NICHT — die Zeile wurde ueber die Wartungsrolle angelegt
|
||||
gruppenpfad-ueber-die-wartungsrolle-liefert-die-fremde-gruppe-mit: bestanden — dieselbe Abfrage ueber die Verwaltungsrolle (BYPASSRLS) liefert ["grant-a","grant-foreign-group"]
|
||||
Alle 74 Pruefungen bestanden.
|
||||
```
|
||||
|
||||
Regelliste aus `pg_policies` der lebenden Datenbank (Systemkatalog, nicht die
|
||||
Migrationsdatei):
|
||||
|
||||
```
|
||||
GroupMembership#tenant_isolation_policy#ALL#(("groupId" IN (SELECT "Group".id FROM "Group" WHERE ("Group"."tenantId" = current_tenant_id()))) AND ("userId" IN (SELECT "User".id FROM "User" WHERE ("User"."tenantId" = current_tenant_id()))))
|
||||
ModuleGrant#tenant_isolation_policy#ALL#(("tenantId" = current_tenant_id()) AND (("groupId" IS NULL) OR ("groupId" IN (SELECT "Group".id FROM "Group" WHERE ("Group"."tenantId" = current_tenant_id())))) AND (("userId" IS NULL) OR ("userId" IN (SELECT "User".id FROM "User" WHERE ("User"."tenantId" = current_tenant_id())))))
|
||||
SearchProvider#tenant_isolation_policy#ALL#("tenantId" = current_tenant_id())
|
||||
TenderRssFeedSource#tenant_delete_policy#DELETE#("tenantId" = current_tenant_id())
|
||||
TenderRssFeedSource#tenant_insert_policy#INSERT##WITH CHECK ("tenantId" = current_tenant_id())
|
||||
TenderRssFeedSource#tenant_platform_read_policy#SELECT#(("tenantId" = current_tenant_id()) OR ("tenantId" IS NULL))
|
||||
TenderRssFeedSource#tenant_update_policy#UPDATE#USING ("tenantId" = current_tenant_id())#WITH CHECK ("tenantId" = current_tenant_id())
|
||||
```
|
||||
|
||||
### (r2) Signaltabelle — beide Fehlerrichtungen je Regel
|
||||
|
||||
| Regel | Zu streng (Falsch-Negativ, DoS) | Zu locker (Falsch-Positiv, Rechteausweitung) |
|
||||
|---|---|---|
|
||||
| `GroupMembership` | Wäre die neue Regel zu streng, würde eine tatsächlich mandanteneigene Mitgliedschaft unsichtbar — Signal: `groupmembership-folgt-join-auf-group` würde fehlschlagen (liefert heute korrekt 1 Zeile) | `groupmembership-schreiben-fremder-benutzer-abgelehnt` — Signal wäre ein GELUNGENES Einfügen einer fremden Benutzerkennung; heute abgewiesen |
|
||||
| `ModuleGrant` | Eine eigene, korrekt referenzierende Freigabe würde unsichtbar — Signal: `modulegrant-gebunden-nur-eigene-zeile` würde fehlschlagen (heute 1 Zeile) | `modulegrant-fremde-gruppe-abgelehnt`/`modulegrant-fremder-benutzer-abgelehnt` — Signal wäre ein GELUNGENES Einfügen mit fremder Gruppen- bzw. Benutzerkennung; heute beide abgewiesen |
|
||||
| `TenderRssFeedSource` (Lese-/Schreibsplit) | Zu streng bedeutet: die plattformweite Zeile bleibt trotz Bindung unsichtbar — Signal: `tenderrssfeed-plattformzeile-gebunden-sichtbar` würde fehlschlagen (heute sichtbar unter beiden Mandanten) ODER die eigene Zeile verschwindet — Signal: `tenderrssfeed-eigene-zeile-gebunden-weiterhin-sichtbar` würde fehlschlagen | Zu locker bedeutet: ein Mandant kann die plattformweite Zeile ändern/entfernen — Signal: `tenderrssfeed-gebundenes-aendern-der-plattformzeile-abgelehnt`/`tenderrssfeed-gebundenes-loeschen-der-plattformzeile-abgelehnt` würden fehlschlagen (heute beide: 0 betroffene Zeilen) |
|
||||
|
||||
### (r3) Stellen, an denen Leere weiterhin als Abwesenheit gedeutet wird
|
||||
|
||||
Namentliche Liste, wie in den Abschnitten (d)/(g3)/(t3)/(u3)/(m3) oben je
|
||||
Bereich geführt — hier bereichsübergreifend für die drei betroffenen
|
||||
Tabellen, inklusive der NEUEN Stelle aus Befund F:
|
||||
|
||||
- `TenderRssFeedSourceService.listForUser` (**NEU seit 260910-jab, Aufgabe
|
||||
2**) — vor dieser Reparatur lieferte der ungebundene Pfad nach dem
|
||||
Scharfschalten NICHTS (eine schreiende Leere, die auffällt). Nach der
|
||||
Reparatur würde er UNGEBUNDEN nur die plattformweiten Zeilen liefern — eine
|
||||
kurze, glaubhafte Teilantwort, die NICHT auffällt (der Nutzer sieht
|
||||
plattformweite Feeds und hat keinen Anlass zu melden, dass seine eigenen
|
||||
fehlen). Deshalb gebunden — die einzige Stelle, die diese Aufgabe still
|
||||
falsch gemacht hätte, wenn sie nicht mitgebunden worden wäre.
|
||||
- Alle bereits in (t3) genannten fünf lautlosen Stellen in den beiden
|
||||
Tender-Hintergrunddiensten bleiben unverändert lautlos — von dieser
|
||||
Regeländerung nicht betroffen.
|
||||
- `GroupMembership`/`ModuleGrant` selbst tragen keine "Leere als Abwesenheit
|
||||
gedeutet"-Stelle im hier verstandenen Sinn (eine ABGEWIESENE Schreibung ist
|
||||
ein harter Fehler, keine stille Leere) — die relevante Fehlerrichtung ist
|
||||
hier die Rechteausweitung (r2), nicht die stille Leere.
|
||||
|
||||
### (r4) Was dieser Durchlauf bewusst nicht löst
|
||||
|
||||
- **Der Verwaltungsweg für plattformweite Zeilen fehlt.** Unter der
|
||||
Anwendungsrolle lässt sich eine plattformweite `TenderRssFeedSource`-Zeile
|
||||
weder anlegen noch entfernen — in der alten wie in der neuen Regel, weil
|
||||
jede Schreibregel einen Mandanten verlangt. `createPlatform`/`remove`
|
||||
bleiben deshalb bewusst ungebunden. Als eigener offener Ledger-Eintrag
|
||||
festgehalten (WINDOWS #24), damit dieser Rest nicht mit #19 verschwindet —
|
||||
ein Verwaltungsweg (z. B. eine eigene Systemrolle oder ein expliziter
|
||||
Admin-Bypass-Pfad) ist Gegenstand von Etappe 4.
|
||||
- **Die fehlende Benutzerdimension der Ausschreibungsregeln.** Selbst
|
||||
gemessen statt übernommen (`grep -rn "current_setting\|set_config"
|
||||
apps/api/src apps/api/prisma/migrations`, 260910-jab): es existiert
|
||||
GENAU EINE Sitzungsvariable, `app.current_tenant`
|
||||
(`prisma-tenant.extension.ts`, `20260618112133_rls_policies`). Es gibt
|
||||
KEINE zweite Sitzungsvariable für den Benutzer — Tabellen wie
|
||||
`TenderSavedSearch` (siehe `tendersavedsearch-fremder-nutzer-desselben-
|
||||
mandanten-sichtbar` oben) haben deshalb strukturell keine Möglichkeit,
|
||||
eine Benutzerdimension auf Datenbankebene durchzusetzen, ohne eine solche
|
||||
Variable erst einzuführen. Nicht gebaut in diesem Durchlauf — die
|
||||
anwendungsseitige `userId`-Filterung bleibt der einzige Schutz.
|
||||
- **Die plattformweite Eindeutigkeit von Anmeldename und Adresse (WINDOWS
|
||||
#22)** — unverändert, nicht Gegenstand dieses Plans.
|
||||
|
||||
### (r5) Was dieser Durchlauf bewusst nicht anfasst
|
||||
|
||||
- `SearchProvider` — die Regel bleibt wörtlich unverändert (`"tenantId" =
|
||||
current_tenant_id()`), mit gemessener Begründung (widerlegte Prämisse,
|
||||
siehe `docs/mandantentrennung-zugriffsklassifikation.md`).
|
||||
- Die Regeln auf `Group` und `TenantModuleActivation` — beide unverändert,
|
||||
nicht Teil der drei benannten Löcher.
|
||||
- Der Schalter (`DATABASE_URL`, Rolle `tessera`) — bleibt aus. Die drei
|
||||
Regeln sind heute wirkungslos; das Wegwerf-Werkzeug und die Regelliste der
|
||||
lebenden Datenbank sind die einzigen Zeugen dafür, dass sie greifen.
|
||||
|
||||
## Verweis
|
||||
|
||||
Die Bestandsaufnahme, welche Fundstelle den hier beschriebenen Übergang
|
||||
|
||||
Reference in New Issue
Block a user