Files
schalli 46f0e781be
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 54s
Tessera CI/CD / Build & Publish Images (push) Successful in 28s
docs(quick-260911-fh9): Etappe 2 Bereich auth abgeschlossen und verifiziert
2026-09-11 12:10:02 +02:00

12 KiB

phase, verified, status, score, covered_files, covered_digest, behavior_unverified, overrides_applied
phase verified status score covered_files covered_digest behavior_unverified overrides_applied
quick-260911-fh9 2026-09-11T12:10:00Z passed 8/8 must-haves verified
.planning/WINDOWS.md
.planning/quick/260911-fh9-mandantentrennung-etappe-2-bereich-auth-/260911-fh9-PLAN.md
.planning/quick/260911-fh9-mandantentrennung-etappe-2-bereich-auth-/260911-fh9-SUMMARY.md
apps/api/scripts/rls-scratch-check.mjs
apps/api/src/auth/auth.controller.spec.ts
apps/api/src/auth/auth.controller.ts
apps/api/src/auth/auth.module.ts
apps/api/src/auth/auth.service.spec.ts
apps/api/src/auth/auth.service.ts
docs/mandantentrennung-etappe2-fehlerrichtung.md
docs/mandantentrennung-zugriffsklassifikation.md
v1:sha256:a4eee94bfd0ca019936b1d7766a7bcaa03d97438c1319dad220a021d1e947cde 0 0

Quick Task 260911-fh9: Bereich auth der Mandantentrennung Etappe 2 — Verification Report

Task Goal: getMe, changePassword, adminResetPassword an den Mandanten aus dem JWT-Claim binden (NICHT an das umschaltbare req.tenantId), die fehlenden Mandanten-/Rollenpruefungen in adminResetPassword schliessen, die Identitaets-Attrappe im Test durch einen Zwei-Klienten-Nachbau ersetzen, den Anmeldeweg unangetastet lassen, das Klassifikationsdokument nachziehen.

Verified: 2026-09-11T12:10:00Z Status: passed Re-verification: No — initial verification

Goal Achievement

Observable Truths

