catch (e: any) in groups, module-grants, ldap, user, admin-seed, calendar
und vier tenders-Diensten auf catch (e: unknown) umgestellt. Die
Eingrenzung passiert an der Verwendungsstelle, nicht per Zusicherung.
Neu: apps/api/src/prisma/prisma-error.ts mit prismaErrorCode() und
prismaErrorTarget(). Bewusst Form-Pruefungen statt instanceof
Prisma.PrismaClientKnownRequestError - gemessen: samtliche Testdoppel in
apps/api werfen new Error(...) mit angehaengtem .code (groups, user, ldap,
tenders, module-grants, admin-seed) und dashboard.service.spec.ts:451 ein
reines { code: 'P2002' }. Ein instanceof-Test haette all diese Werte in den
anderen Zweig geschickt - Verhaltensaenderung, verboten nach D-03/T-M34-06.
Die Helfer bilden err?.code und err?.meta?.target eins zu eins ab.
ldap.service.ts liest zusaetzlich meta.target; prismaErrorTarget() gibt
unknown zurueck, weil der Bestand dort Array UND Zeichenkette getrennt
behandelt - ein engerer Typ waere eine Behauptung.
calendar.service.ts:341 nutzt instanceof Error statt e?.message: gemessen
wirft validateUrlNotPrivate() ausschliesslich ForbiddenException (der
eigene catch dort setzt jeden Fremdfehler in eine um), also trifft
instanceof dieselben Faelle. Ersatzzweig 'URL not allowed' unveraendert.
noExplicitAny in apps/api/src: 56 -> 38. type-check 4/4, lint 5/5 (0
error), apps/api 72/1143, apps/web 73/531, rls-access-inventory 30/30.
noNonNullAssertion 56, as unknown as 33, Unterdrueckungsmarker 1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- prisma-tenant.extension.ts: (prisma as any) und die Handannotation an
$allOperations in forTenant()/forSystem() entfernt; Kopfkommentar
unveraendert. .then((results: any[]) => ...) auf unknown[] umgestellt.
- 105 Aufrufstellen `const X = forTenant(...) as any` / `forSystem(...) as
any` von der Zusicherung befreit, Zuweisungsform woertlich erhalten
(rls-access-inventory.spec.ts bleibt scharf, 30/30 gruen einzeln
geprueft).
- withTenantTransaction(): Prisma.TransactionClient fuer tx probiert,
gemessen verworfen - bricht das Testdoppel in
prisma-tenant.extension.spec.ts (TS2322 auf einem absichtlich
unvollstaendigen Fake-Objekt). tx bleibt any, mit Begruendung am Typ.
- Gefolge des jetzt getypten Klienten entfernt: any[]-Annotationen und
.map((x: any) => ...) in groups.service.ts, module-grants.service.ts,
dkv.service.ts, ldap-config.service.ts, tenders.controller.ts:270.
- Befund (D-03): tender-matching.service.ts:159 trug eine Handannotation
(match: { tender: unknown }), die den Wert nur deshalb auf unknown
verengte, um TS7006 unter dem alten any-Klienten zu vermeiden - mit dem
getypten Klienten war das falsch. Annotation geloescht, kein Ersatz
durch Zusicherung.
- Zwei any bleiben gezielt in groups.service.ts (u/a in
ensureDefaultGroup(), gefolge von tx: any) - Begruendung am Code.
noExplicitAny apps/api/src: 288 -> 149 (Schranke 155). type-check 4/4,
lint 5/5 (0 error). apps/api 72/1143 gruen, apps/web 73/531 gruen,
rls-access-inventory.spec.ts 30/30 gruen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
- TenderRssFeedSourceService.listForUser() nimmt jetzt (userId, tenantId)
entgegen und laeuft ueber einen gebundenen Klienten (forTenant) — die neue
Leseregel schliesst plattformweite Zeilen ein, die Reparatur haette den
ungebundenen Pfad sonst still auf nur die plattformweiten Zeilen reduziert
(Befund F). createPlatform/remove bleiben bewusst ungebunden, Kommentare an
der neuen Regel richtiggestellt.
- TendersController.listRssFeeds reicht die Mandantenkennung aus dem
Aufrufzusammenhang durch.
- Vier Aufzeichnungen im Quelltext (module-access.service.ts,
groups.service.ts, module-grants.service.ts, rls-coverage.spec.ts) sagen
jetzt, dass die Datenbankregel seit 20260910120000_rls_widen_membership_
grant_and_platform_read beide Seiten prueft; die Anwendungspruefungen
bleiben unveraendert bestehen (zweites Netz, wirkt vor dem Scharfschalten
als einziger Schutz).
- Zwei-Klienten-Nachweis in module-grants.service.spec.ts ergaenzt (Kommentar,
warum die beiden Cross-Tenant-Tests nach der Regelaenderung nicht entfallen
duerfen) und in tender-rss-feed.service.spec.ts umgekehrt (listForUser
bindet jetzt).
- Rule 1: implizites any beim Destrukturieren in listRssFeeds (feeds ist seit
der Bindung `any`) mit expliziter Annotation behoben.
- Falsifizierungsnachweis durchgefuehrt: Bindungsaufruf zurueckgenommen,
genau ein Test wurde rot (AssertionError, 0 statt der erwarteten
Aufrufe), Ruecknahme rueckgaengig gemacht.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
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
- ModuleGrantsService.getUserAccess() now selects internalName on the
membership query's group projection (already present via `include:
{ group: true }` on the grant query)
- Both display points (viaGroups names, membership chips' name field)
use internalName ?? name; groups[] sorting now runs over the
displayed name as a result, distinct from GroupsService.listForTenant()
which still sorts by the raw name column
- 3 new test cases: fallback set/unset, sort-by-displayed-name
- No apps/web/ changes (verified via git diff --name-only)
- New groupMembership.findMany query, tenant-scoped via group.tenantId
(GroupMembership has no own tenantId column)
- Response shape changes from an array to { groups, modules }; modules
entries stay field-identical to before
- Group without any module grant now stays visible, closing the
reproduced defect
- assertTargetBelongsToTenant prüft groupId/userId aus dem Request-Body
gegen tenantId aus dem JWT (T-15-01), vor jedem Grant-Insert
- grant: Entweder-oder-Regel (D-04), aktive TenantModuleActivation (D-02),
P2002 als Erfolg (Doppelklick-Schutz)
- getMatrix (D-15) und getUserAccess (D-16) für Matrix-Seite und
Benutzer-Detail, jeweils sortiert und mandantengescoped
- 20 Tests inkl. adjacency/empty/ordering/idempotency/concurrency