Verifikation unabhaengig nachgemessen: 651/651 API-Tests, 233/233 Web-Tests, beide Typpruefungen sauber. Der Sicherheitsfund T-Q3-01 wurde nicht nur gruen getestet, sondern falsifiziert: der Verifizierer hat die neue Besitzpruefung testweise zurueckgebaut, woraufhin der Test fehlschlug und die fremde Adresse tatsaechlich in prisma.user.update() landete. Danach sauber zurueckgesetzt. Ebenso gegengeprueft: beide Anlege-Wege (Sync und Einzelimport) nutzen denselben Kollisionsentscheider, im ausgelieferten Code stehen keine kundenspezifischen Namen oder Adressen, rohe ORM-Texte erreichen die Oberflaeche nicht mehr, und die Gruppensuche unter internem wie AD-Namen (#6c) ist per Regressionstest gesichert. Eine Abweichung des Ausfuehrenden ist dokumentiert und bestaetigt: die im Plan vorgesehene Testvorlage war wegen Kurzschlussauswertung schon gegen den unveraenderten Code gruen; mit einem zweiten, nicht passenden Konto reproduziert sie den Absturz nun wirklich. Status human_needed: die fuenf Browser-Pruefungen brauchen einen Neubau und sind Sache des Users. WINDOWS #14 und #15 bleiben bis dahin offen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
12 KiB
phase, plan, subsystem, tags, status, dependency-graph, tech-stack, actuals, decisions, metrics
| phase | plan | subsystem | tags | status | dependency-graph | tech-stack | actuals | decisions | metrics | |||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260909-ab3 | 01 | ldap |
|
complete |
|
|
|
|
|
Quick Task 260909-ab3: Matrix-Suche und Sync-Meldungen reparieren Summary
Freigaben-Matrix-Suche filtert Modul- und Gruppenachse jetzt unabhaengig (WINDOWS #14 geschlossen); AD-Sync legt Konten mit kollidierender E-Mail-Adresse an — ohne Adresse — statt sie stillschweigend zu uebergehen, und der Bericht spricht durchgehend verstaendliches Deutsch (WINDOWS #15 geschlossen).
What Was Built
Drei getrennte, jeweils fuer sich lauffaehige Commits — exakt wie im Plan vorgegeben.
Task 1 — Matrix-Suche filtert nur noch die getroffene Achse (commit e8c2411)
page.tsx:filteredModules/filteredGroupsdurch einen gemeinsamenuseMemoersetzt, dermoduleHits/groupHitsgetrennt ermittelt und nach der im Plan festgelegten Wahrheitstabelle kombiniert (leeres Feld -> alles; nur eine Achse trifft -> die andere Achse bleibt vollstaendig stehen; beide treffen -> beide gefiltert; nichts trifft ->noMatch).- Regressionsschutz fuer WINDOWS #6c (Spaltensuche findet eine importierte Gruppe sowohl unter internem als auch AD-Namen) unveraendert erhalten und mit einem eigenen Testfall abgesichert.
- Neue Meldung
adminModules.grants.noSearchResultsin de.json/en.json — de.json folgt der im Bestand bereits etablierten typografischen Anfuehrungszeichen-Konvention („...\", siehenameCollisionError). - Vier neue Testfaelle in
grants-matrix.test.tsxvorab gegen den unveraenderten Bestand rot gelaufen — alle vier aus den im Plan vorhergesagten Gruenden (fehlende Modul-/Gruppennamen bzw. fehlender Meldungstext).
Task 2 — Kollidierende AD-Konten werden angelegt, ohne Adresse (commit 1222951)
schema.prisma:User.emailaufString? @uniquegestellt; Migration20260909120000_user_email_optionalgeschrieben (nurDROP NOT NULL, Eindeutigkeitsindex unangetastet) — nicht ausgefuehrt, wie vom Plan verlangt.ldap.service.ts: neue private MethoderesolveEmailForWrite(desiredEmail, ownRecordId)fragtprisma.user.findUnique({where:{email}}))und gibt die Adresse nur frei, wenn niemand sie haelt oder der Halter der eigene Datensatz ist (T-Q3-01) — sonst wird kein Konto umgehaengt, sondern ein Kollisionsvermerk erzeugt.- In
upsertMappedUser(Sync-Pfad) UNDimportUsersByDn(Handimport-Pfad) verdrahtet, damit der zweite Anlageweg nicht als Luecke mit rohem Datenbanktext bestehen bleibt. LdapSyncResultumemailConflicts({account, email}[]),skippedNoLogin(string[]) undentryFailures(string[]) erweitert. Die englische "no username mapped"-Meldung im Sync-Pfad ist verschwunden (wandert jetzt inskippedNoLogin); der generische Catch-Block schreibt den technischen Wortlaut nur noch viathis.logger.error, nie in den Bericht (T-Q3-02).UserService.createnimmtemailjetzt optional entgegen. Type-Check deckte genau die im Plan vorhergesagten zwei Fundstellen auf (tender-digest.scheduler.ts:140,tender-matching.service.ts:131) — beide brechen jetzt mitcontinueab, wenn der Empfaenger keine Adresse hat.- Fuenf neue Testfaelle in
ldap.service.spec.tsvorab gegen den unveraenderten Bestand rot gelaufen, jeweils aus dem im Plan vorhergesagten Grund. Eine bestehendetoEqual-Assertion (empty base DN no-op-Test) musste um die drei neuen Ergebnisfelder ergaenzt werden (legitime Bestandspflege, keine Verhaltensaenderung).
Task 3 — Verstaendlicher Sync-Bericht und Konten ohne Adresse in der UI (commit 2167046)
admin/ldap/page.tsx:SyncResult-Interface um die drei neuen Felder erweitert; drei neue Berichtsabschnitte in der vorgegebenen Reihenfolge (Kollision: Bernstein wiedefaultMarkerMoved; uebersprungen ohne Anmeldenamen:text-muted-foreground, ausdruecklich kein Rot; unerwartete Fehler:text-destructive).- Vier neue Sprachschluessel unter
admin.ldap.syncin de.json/en.json; Umlaut-Gate lief gruen ohne neue Allowlist-Eintraege (nur "Adresse" traf dasss-Muster, war bereits erlaubt). GroupMembersModal.tsx:TenantUser.emailaufstring | null, Suchvergleich (Zeile 104) analog zum bestehendendisplayName-Muster mit(u.email ?? '')abgesichert.users/page.tsx:User.emailaufstring | null; Formularuebernahme faellt auf''zurueck, Tabellenzelle zeigt einen Gedankenstrich (–) statt einer leeren Zelle.UserFormData.emailbleibt Pflichtfeld fuer die Handanlage.
Verification Results
- API-Testsuite:
pnpm --filter @tessera/api exec vitest run— 651/651 gruen (46 Dateien), inklusiveldap.service.spec.tsmit 67/67 (62 bestehend + 5 neu). - API-Typpruefung:
pnpm --filter @tessera/api type-check— sauber. prisma validate/prisma generate: beide sauber; Migration existiert, wurde nicht ausgefuehrt.- Web-Testsuite:
pnpm --filter @tessera/web exec vitest run— 233/233 gruen (38 Dateien), inklusivegrants-matrix.test.tsx(12/12) undgroups-page.test.tsx(13/13). - Web-Typpruefung:
pnpm --filter @tessera/web type-check— sauber. - Sprachschluessel-/Umlaut-Gate:
umlaut-guard.spec.tsgruen bei jedem Zwischenstand — alle neuen Schluessel existieren in de.json UND en.json, kein neues, ungeprueftes Umlaut-Ersatzwort. - Alle vier Befehle aus
<verification>des Plans ein weiteres Mal am Ende, vom Wurzelverzeichnis aus, gruen.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Task-3-Testfixture konnte den bezeichneten Absturz nicht ausloesen
- Found during: Task 3, RED-Lauf vor der Implementierung
- Issue: Der Plan gibt exakt einen Mock-Benutzer vor (
{ id: 'u12', username: 'funktionskonto', displayName: null, email: null }) und den Suchbegriff'funktion'. Da'funktionskonto'.includes('funktion')bereits ueberu.usernamematcht, greift die JavaScript-OR-Kurzschlussauswertung (||) VOR der ungesichertenu.email.toLowerCase()-Zeile — der Testlauf gegen den unveraenderten Bestand war gruen, nicht rot wie vom Plan vorhergesagt. Empirisch mitnode -ebestaetigt (siehe Commit-Nachricht). - Fix: Ein zweites Konto (
{ id: 'u13', username: 'anderes.konto', displayName: null, email: null }) in dieselbe Mock-/users-Antwort aufgenommen. Sein Benutzername matcht den Suchbegriff nicht, sodass die OR-Kette bis zur ungesicherten Zeile durchlaeuft und dort tatsaechlich abstuerzt — Testlauf danach korrekt rot, exakt anGroupMembersModal.tsx:104.funktionskontobleibt Teil der Fixture und der Assertion, wie vom Plan verlangt. - Files modified: apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx
- Verification: Test rot vor der Implementierung (TypeError an der vorhergesagten Zeile), gruen danach; volle Testsuite (233/233) bleibt gruen.
- Committed in:
2167046(Task-3-Commit)
Total deviations: 1 auto-fixed (Rule 1 — Testfixture-Korrektur, kein Produktionscode-Verhalten geaendert)
Impact on plan: Der zugrunde liegende Defekt (ungesichertes .toLowerCase() auf einer moeglicherweise leeren Adresse) war real und wurde wie geplant behoben; nur die Testdaten mussten angepasst werden, damit der Test die Behauptung tatsaechlich pruefte statt trivial gruen zu sein.
Threat Model Verification
Alle sechs mitigate-Eintraege aus dem Plan-Threat-Register sind umgesetzt:
- T-Q3-01 (Spoofing,
upsertMappedUser-Aktualisierungszweig): Mitigiert —resolveEmailForWritevergleicht Halter-ID gegen die eigene Datensatz-ID; Test 3 aus Task 2 belegt, dassprisma.user.updateohneemail-Feld aufgerufen wird, wenn die Adresse einem Dritten gehoert. - T-Q3-02 (Information Disclosure, Sync-Bericht): Mitigiert — Test 5 aus Task 2 belegt per
JSON.stringify(result)-Negativpruefung, dass der geworfene ORM-Text nirgends im Bericht landet; er erscheint nur imlogger.error-Aufruf. - T-Q3-04 (Spoofing, Passwort-Zuruecksetzung bei leerer Adresse): Bestaetigt ohne Codeaenderung — die Suche in
auth.service.tslaeuft ueber eine angefragte Zeichenkette, einnull-Wert in der Spalte wird davon nie getroffen. - T-Q3-03/T-Q3-05/T-Q3-06/T-Q3-SC (accept): Keine Codeaenderung noetig, Einschaetzung des Plans bestaetigt (keine neuen Pakete installiert, kein zusaetzliches DoS-Risiko bei den gemessenen Groessenordnungen).
Self-Check: PASSED
Files:
- FOUND: apps/web/src/app/(portal)/admin/modules/grants/page.tsx
- FOUND: apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx
- FOUND: apps/api/prisma/schema.prisma
- FOUND: apps/api/prisma/migrations/20260909120000_user_email_optional/migration.sql
- FOUND: apps/api/src/ldap/ldap.service.ts
- FOUND: apps/api/src/ldap/ldap.service.spec.ts
- FOUND: apps/api/src/user/user.service.ts
- FOUND: apps/api/src/tenders/tender-digest.scheduler.ts
- FOUND: apps/api/src/tenders/tender-matching.service.ts
- FOUND: apps/web/src/app/(portal)/admin/ldap/page.tsx
- FOUND: apps/web/src/app/(portal)/admin/users/page.tsx
- FOUND: apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.tsx
- FOUND: apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx
- FOUND: apps/web/src/messages/de.json
- FOUND: apps/web/src/messages/en.json
Commits:
Deployment Note (vom Plan uebernommen)
Dieser Plan hat nichts ausgerollt: kein docker compose build, kein up, kein prisma migrate deploy. Die Migration 20260909120000_user_email_optional steckt im API-Image und wird beim naechsten Neubau/Neustart der API automatisch angewandt (apps/api/Dockerfile:38 ruft prisma migrate deploy vor dem Start auf). Solange das nicht geschehen ist, bleibt User.email in der laufenden Datenbank ein Pflichtfeld — ein kollidierendes Konto wird dann weiterhin nicht angelegt, scheitert aber sauber als gezaehlter entryFailures-Eintrag statt mit rohem Techniktext.
Die fuenf im Plan genannten Browser-Nachpruefungen (Punkte 1-5 unter <verification>) sind vom Nutzer nach seinem naechsten Neubau von API und Web nachzuholen — nicht Teil dieser Ausfuehrung.