# Truth Status Evidence
1 getMe/changePassword binden an @CurrentUser().tenantId (Claim), nicht an req.tenantId/x-tenant-id ✓ VERIFIED auth.controller.ts: me/changePassword reichen ausschliesslich user.tenantId aus @CurrentUser() durch; grep -c "req.tenantId|x-tenant-id" = 0. Eigener Test belegt, dass ein SUPER_ADMIN mit Claim t1 ebenfalls ('t1','u1') liefert — der Handler liest strukturell nur @CurrentUser(), eine umgeschaltete x-tenant-id-Kopfzeile kann das nicht beeinflussen, weil der Handler keinen zweiten Kanal fuer den Mandanten besitzt. DB-seitig bestaetigt auth-getme-generierter-client-gebunden-fremder-mandant-liefert-null (rls-scratch-check.mjs, selbst ausgefuehrt), dass ein unter fremdem Mandanten gebundener Klient die eigene Zeile nicht sieht.
2 adminResetPassword: Tenant-Grenze (ADMIN A -> User B unerreichbar) UND Rollen-Grenze (ADMIN setzt kein SUPER_ADMIN-Kennwort) sind geschlossen und je mit benanntem Test gepinnt ✓ VERIFIED auth.service.ts: ForbiddenException bei user.role === Role.SUPER_ADMIN && callerRole !== Role.SUPER_ADMIN; BadRequestException('User not found') bei unsichtbarer (fremdmandantiger) Zeile. Beide durch benannte Tests in auth.service.spec.ts gepinnt ("FREMDER Mandant: BadRequestException...", "Aufrufer ADMIN, Ziel SUPER_ADMIN...ForbiddenException..."), beide zusaetzlich DB-seitig durch rls-scratch-check.mjs (auth-adminreset-generierter-client-gebundenes-update-fremder-mandant-scheitert-laut) bestaetigt. SUPER_ADMIN-Pfad laeuft ueber UserService.findByIdForPlatformAdmin; resolveTargetTenantId im Controller leitet fuer SUPER_ADMIN den Mandanten des ZIELS ab (target.tenantId), fuer ADMIN den des Aufrufers (currentUser.tenantId) — gelesen in auth.controller.ts, gepinnt durch 4 Controller-Tests.
3 Die drei SECURITY-DEFINER-Anmeldefunktionen sind unangetastet; pg_proc bestaetigt es ✓ VERIFIED Selbst ausgefuehrt: TESSERA_SCRATCH_ADMIN_URL=... node apps/api/scripts/rls-scratch-check.mjs gegen tessera-ctl-db-1 (172.19.0.2) — 120/120 Pruefungen bestanden, darunter auth-anmeldefunktionen-security-definer-unveraendert (3 Funktionen, prosecdef=true, provolatile='s', search_path=public, pg_temp, LIMIT 1) und auth-anmeldesuche-findet-benutzer-weiterhin-mit-festem-spaltensatz (genau 9 Spalten nach der Wegwerftabellen-Erweiterung um 5 Spalten — weder avatarPath noch accentColor noch email durchgelassen). git diff --name-only 4c3172b..HEAD -- apps/api/prisma leer (orchestratorseitig bereits gemessen, selbst nachgemessen).
4 Identitaets-Attrappe ersetzt durch asymmetrischen Zwei-Klienten-Nachbau ✓ VERIFIED auth.service.spec.ts: forTenant umgeleitet auf unboundClient.__makeBoundClient(tenantId); ungebundener Basisclient (fake) hat NUR $queryRaw/__makeBoundClient/__users/__resetTokens/__boundCallLog — kein user/passwordResetToken; gebundener Klient (makeScopedUser/makeScopedResetToken) hat user/passwordResetToken, kein $queryRaw. Selbst falsifiziert: getMe probeweise auf this.prisma (ungebunden) zurueckgebaut -> exakt 4 Tests rot mit TypeError: Cannot read properties of undefined (reading 'findUnique') — genau wie im SUMMARY behauptet. Aenderung zurueckgenommen, 29/29 wieder gruen, git status sauber.
5 Genau 3 verbleibende ungebundene Stellen sind $queryRaw-Anmeldesuchen, keine Modellzugriffe ✓ VERIFIED grep -c 'this\.prisma\.\$queryRaw' auth.service.ts = 3 (Zeilen 109, 217, 254 — validateUser, requestPasswordReset, resetPassword); grep -c 'this\.prisma\.user' auth.service.ts = 0.
6 Transienter Rotzustand von rls-access-inventory.spec.ts zwischen Aufgabe 2 und 3, jetzt gruen; Klassifikationszeile auth.service.ts/user = gebunden ✓ VERIFIED npm --prefix apps/api run test -- src/prisma/rls-access-inventory.spec.ts: 11/11 gruen (selbst ausgefuehrt). docs/mandantentrennung-zugriffsklassifikation.md Zeile 422: Stand gebunden, Begruendung mit allen drei Methoden.
7 Ledger-Eintraege #28 (Frontend-Leere) und #29 (Schwesterweg PATCH /users/:id) existieren, offen, ohne Reparatur ✓ VERIFIED .planning/WINDOWS.md: beide Eintraege vorhanden, "status": "open". #29-Behauptung selbst nachgeprueft: UserController.update prueft nur dto.role === Role.SUPER_ADMIN (Neuzuweisung), NICHT user.role === Role.SUPER_ADMIN (Bestandsrolle des Ziels) — die Luecke ist real und unbehoben.
8 Fuenf handgepflegte Klassifikationsstellen nachgezogen (Uebersichtszeile, Summenzeile, Bestandsaufnahme, Klassen-Verteilung, Was diese Etappe NICHT entscheidet); Umfang gegen 6236b30/4c3172b als Erlaubnisliste gegatet ✓ VERIFIED Alle fuenf Stellen selbst nachgesehen: Uebersichtszeile auth 3 10, Summenzeile 78/167, Bestandsaufnahme-Zeile gebunden, Klassen-Verteilung 32/17/13/2=64 mit Stand-Vermerk 260911-fh9, neuer Etappe-3-Punkt in "Was diese Etappe NICHT entscheidet". git diff --name-only 4c3172b..HEAD = genau die 10 im Plan erlaubten Dateien (plus .planning/); apps/api/prisma, apps/web, Compose/Env unveraendert.

Score: 8/8 truths verified (0 present, behavior-unverified)

Required Artifacts

Artifact Expected Status Details
apps/api/scripts/rls-scratch-check.mjs 13. Abschnitt runAuthAreaChecks, >=10 neue Pruefungen, pg_proc-Messung ✓ VERIFIED Eigenstaendig ausgefuehrt: 120/120 bestanden, korrekte Reihenfolge (runTenantAreaChecks -> runAuthAreaChecks -> runTransactionShapeMeasurement, Zeilen 4016-4018)
docs/mandantentrennung-etappe2-fehlerrichtung.md Abschnitt ## Bereich auth vor ## Verweis, (h1)-(h5) ✓ VERIFIED Zeilen 2473-2676, unmittelbar vor ## Verweis (2677), alle fuenf Unterabschnitte vorhanden
apps/api/src/auth/auth.service.ts drei gebundene Methoden, je EIN tenantPrisma ✓ VERIFIED Gelesen vollstaendig, 0 ungebundene this.prisma.user, genau 3 $queryRaw, 6 forTenant-Aufrufstellen
apps/api/src/auth/auth.service.spec.ts Zwei-Klienten-Nachbau, alle Faelle, Falsifizierung ✓ VERIFIED 29/29 Tests gruen; Falsifizierung selbst reproduziert (4 Tests rot, zurueckgenommen)
apps/api/src/auth/auth.controller.ts Claim-Durchreichung, Rollenverzweigung ✓ VERIFIED Gelesen vollstaendig; 0 req.tenantId/x-tenant-id/PrismaService
apps/api/src/auth/auth.module.ts importiert UserModule, zyklusfrei ✓ VERIFIED GroupsModule importiert nichts; nur app.module.ts importiert AuthModule — selbst nachgemessen
apps/api/src/auth/auth.controller.spec.ts NEU, Mandantenquelle, Metadaten ✓ VERIFIED 23/23 Tests gruen; Falsifizierung selbst reproduziert (2 Tests rot, zurueckgenommen)
docs/mandantentrennung-zugriffsklassifikation.md 5 Stellen nachgezogen ✓ VERIFIED Siehe Truth 8
.planning/WINDOWS.md 2 neue offene Eintraege ✓ VERIFIED #28, #29 vorhanden, offen
From To Via Status Details
auth.controller.ts me/changePassword AuthService.getMe/changePassword user.tenantId aus @CurrentUser() ✓ WIRED Grep + Test bestaetigt
auth.controller.ts adminResetPassword UserService.findByIdForPlatformAdmin resolveTargetTenantId fuer SUPER_ADMIN ✓ WIRED Test bestaetigt (findByIdForPlatformAdmin genau 1x mit 'target')
AuthModule UserModule imports: [...] ✓ WIRED auth.module.ts importiert UserModule; zyklusfrei statisch gemessen
auth.service.ts validateUser/requestPasswordReset/resetPassword auth_lookup_*-Funktionen (SECURITY DEFINER) this.prisma.$queryRaw ✓ WIRED 3/3, unveraendert, pg_proc-Messung bestanden

Behavioral Spot-Checks

Behavior Command Result Status
auth.service.spec.ts + auth.controller.spec.ts laufen npm --prefix apps/api run test -- src/auth/auth.service.spec.ts src/auth/auth.controller.spec.ts 52/52 gruen (29+23) ✓ PASS
rls-access-inventory.spec.ts laeuft npm --prefix apps/api run test -- src/prisma/rls-access-inventory.spec.ts 11/11 gruen ✓ PASS
Volle Testsuite npm --prefix apps/api run test (einmal) 951/951 gruen, 60 Dateien ✓ PASS
type-check npm --prefix apps/api run type-check exit 0 ✓ PASS
Wegwerf-Werkzeug gegen laufende DB TESSERA_SCRATCH_ADMIN_URL=... node apps/api/scripts/rls-scratch-check.mjs (frisch aufgeloeste Adresse 172.19.0.2) 120/120 bestanden ✓ PASS
Falsifizierung 1: getMe auf ungebundenen Klienten zurueckgebaut Codeaenderung + vitest run auth.service.spec.ts 4 Tests rot (TypeError: Cannot read properties of undefined (reading 'findUnique')), zurueckgenommen, 29/29 wiederhergestellt ✓ PASS
Falsifizierung 2: resolveTargetTenantId fuer SUPER_ADMIN auf currentUser.tenantId reduziert Codeaenderung + vitest run auth.controller.spec.ts 2 Tests rot (Fan-out-Faelle), zurueckgenommen, 23/23 wiederhergestellt ✓ PASS

Requirements Coverage

Requirement Source Plan Description Status Evidence
WINDOWS-18 260911-fh9-PLAN.md Die drei Nach-Anmeldungs-Methoden binden an den Mandanten aus dem Sitzungsnachweis ✓ SATISFIED Truth 1, 3
ETAPPE-2-AUTH 260911-fh9-PLAN.md Rechteausweitung ueber und innerhalb der Mandantengrenze in adminResetPassword geschlossen, Dokumentation nachgezogen ✓ SATISFIED Truth 2, 6, 7, 8

Anti-Patterns Found

Keine. grep -n "TODO\|FIXME\|TBD\|HACK\|PLACEHOLDER" apps/api/src/auth/auth.service.ts apps/api/src/auth/auth.controller.ts apps/api/src/auth/auth.module.ts liefert keine Treffer in den geaenderten Codedateien (Kommentare beziehen sich auf Ledger-Eintraege mit Referenznummern, keine unbezeichneten Marker).

Human Verification Required

Keine. Dieser Bereich ist rein backend-seitig, ueber Unit-Tests, ein Wegwerf-Datenbank-Werkzeug und Quelltext-Messung vollstaendig ueberprueft — keine visuelle, Echtzeit- oder UX-Beurteilung noetig.

Gaps Summary

Keine Luecken gefunden. Alle acht abgeleiteten Wahrheiten (must-haves aus PLAN-Frontmatter, kombiniert mit den elf Pruefpunkten aus dem Verifikationsauftrag) sind mit unabhaengig reproduzierten Belegen (Testlaeufen, Datenbankwerkzeug-Ausgabe, Falsifizierungen, Quelltext-Lesungen) bestaetigt. Zwei bewusst offene Ledger-Eintraege (#28, #29) sind korrekt als offene Punkte dokumentiert, nicht als geschlossen behauptet — sie liegen ausserhalb der Erlaubnisliste dieses Plans und wurden dort auch nicht angefasst.


Verified: 2026-09-11T12:10:00Z Verifier: Claude (gsd-verifier)