d8fb9ae07deb4c9450d6f2b78d2722d46a1287f5
277 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d8fb9ae07d |
refactor(quick-260921-m34): Aufgabe 3c - Randschicht beurteilt, drei Befunde gemeldet, 15 bleiben mit Urteil
httpntlm (exchange.provider, exchange-inbox.provider): NtlmOptions und
NtlmResponse beschreiben genau das, was uebergeben und gelesen wird. Die
ueberfluessige Zusicherung (httpntlm as any) faellt weg.
Graph-Rueckrufe (exchange.provider :157/:313): AuthProviderCallback aus dem
SDK selbst statt Handannotation - als import type, also ohne den dynamischen
Import zur Laufzeit zurueckzunehmen.
imapflow: streamToBuffer() nimmt Readable statt NodeJS.ReadableStream (alle
drei Aufrufer reichen client.download().content herein, imapflow deklariert
das als Readable) - damit traegt der Typ destroy() und die Zusicherung
faellt. node.parameters?.name war ebenfalls schon getypt.
nodemailer: ResolvedTransport.options wird SMTPTransport.Options; beide
Zweige bauen reine SMTP-Optionen, createTransport() nimmt sie ohne
Zusicherung.
node-forge: die vier let p7: any werden Captured<PkcsEnvelopedData |
PkcsSignedData> - der MITGELIEFERTE Typ. Die Lesestellen grenzen mit
'certificates' in p7 ein statt zuzusichern; verhaltensgleich, weil der
enveloped-Form das Feld fehlt und beide Schreibweisen dann die leere Liste
liefern. cert.siginfo war bereits getypt.
apps/web/src/test/setup.ts: expect.extend(matchers) traegt ohne Zusicherung
- geprueft im echten Typlauf (setup.ts liegt im include von
apps/web/tsconfig.json, mit einem absichtlichen Fehler nachgewiesen).
BEFUND 4 (D-03, gemeldet, NICHT repariert) imap.provider.ts:78 - der
Ausdruck (node as any).disposition?.parameters?.filename liest .parameters
von einer ZEICHENKETTE: imapflow deklariert disposition als string
(imap-flow.d.ts:448), die Parameter liegen in dispositionParameters (:450).
dispositionFilename ist damit zur Laufzeit immer ''. Folge: Outlook-Anhaenge,
die als application/octet-stream kommen, werden ueber den Dateinamen aus
Content-Disposition NICHT erkannt - nur ueber den aus Content-Type. Umbiegen
waere eine Verhaltensaenderung; die Zusicherung bleibt sichtbar stehen.
BEFUND 5 (D-03, gemeldet, NICHT repariert) imap.provider.ts:402 -
requireTLS kommt in imapflow 1.4.3 NIRGENDS vor, weder in ImapFlowOptions
noch im Laufzeitcode (beides durchsucht). Die Option wird still verworfen;
STARTTLS wird durch sie nicht erzwungen. Genau das } as any hat es
verdeckt. Bleibt stehen, damit der Befund in der Zaehlung sichtbar ist.
BEFUND 6 (D-03, gemeldet, Verhalten unveraendert) httpntlm liefert den
Rumpf als Zeichenkette, nicht als Buffer: httpreq setzt ihn nur bei
gesetzter Option binary auf Buffer (httpreq@1.1.1/lib/httpreq.js:391),
keiner der beiden Aufrufer setzt sie. Der Bestand rief unbesehen
.toString('utf-8') auf - das ging nur gut, weil String.toString() sein
Argument ignoriert. Die Testdoppel reichen dagegen wirklich Buffer herein.
NtlmResponse.body nennt jetzt beide Formen, die Fallunterscheidung liefert
fuer jede exakt dasselbe Ergebnis wie zuvor.
Urteil BLEIBT mit Begruendung im Code an allen 15 verbleibenden Stellen:
3x addCronJob (require-Umweg aus 07-04), 5x node-forge (EC-Zweig und
extensions: any[] sind in @types/node-forge nicht beschrieben, 2x null as
any wo die Typen die Bibliothek nachweislich falsch beschreiben), 2x
imap-Befunde oben, 2x tx: any plus 2x Gefolge (Aufgabe 1), 1x
disposition-Befund.
noExplicitAny in apps/api/src: 31 -> 15 (Ausgang 288, Schranke 45), apps/web
1 -> 0. 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,
ts-expect-error/ts-ignore 0/0, Unterdrueckungsmarker 1. biome.json, alle
package.json und pnpm-lock.yaml unveraendert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
|
||
|
|
3892c5f3c6 |
refactor(quick-260921-m34): Aufgabe 3b - Prisma-nahe Formen getypt, Erkenner-Falle gemeldet
- auth.service.ts:393: (response as any).cookie war schlicht ueberfluessig.
response ist in derselben Signatur bereits Response aus express, die
Schwesterstelle :190 kommt ohne Zusicherung aus. Ersatzlos entfernt.
- calendar.service.ts: das lokal gebaute data-Objekt traegt jetzt
Prisma.CalendarSourceUncheckedCreateInput bzw. ...UncheckedUpdateInput
statt Record<string, unknown> plus Zusicherung. Damit fallen beide
`data as any` weg, ohne dass ein Feld behauptet wird.
- user.service.ts: `let created: any` -> User (die Zuweisung steht im try,
der catch endet ausnahmslos mit throw). `const updateData: any` wird aus
der Signatur hergeleitet - Omit<UpdateUserInput, 'password'> plus dem
daraus berechneten passwordHash; die Parameterform ist dafuer als
UpdateUserInput benannt und nicht neu erfunden. `const results: any[]`
wird Pick<User, keyof typeof PLATFORM_USER_SELECT>[], die Spaltenauswahl
steht als Konstante daneben.
- tenant.controller.ts:69: Elementtyp aus dem hergeleitet, was die Schleife
hineinlegt (fuenf Tenant-Spalten plus userCount).
BEFUND 3 (D-03, gemeldet, kein Verhalten betroffen) Die naheliegende
Prisma-Schreibweise Prisma.UserGetPayload<{ select: typeof X }> laesst
rls-access-inventory.spec.ts rot werden: der Erkenner zaehlt JEDE
select:-Angabe ausserhalb eines erkannten Modellaufrufs als Verstoss und
unterscheidet Typposition nicht von Aufrufposition. Gemessen beim ersten
Versuch. Der Erkenner ist die Mandantenkontrolle (T-M34-03) und wurde
NICHT aufgeweicht - stattdessen leitet der Zeilentyp ueber Pick<User, ...>
her, was ohne das Wort select auskommt. Begruendung steht am Typ.
noExplicitAny in apps/api/src: 38 -> 31. 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
|
||
|
|
32591b6690 |
refactor(quick-260921-m34): Aufgabe 3a - 18 Fehlerfaenger auf unknown, mit echter Eingrenzung
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
|
||
|
|
52668c2c88 |
refactor(quick-260921-m34): Aufgabe 2c - Hochladewege getypt, 25 Zusicherungen fallen mit
@UploadedFile()/@UploadedFiles() in cert-manager.controller auf UploadedFileLike. Der Dienst nimmt CertFileLike = Pick<UploadedFileLike, 'buffer' | 'originalname'> - genau die zwei Felder, die er liest; mimetype und size bleiben draussen, weil kein Zweig sie anfasst. Belegt statt behauptet: keiner der sechs FileInterceptor/FilesInterceptor- Aufrufe in apps/api/src setzt eine storage-Option, also gilt multers memoryStorage, also ist buffer ein Buffer. @types/multer bleibt uninstalliert (D-04). Damit fallen 25 Zusicherungen der Form file.buffer as Buffer und file.originalname as string ersatzlos weg - sie standen nur da, weil file ein any war. as unknown as bleibt bei 33, noNonNullAssertion bei 56. noExplicitAny in apps/api/src: 66 -> 56 (Ausgang der Aufgabe: 149, Schranke des Plans: 75). type-check 4/4, lint 5/5, apps/api 72/1143, apps/web 73/531. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
7c9d7c1223 |
refactor(quick-260921-m34): Aufgabe 2b - getypte Anfrage in elf Controllern, zwei Befunde gemeldet
(req as any) und @Req() req: any durch AuthenticatedRequest ersetzt in
dashboard, favorites, calendar, groups, module-grants, module-registry,
tenders, dkv, ldap, settings; @CurrentUser() in user.controller auf AuthUser.
Die abwehrenden Pruefungen ("No tenant context", "No user context") bleiben
lebendig, weil user auf dem Anfragetyp wahlfrei ist - genau das beschreibt
den Zustand auf oeffentlichen Wegen.
Nebengewinn ohne neue Zusicherungen: req.tenantId as string | undefined
(dkv, settings), file.buffer as Buffer und file.mimetype as string
(dkv, user) sind weggefallen, weil der Typ sie jetzt traegt.
BEFUND 1 (D-03, gemeldet) dashboard.controller.ts:74 alt: der Handler las
req.user?.role NACH extractContext und gab sie an getWidgets(role: Role)
weiter, das eine Rolle zwingend verlangt. Die Annahme "hier gibt es immer
einen Aufrufer" stimmt - die Pruefung "No user context" erzwingt sie -, aber
sie stand in einer anderen Methode, wo der Compiler sie nicht sehen konnte.
extractContext gibt die Rolle jetzt mit zurueck: keine neue Pruefung, kein
erfundener Wert, gleiche Reihenfolge, gleiche Meldungen.
BEFUND 2 (D-03, gemeldet) tenders.controller.ts:142: resolveRequestingTenantId
erklaerte string | undefined, liest aber req.tenantId, das TenantGuard fuer
einen SUPER_ADMIN ohne Mandanten auf null setzt. Die Erklaerung war also nie
vollstaendig. Erweitert auf string | null | undefined, und buildTenderWhere
nimmt string | null - beides nur Erklaerung, kein Verhalten: die Funktion
entscheidet seit jeher ueber Wahrheitswert und faellt bei beiden zu
(nur global sichtbare Ausschreibungen).
Fixtures in user.controller.spec.ts ergaenzt (username, mustChangePassword,
originalname, size). Testzahlen unveraendert.
noExplicitAny in apps/api/src: 137 -> 66. type-check 4/4, lint 5/5,
apps/api 72/1143, apps/web 73/531, tenant.guard.ts unveraendert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
|
||
|
|
f2fc39f51c |
refactor(quick-260921-m34): Aufgabe 2a - gemeinsamer Aufrufer-Typ, aus den Signierstellen abgeleitet
apps/api/src/auth/types/auth-user.ts angelegt: AuthUser, AuthenticatedRequest, LocalAuthenticatedRequest, LoginUser, JwtPayload, UploadedFileLike. Jedes Feld traegt seine Herkunft als Kommentar. tenantId ist string, hergeleitet und nicht gewaehlt: die Spalte User.tenantId ist in schema.prisma Pflicht, beide Signierstellen schreiben genau sie, und der Bestand beschreibt dasselbe Objekt in SessionUser schon so. Der SUPER_ADMIN-Zweig in TenantGuard spricht nicht dagegen - der Waechter liest AuthUser gar nicht, und dass es den Zweig gibt, steht als null in AuthenticatedRequest.tenantId weiter im Typsystem. tenant.guard.ts bleibt unberuehrt. role ist die Aufzaehlung Role: schema.prisma deklariert die Spalte so, die SQL-Funktion auth_lookup_user_by_username gibt sie als "Role" zurueck. Die Handannotation role: string in AuthLookupUserByUsernameRow war eine zweite Fassung desselben Wertes und faellt damit weg. SessionUser und UploadedPng in bug-reports.service.ts sind jetzt Pick<> der neuen Typen statt eigener Beschreibungen. Fixtures in auth.controller.spec.ts ergaenzt: sie uebergaben einen Aufrufer ohne username und ohne mustChangePassword - eine Form, die JwtStrategy nie erzeugt. Testzahlen unveraendert. noExplicitAny in apps/api/src: 149 -> 137. type-check 4/4, lint 5/5, apps/api 72/1143, apps/web 73/531. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
b188946e31 |
refactor(quick-260921-m34): Aufgabe 1 - Mandantenbindung entzaubert, 105 unnoetige any-Zusicherungen entfernt
- 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
|
||
|
|
5a03b75f1e |
refactor(quick-260921-jt4): ueberfluessige case-Marke in tender-normalizer.service.ts entfernt (Restposten 2)
Die Fallmarke 'doe-opendata' stand unmittelbar ueber default und fiel in denselben Zweig — 260921-bi2 liess sie stehen, weil der Kommentar darunter Absicht dokumentiert. Die Absicht laesst sich ohne die Fallmarke ausdruecken und wird dabei deutlicher: der erweiterte Kommentar traegt jetzt beide Aussagen (DÖE-Quelle landet hier UND kuenftige additive SourceType-Mitglieder sollen ebenfalls hier landen statt zu scheitern). Kein Verhaltenswechsel — derselbe Zweig wie vorher. Belegt durch die vorhandenen 18 tender-normalizer-Tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
de6986340d |
fix(quick-260921-iwr): mergeCerts-Waechter loest keinen zusaetzlichen Lint-Fund mehr aus
Die urspruengliche findIndex((c) => c === null)-Pruefung erzeugte einen neuen lint/complexity/useIndexOf-Fund (info) und hob TOTAL dadurch auf 430 statt der erwarteten 429 an — die Endverifikation des Plans deckte das auf (D-06: TOTAL darf nirgends anders steigen). @types/node-forge deklariert Bag.cert als "Certificate | undefined", die node-forge-Laufzeit setzt bei einem unlesbaren Bag aber "null" (nicht undefined). Ein blosses indexOf(null) ist deshalb nicht typsicher; die Pruefung testet jetzt ausdruecklich auf beide Werte, was fuer biome kein Single-Value-Vergleich mehr ist und keinen useIndexOf-Vorschlag ausloest. TOTAL steht jetzt bei 429 wie geplant, NONNULL unveraendert bei 6, tsc und beide Testsuiten weiterhin gruen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
27909e4502 |
refactor(quick-260921-iwr): zwei ueberfluessige Ausrufezeichen in imap.provider.ts entfernt
imapflow typisiert uid in FetchMessageObject als Pflichtfeld (lib/imap-flow.d.ts Zeile 469, Kommentar "Always included in the response"). Die beiden Zusicherungen msg.uid! sicherten also einen Wert ab, der ohnehin nicht fehlen kann. Reine Lesbarkeitsaenderung ohne Verhaltensaenderung, tsc bleibt gruen. Die drei verbliebenen Zusicherungen (tenders.controller.ts:244, favorites-widget.tsx:149, sidebar.tsx:80) bleiben unveraendert — jede durch eine konkrete vorgelagerte Zeile garantiert (Provider-Eintrag, gemeinsame Herleitung aus favorites, Anlegen des Map-Eintrags direkt davor). NONNULL 8 -> 6, keines davon mehr in imap.provider.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
b4aaed4b7c |
fix(quick-260921-iwr): drei bag.cert-Zusicherungen in cert-manager.service.ts zu echten Waechtern gemacht
node-forge setzt bag.cert bei einem wohlgeformten, aber nicht als X.509
lesbaren Zertifikats-Bag auf null (lib/pkcs12.js Zeile 703-709). Die drei
Zusicherungen in parseCert, mergeCerts und convertCert behaupteten also
etwas Falsches, obwohl die Folge bereits behandelt war: alle vier Pfade
antworteten schon vorher mit 400, nie mit 500 (mit einer selbst gebauten
83-Byte-PFX nachgemessen).
Ersetzt die drei Zusicherungen durch ausdrueckliche Pruefungen mit
praeziser BadRequestException. In mergeCerts wird kein Zertifikat mehr
verschluckt: alle Bags werden auf Vollstaendigkeit geprueft, bevor die
Liste ueber ein Typpraedikat zurueckgegeben wird.
RED-Tests zuerst geschrieben und mit den heutigen Meldungen ("Failed to
extract certificate details" / "Failed to create merged certificate
output" / "Failed to convert certificate to pem: serialization error")
rot bestaetigt, dann die Waechter ergaenzt: GREEN.
Verhaltensaenderung ausdruecklich beabsichtigt (D-02): nur der Text der
Fehlermeldung fuer diese eine Eingabeklasse aendert sich, der Statuscode
bleibt in allen vier Pfaden 400.
NONNULL 11 -> 8, davon 3 in cert-manager.service.ts (Zeilen 133/516/661
bleiben unveraendert — durch Hash-Laenge bzw. vorgelagerte Passwort-
Pruefung garantiert).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
|
||
|
|
b92dd5dda1 |
fix(api,web): exec-Schleifen ohne Unterdrueckungskommentar umgeschrieben, LDAP-NUL-Maskierung und Sprachcookie-SameSite dokumentiert/gehaertet
Drei while ((m = re.exec(t)) !== null)-Schleifen (dkv-parser.service.ts, dkv-parser.validate.ts, icon-discovery.service.ts) sind die korrekte Standardform fuer globale Regexe - kein verrutschtes "=". Umgeschrieben auf eine verhaltensgleiche for-Schleife, die ohne noAssignInExpressions- Unterdrueckung auskommt: Zuweisung wandert in Initialisierung und Fortschaltung der for-Schleife, Bedingung prueft weiterhin auf null. Abfolge der exec-Aufrufe, lastIndex-Fortschritt und Rumpfinhalte unveraendert. Neue Spezifikation dkv-parser.service.spec.ts deckt parseDkvText erstmals eigenstaendig ab (zwei Fahrzeugbloecke, Rechnungsnummer und -datum aus einer gemockten pdf-parse-Attrappe) - das Rueckfall-Tor fuer diesen Umbau. dkv-parser.validate.ts bleibt bei 27 Fahrzeugbloecken/66 Transaktionen gegen die reale invoice.pdf identisch. LdapService.escapeLdapFilterValue bleibt zeichengleich: der NUL-Treffer in der Regel ist die von RFC 4515 vorgeschriebene \00-Maskierung, kein Fehler. Ein biome-ignore-Kommentar dokumentiert das, statt die Funktion zu aendern. locale-switcher.tsx setzt jetzt SameSite=Lax auf dem NEXT_LOCALE-Cookie - path=/ und max-age waren bereits korrekt, es lag also kein Persistenzdefekt vor. Ohne SameSite haengt die Uebertragung am Browservorgabewert statt an einer Festlegung. Neue Spezifikation locale-switcher.test.tsx haelt die vollstaendige geschriebene Cookie-Zeichenkette fest. Biome-Warnungen 446 -> 434 (noControlCharactersInRegex/useIterableCallbackReturn/ noGlobalIsNan auf 0, noAssignInExpressions auf 4 und noDocumentCookie auf 17 - beide Reste ausschliesslich in Testdateien, suppressions/unused auf 0). Quick-Vorgang 260921-i8x, Task 3/3. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
f7c02b7fc9 |
fix(api): erzwungenen Passwortwechsel an der API wirklich durchsetzen
JwtStrategy.validate liess mustChangePassword auf dem Weg vom Token zu request.user fallen; der global registrierte ForcePasswordChangeInterceptor prueft genau dieses Feld und hat seit seiner Einfuehrung nie etwas blockiert. validate() reicht das Feld jetzt durch (strenger Vergleich mit true, Alt-Sitzungen ohne den Anspruch bleiben unveraendert unbetroffen). Zusaetzlich die Erlaubnisliste des Abfangers von Teilstring-Vergleich auf exakten Abgleich von Methode UND Pfad umgestellt (Absicherung gegen eine kuenftige kollidierende Route, heute nicht ausnutzbar). Nahttest gepinnt, der gegen den alten Quelltext nachweislich scheitert (6 von 12 neuen Faellen rot vor der Aenderung, gruen danach). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
636fe0df8f |
refactor(quick-260921-bi2): maschinelle Lint-Fixe und toten Code abbauen
- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf apps/web+packages, noUselessEscapeInRegex, useConst, useExponentiationOperator) sowie fuenf ungesicherte Regeln (useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate, useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen (ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei fehlender Sitzung geprueft) - noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60, die Fallmarke dokumentiert Absicht) - Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts), fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten) - Sechs weitere, im Plan nicht namentlich gelistete aber gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich bereinigt (groups.service.spec.ts, cert-manager.test.tsx, ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/ noUnusedImports/noUnusedFunctionParameters zu erreichen Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...). Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten 621/542 — eine Differenz von 1, weil das Streichen des Namens aus `catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint --force 5/5. Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben): - force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad, nicht die HTTP-Methode - change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf der Seite stehen (keine Weiterleitung, keine Aktualisierung der Benutzerablage) - VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst sich doppelt ausloesen - SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J |
||
|
|
716947228e |
feat(bug-reports): Herkunft der Fehlermeldung im Betreff-Kuerzel und als Zeile Herkunft ausweisen
WebView2 (Windows) sieht im User-Agent aus wie Edge, WebKitGTK (Linux) wie Safari — im Postfach war eine Client-Meldung von einer Browser-Meldung nicht zu unterscheiden. Neuer reiner Helfer origin.ts leitet aus vier optionalen DTO-Feldern (Desktop-App) bzw. dem User-Agent (Browser) ein Betreff-Kuerzel und eine Zeile "Herkunft: ..." ab; rein informativ, laengenbegrenzt, nichts wird gespeichert (T-GZA-01). Browser-Pfad ist damit Ende-zu-Ende fertig. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016g2npLxzH5gZpg8s2S6vKh |
||
|
|
de81c74e53 |
feat(api): GET /desktop/update — signierte Desktop-Pakete im Format des Tauri-Updaters ausliefern
- Neuer oeffentlicher Endpunkt (vor download/:platform): base-Origin wird
per safeOrigin validiert (nur http/https, kein Pfad/Query/Fragment/
Userinfo, sonst 400) und nur zum Bau der absoluten Download-URL genutzt,
nie serverseitig abgerufen
- 204 ohne Body bei fremder Plattform/Architektur, fehlendem Manifest,
fehlender Signatur oder fehlendem/ungueltigem updateVersion; sonst
{ version, pub_date (nur RFC 3339), url, signature, notes }
- Manifest-Felder signature (je Plattform) und updateVersion optional in
@tessera/shared, Eintragspruefung akzeptiert nur String-Signaturen
- 10 neue Spec-Tests, Test 11 erweitert (23 gesamt); latest/download
unveraendert
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
b023d6f726 |
docs: Favoriten-Sortierung und Symbol-Ersatzweg im CHANGELOG und Anwenderhandbuch; Nachtraege zur Mandantenbindung
- CHANGELOG.md: je ein Stichpunkt unter Neu (Sortierpfeile) und Behoben (Symbol trotz Zertifikatsfehler/interner Adresse) - docs/anleitung-anwender.md: Tabellenzeile „Favoriten" nennt die Pfeile und den Browser-Ersatzweg fuer das Symbol - docs/mandantentrennung-zugriffsklassifikation.md: Nachtrag zu favoriteLink — reorder() laeuft ueber withTenantTransaction() ohne Benutzerdimension in der Sitzung, Stand bleibt gebunden - prisma-tenant.extension.ts: Kopfkommentar-Nachtrag, favorites.service.ts (reorder) ist der erste Nutzer-CRUD-Aufrufer von withTenantTransaction() — nur Kommentartext, Funktionscode unveraendert (prisma-tenant.extension.spec.ts und rls-access-inventory.spec.ts weiterhin gruen) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2a562d0b14 |
feat(api): Favoriten — Symbol trotz Zertifikatsfehler holen, Reihenfolge per PUT /favorites/order speichern
- icon-discovery.service.ts: undicis eigenes fetch mit Modul-Singleton
LENIENT_TLS_AGENT (Agent({ connect: { rejectUnauthorized: false } }))
als dispatcher in fetchWithRedirectGuard, der einzigen Ausgangsstelle
fuer HTML-Ermittlung und Icon-Byte-Holen; SSRF-Schutz unveraendert
- undici 7.28.0 (bereits im Lockfile aufgeloest) als direkte Abhaengigkeit
von @tessera/api via pnpm add --offline
- PUT /favorites/order (ReorderFavoritesDto) vor den :id-Routen;
FavoritesService.reorder() setzt position=index fuer die Favoriten
eines Widgets in EINER withTenantTransaction, userId+widgetId in jeder
Bedingung (zweites Netz), eine BadRequestException fuer alle
Abweichungen (T-JDD-06)
- getIcon: X-Content-Type-Options nosniff + restriktive CSP (T-JDD-02)
- 10 neue Tests (3 Dispatcher, 7 reorder); volle API-Suite 68 Dateien/
1101 Tests und type-check gruen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
0d5c80fbbf |
fix(18): dot-only Manifest-Dateinamen ("."/"..") in getPackage() ablehnen (CR-01)
Die Zeichen-Whitelist /^[A-Za-z0-9._-]+$/ liess Namen wie ".." durch, weil Punkt und Bindestrich erlaubte Zeichen sind -- path.join(desktopDistDir, '..') loest aber in den Elternordner auf und unterlaeuft genau die Verteidigung in der Tiefe (T-18-02), die diese Zeile laut Kommentar herstellen soll. Jetzt werden "." und ".." explizit abgelehnt UND der aufgeloeste Pfad zusaetzlich gegen desktopDistDir geprueft (haelt auch kuenftige Varianten ab, falls die Whitelist anderswo wiederverwendet wird). Neue Testfaelle fuer manifest-Eintraege namens "..", "." und "../manifest.json". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a8964f1a23 |
fix(18): Manifest-Eintraege in getManifest() auf gueltige Form pruefen (WR-03)
Ein kaputter Plattform-Eintrag (z. B. fehlendes name-Feld oder ungueltiger sha256) fiel bisher erst spaeter unbemerkt durch -- entry.name === undefined wurde zu "undefined" gecoerct und als Dateiname gesucht. getManifest() prueft jetzt jeden vorhandenen Plattform-Eintrag (name: string, size: number, sha256: 64-stelliger Hex-String) und behandelt ein kaputtes Manifest wie ein fehlendes (404 + Warn-Log), statt die kaputte Form durchzureichen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ae8fecb538 |
feat(18-01): Task 1 — Linux-Paket bis zum Download aus der API (Durchstich)
Die duenne Strecke Skript -> Abbild -> API beweisen (D-08, D-10): - desktop-collect.sh sammelt AppImage/exe ein, schreibt manifest.json (Version, Kanal, Commit, Groesse, SHA-256) ohne Secrets - apps/api/src/desktop/: neues Modul mit GET /desktop/latest und GET /desktop/download/:platform, beide @Public(); Plattform-Whitelist vor jedem Dateisystemzugriff, Dateiname ausschliesslich aus dem Manifest (T-18-01, T-18-02) - packages/shared: DesktopPlatform/-Manifest(File)/-Latest(Response) Typen - Dockerfile kopiert desktop-dist/ in die runner-Stufe; desktop-dist/ per .gitkeep + .gitignore versioniert (leeres Verzeichnis, Pakete bleiben ungetrackt) - HTTP-Durchstich-Spec (8 Tests) via NestFactory, kein fs-Mock; echtes Temp-Verzeichnis + unabhaengig berechneter SHA-256 Abweichung: DesktopController braucht @Inject(DesktopService) explizit — Vitest transpiliert ueber esbuild, das emitDecoratorMetadata nicht abbildet, sonst bleibt desktopService bei einem echten NestFactory-Bau undefined (Rule 3, Blocker). Lokal bewiesen: neu gebautes API-Abbild liefert /desktop/latest (200) und /desktop/download/linux (200, attachment) aus, auch ueber /api-proxy/ des Web-Containers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
39ea1474a5 |
feat: Link-Widget restlos entfernt (Web, API, Migration, Handbuch, Changelog)
- Web: Union-Mitglied, Constraints, Icon, Registry-Eintrag, wire-Funktion, Katalog, Seiten-Verdrahtung, Tests und i18n (de/en) fuer den Typ link entfernt; link-widget.tsx/.test.tsx geloescht - API: create-widget.dto.ts (@IsIn-Liste) und widget-module-map.ts (Kommentare) auf sieben Typen angepasst; schema.prisma unveraendert - Neue Migration 20260916120000_remove_link_widget: eine idempotente DELETE-Anweisung auf WidgetInstance, FavoriteLink kaskadiert ueber den bestehenden FK; wird in diesem Auftrag NICHT ausgefuehrt - Neuer widget-wrapper-Test belegt den grauen Fallback fuer unbekannte Widget-Typen (Bestandsverhalten, jetzt festgeschrieben) - Handbuch (Widget-Tabelle, Einstellungs-Hinweise) und CHANGELOG (Geaendert/Entfernt/Behoben) aktualisiert - Web: 52 Dateien / 344 Tests gruen, tsc Exit 0; API: tsc Exit 0, dashboard-Spec 31 Tests gruen Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3f5afb0f54 |
feat(quick-260916-bwo): Dashboard-Raster verdoppelt (24 Spalten, 20 px, 8 px Abstand), Konstanten x2, einmalige Umrechnung gespeicherter Anordnungen mit Marker __gridVersion
- dashboard-grid.tsx: COLS 24/20/12/8/2, rowHeight 20, margin 8 (containerPadding folgt), Rueckfallwerte 4 - widget-registry.tsx: alle 32 Werte in WIDGET_CONSTRAINTS verdoppelt - grid-layout-migration.ts (neu): migrateGridLayouts/withGridVersion, Marker nur im JSON, Idempotenz (T-BWO-02) - dashboard-store.ts: Umrechnung beim Laden, Sofort-Speichern mit Marker, withGridVersion bei jedem saveLayout - Tests: Migration 7 (neu), Store 6 (neu), Registry +1 (Tabelle), Grid +2 (Props ueber Mock), API-Spec +2 (Durchreichung __gridVersion, timeFontSizePt) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY |
||
|
|
54121c1721 |
feat(quick-260914-m97): Fehlermeldungen per E-Mail — Empfaenger in SmtpConfig (Migration), MailService-Anhaenge, Modul bug-reports mit Drossel, PNG-Pruefung und Mandant aus der Sitzung
- SmtpConfig.bugReportRecipient (nullable, additive Migration 20260914170000), DTO @IsOptional @IsEmail, SAFE_SELECT, getBugReportRecipient gebunden - MailService: Versandkern deliver (wirft, Anhaenge), sendViaTenantTransport bleibt verschluckender Mantel (T-02-12), sendBugReport laesst Fehler durch - POST /bug-reports: Multipart 4 MiB je Route, alle angemeldeten Rollen, Drossel 5/10 min -> 429, PNG-Signatur -> 400, kein Empfaenger -> 409, Versandfehler -> 502, eine Protokollzeile - Falsifizierungen (a)-(d) als Specs; @Expose() im DTO, damit errors auch bei fehlendem Feld zu [] wird - Doku-Zeile fuer rls-access-inventory, TESSERA_BUGREPORT_TO in docker-compose.prod.yml Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY |
||
|
|
cdb571c509 |
feat(quick-260914-ku1): Versionsstempel — GET /health/version aus APP_*, VersionResponse, app-version.ts und Abzeichen v<Version> · <Kanal> in der Seitenleiste
- packages/shared: VersionResponse { name, version, channel, commit, buildTime }
- apps/api: health/app-version.ts (getAppVersion mit ||-Vorgaben dev/dev, formatAppVersionLine),
HealthController.getVersion delegiert (bleibt @Public, T-KU1-03), main.ts protokolliert
"Tessera API <version> (<channel>) <commit>" beim Start; neuer Spec mit 6 Tests
- apps/web: lib/app-version.ts (NEXT_PUBLIC_APP_* mit vollem Literalnamen, loadApiVersion
memoisiert und still bei Fehler), AppVersionBadge unten in der Seitenleiste (auch mobil,
nicht eingeklappt), sidebar.channel.{beta,live,dev} in de/en; 5 + 4 + 1 neue Tests
- Suiten: API 65 Dateien / 1060 Tests, Web 40 / 243, tsc in shared/api/web Exit 0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
|
||
|
|
6e2a641d76 |
feat(quick-260914-eym): Mail-Transport je Versand nach Mandant (WINDOWS #30), ldap/digest/matching ueber Systemkontext, vier Tabellen im Werkzeug, Erlaubnisliste vollstaendig
- mail: MailerModule-Fabrik und DB-Startpfad (findFirst beim Boot) ersatzlos entfernt; MailService baut je Versand einen nodemailer-Transport aus getDecryptedSmtpConfig(tenantId) des Empfaenger-Mandanten, Umgebungs-Kette (MAIL_* -> TESSERA_SMTP_* -> localhost:1025) nur als Rueckfall; Fehler weiter verschluckt (T-02-12), close() im finally; neue mail.service.spec.ts (4 Tests, T-GWH-03 geschlossen) - settings: Startpfad-Methode samt vier Spec-Tests geloescht; auth: requestPasswordReset reicht user.tenantId durch (Spec-Zusicherung) - ldap: getAllActiveConfigs und Nachverschluesselung lesen ueber forSystem (zwei Zuweisungen), Schreibzeile je Altzeile ueber forTenant(config.tenantId); Tests 301/306 umgedreht, neuer Altzeilen-Test - tender-digest: Kandidatenabfrage ueber forSystem, Schleife gebunden (+1 Test) - tender-matching: Profilabfrage ueber forSystem, Katalog (D-03) ungebunden (+1 Test) - tender-notifications.integration.spec: Mock um forSystem - Werkzeug: LdapConfig (15 Spalten), LdapFieldMapping (6), TenderMatch (8), TenderSavedSearch (8) je neun Kennungen plus Relations-Kennung ldapconfig-systemkontext-include-fieldmappings-beider-mandanten -> Alle 253 Pruefungen bestanden - Detektor: FORSYSTEM_ALLOWED_CALL_SITES auf 4 Dateien / 5 Aufrufe; Proben-Empfaenger sysPrisma (Gate-Zaehlung, Name nicht hartkodiert) - Klassifikation: 6 Zeilen system-gebunden, settings/smtpConfig gebunden - Falsifizierung durch Rueckbau ausgefuehrt und zurueckgenommen: (a) FOR SELECT bei TenderMatch entfernt -> 5 von 253 rot (Insert gelingt, cmd ALL); (b) Regel TenderSavedSearch aus der Datei entfernt -> 1 von 245 rot (Extraktion), lebende DB bleibt bei 34; (c) local=false -> gruen, plus Reset entfernt -> 5 rot (Erben sichtbar); (d) Zahl 0 -> 2 rot, Fremddatei admin-seed -> 3 rot - Baseline: 64 Dateien / 1054 Tests, tsc 0, Werkzeug 253 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY |
||
|
|
3d645674f0 |
feat(quick-260914-eym): forSystem(), is_system_context(), Systemleseregel auf fuenf Tabellen, DKV-Planer je Mandant — ein Pfad (WINDOWS #21)
- Helfer forSystem(prisma) in prisma-tenant.extension.ts (Array-Form, setzt app.system_context='true' und die beiden anderen Variablen ausdruecklich leer); forTenant()/withTenantTransaction() setzen app.system_context='' als Literal (4 neue Spec-Tests) - Migration 20260914120000_rls_system_context_read: is_system_context() (COALESCE, STABLE) und system_read_policy FOR SELECT auf DkvModuleConfig, LdapConfig, LdapFieldMapping, TenderMatch, TenderSavedSearch — lokal angewendet (36 Migrationen, pg_proc 1, 5 system_read_policy, 34 Regeln) - migration-sql.spec.ts: describe-Block fuer die neue Migration (6 Tests) - rls-scratch-check.mjs: Funktion aus der Migration geschnitten, forSystemQuery/buildInlineSystemClient, Reset in forTenantQuery/ buildInlineExtendedClient, runSystemContextChecks (4 Funktionsfaelle + 9 Kennungen DkvModuleConfig) -> Alle 216 Pruefungen bestanden - rls-access-inventory.spec.ts: fuenfte Erkennungsform const X = forSystem(, Stand system-gebunden mit Vorrangregel, FORSYSTEM_ALLOWED_CALL_SITES (exakte Zahl je Datei, 3 Tests), Proben C/D/E - DKV: loadActiveConfigsForScheduler() ueber forSystem (findMany isActive, CONFIG_SAFE_SELECT, orderBy tenantId); DkvSchedulerService mit Auftrag je Mandant dkv-inbox-poll:<tenantId>, activeTenantId ersatzlos entfernt, setInterval/stopJob je Mandant, registeredTenantIds(); Controller stopJob(tenantId); neue dkv-scheduler.service.spec.ts (7 Tests), dkv.service.spec.ts Tests 6/7 umgestellt - Klassifikation: dkv.service.ts/dkvModuleConfig system-gebunden, Header mit fuenfter Erkennungsform und viertem Stand-Wert - Baseline: 63 Dateien / 1051 Tests, tsc 0, Werkzeug 216 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY |
||
|
|
63f9df0afb |
docs(quick-260914-ebg): Kopfkommentar adminResetPassword — Schwesterwege PATCH/DELETE /users/:id geschlossen (WINDOWS #29)
- auth.service.ts: letzter Satz des Kopfkommentars ueber adminResetPassword nennt T-FH9-05 nicht mehr als offen, sondern verweist auf den seit 260914-ebg (WINDOWS #29) identischen Riegel in UserController.update()/remove() - Falsifizierung: Rueckbau des Task-1-Commits (git apply -R) macht Test 9 und Test 13 rot (Tests 2 failed | 14 passed (16)), danach byte-identisch wiederhergestellt (git checkout --, git status --porcelain leer) - Rule 1 Nebenfund: acht neue Tests in user.controller.spec.ts trugen sechs ueberfluessige `as any`-Umschreibungen (UpdateUserDto ist vollstaendig optional, siehe planning_measurements), die die Biome-Warnungen dieser Datei von 25 auf 31 trieben — entfernt, damit die relative Biome-Schwelle der Baseline (25) wieder eingehalten wird, ohne die Schwelle anzuheben Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY |
||
|
|
759ea3b2ca |
fix(quick-260914-ebg): Zielrollen-Riegel in UserController.update/remove — ADMIN kann SUPER_ADMIN nicht mehr aendern oder loeschen (WINDOWS #29)
- update(): Riegel nach der Mandantengrenze, vor der dto.role-Pruefung — Nicht-SUPER_ADMIN darf SUPER_ADMIN-Ziel nicht mehr aendern (Kennwort, isActive, Rolle, Anmeldename, E-Mail) - remove(): derselbe Riegel nach der Mandantengrenze, vor userService.delete - acht neue Tests (Test 9-16): drei Angriffsformen, SUPER_ADMIN-gegen-SUPER_ADMIN-Regression, ADMIN-gegen-USER-Regression, Reihenfolge-Ordnungstests je Handler - RED-Lauf vor dem Riegel: Tests 2 failed | 14 passed (16) (Test 9, Test 13 rot); GREEN danach: Tests 16 passed (16) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY |
||
|
|
07fc653f52 |
feat(quick-260911-nke): Benutzer an 34 Aufrufstellen gesetzt, zehn Tabellen gemessen, sechs Pruefungen umgedreht
- 30 verbleibende forTenant()-Aufrufstellen in sieben Diensten (calendar 6, dashboard 9, favorites 5, tender-email-config 3, tender-notification-pref 2, tender-rss-feed 2, tender-triage 3) reichen userId als drittes Argument durch. tender-digest.scheduler.ts bleibt zweistellig (Hintergrunddienst, Etappe 3c), mit Begruendung im Kommentar. Keine Methodensignatur, kein Controller angefasst, keine anwendungsseitige userId-Filterung entfernt. - rls-scratch-check.mjs: zwoelf Extraktionsstellen auf die neue Migration umgeleitet (TenderEmailConfig/TenderNotificationPref/TenderSavedSearch/ TenderTriage/TenderRssFeedSource in runTendersAreaChecks, SearchProvider in runSearchProviderAreaChecks/runDashboardAreaChecks, DashboardLayout/ WidgetInstance, CalendarSource/FavoriteLink samt regelstand-eindeutig-Gates). SearchProvider/TenderRssFeedSource jetzt mit extractAllPolicySql (4 Regeln). runUserDimensionChecks() um die uebrigen neun Tabellen erweitert (neue Routine runCommandSeparatedPersonalTableCheck fuer die zwei NULL-faehigen Tabellen inkl. gemeinsame-Zeile-Pruefungen). - Sechs Loch-Pruefungen umgedreht (dashboardlayout, widgetinstance, searchprovider, calendarsource, favoritelink-Doppelaussage getrennt) — alte Messung ohne Benutzer bleibt unter neuem Namen, Umkehrung MIT Benutzer erwartet das Gegenteil; kein alter Name mehr als Kennung. - Baseline: 1020/62 Tests weiterhin gruen, Typpruefung sauber, Werkzeug 203/203 bestanden (vorher 146). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
f0b531b712 |
feat(quick-260911-nke): current_user_id(), Benutzerdimension in den Regeln, forTenant() mit userId — ein Pfad
- Neue Migration 20260911120000_rls_user_dimension_personal_tables: current_user_id() (NULLIF-gefaltet), zehn persoenliche Tabellen umgestellt (acht als eine Regel, SearchProvider/TenderRssFeedSource als je vier befehlsgetrennte Regeln), vier Verwaltungstabellen bewusst unveraendert. Lokal angewendet (migrate deploy, Prisma-Binary aus apps/api/node_modules/.bin), schema.prisma unveraendert. - forTenant(prisma, tenantId, userId?): beide set_config in EINER getaggten Anweisung, $transaction-Array bleibt bei zwei Eintraegen (WINDOWS #20), Leerstring ohne Benutzer statt Weglassen. - tender-saved-search.service.ts: alle vier forTenant()-Aufrufe reichen userId durch; Detektor-Regex bestaetigt 4 Treffer. - rls-scratch-check.mjs: current_user_id() aus der neuen Migration geschnitten (nicht getippt), drei Funktionsfaelle gemessen, neue runUserDimensionChecks() mit generiertem Client fuer TenderSavedSearch (vier Wahrheiten + Spaltenabgleich), die alte Loch-Pruefung tendersavedsearch-fremder-nutzer-desselben-mandanten-sichtbar umgedreht (alte Messung unter neuem Namen erhalten, neue Umkehrung MIT Benutzer). sqlStateOf() um Message-Fallback ergaenzt (RLS-Ablehnung ueber generierten Client traegt den SQLSTATE nur im Fehlertext, nicht in .meta.code). - Baseline: 1020/62 Tests, Typpruefung sauber, Werkzeug 146/146 bestanden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
388690fdf0 |
docs(quick-260911-mkj): Abschnitte abgeleitet, WINDOWS #27 geschlossen, Restmenge als eigener Eintrag
- Klassifikationsdokument: Klassen-Verteilung (35/21/14/2, 72 Paare), Uebersichtsabsatz (zweite methodische Luecke, geschlossen) und Hintergrunddienst-Nachtrag (ldap/getAllActiveConfigs reicht auch in LdapFieldMapping hinein) aus Tabelle/Greps abgeleitet, nicht abgeschrieben - Kritikschrift: Nachtrag (260911-mkj) unter (n4) im Bereich tenant, Vermerk im Etappe-2-Abschluss dass #27 geschlossen ist - WINDOWS.md: #27 fixed (Nachweis: vierte Erkennungsform, Proben, Zwischenmessung 7/3/1); neuer Eintrag #33 fuer die Empfaenger ausserhalb der vier Erkennungsformen (tenders.seed.ts, backfill-tender-source.ts) - Spec: WINDOWS #TBD-MKJ-Platzhalter durch #33 ersetzt Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
5ad23d0537 |
test(quick-260911-mkj): vierte Erkennungsform fuer Relationszugriffe, WINDOWS #27
- rls-access-inventory.spec.ts: analyzeFile in analyzeSource(source, relPath) herausgeloest, parseSchemaRelations() liest schema.prisma zur Testzeit, vierte Erkennung loest include:/select:/_count:/Relationsfilter ueber SCHEMA_RELATIONS auf das Zielmodell auf und traegt es als eigene Fundstelle (gebunden/ungebunden nach Empfaenger) ein - drei Waechter (Rohzahl vs. erkannte Modellaufrufe, Stale-Check fuer RELATION_SPEC_EXCEPTIONS, unresolvedRelationSpecValues leer), zwei Schema-Tests, acht gepinnte Proben (WINDOWS #27 ungebunden/gebunden, reale ldap-Form, verschachtelte where-Kette, Negativprobe, _count: true, unbekannter Empfaenger, Konstantenaufloesung) - Bestandsaufnahme (docs/mandantentrennung-zugriffsklassifikation.md): sieben neue Paare, drei fortgeschriebene Staende (davon ldapFieldMapping mit Klassenwechsel auf beides), Kopfabsatz "Erkennungsluecke GESCHLOSSEN" ersetzt den alten "seit 260911-e2s vermessen"-Absatz, 65 -> 72 Paare Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
12409322f5 |
docs(quick-260911-gwh): Etappe 2 auf Endstand bringen -- sechster Fall, Befund K erfuellt, Ledger, Anleitung
- .planning/WINDOWS.md: drei neue offene Eintraege (#30 Startpfad des Mailmoduls, #31 verschluckte Leere favorites, #32 verschluckte Leere settings); Platzhalter WINDOWS #TBD-GWH in settings.service.ts und mail.module.ts durch #30 ersetzt - docs/mandantentrennung-zugriffsklassifikation.md: Uebersichtszeilen favorites (0/8, war 7/0) und settings (1/3, war 4/0) neu gemessen; Summenzeile 68/178 (Endstand Etappe 2); Bestandsaufnahme (favoriteLink gebunden, smtpConfig gemischt, neue Zeile widgetInstance/gebunden); Klassen-Verteilung 65 Paare (33/17/13/2); Hintergrunddienst-Abschnitt mit sechstem Fall (mail.module.ts, WINDOWS #30) und erfuellter Befund-K-Bedingung; neuer Punkt in "Was diese Etappe NICHT entscheidet" - docs/mandantentrennung-etappe2-fehlerrichtung.md: Nachtraege unter Befund K in (t4) und im Uebergaben-Absatz von (d4) -- Reihenfolgebedingung erfuellt; ## Etappe 2 -- Abschluss mit den derivierten Endzahlen - docs/anleitung-entwicklung.md: 23 RLS-Tabellen statt sieben, FavoriteLink nicht mehr als Tabelle ohne Regel, tenantPrisma statt manuellem tenantId-Filter als gelebter Stil - rls-access-inventory.spec.ts wieder gruen (11/11), volle Suite 994/994, Werkzeug 137/137; zwei Dokument-Falsifizierungen durchgefuehrt und zurueckgenommen (siehe SUMMARY) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
b5f22e2c4a |
feat(260911-gwh): GREEN — favorites/settings binden, Startpfad umbenennen, Widget-Besitzriegel
- favorites.service.ts: alle fuenf Methoden nehmen tenantId als ersten Parameter, laufen je ueber EINEN Klienten tenantPrisma (7 gebundene Favoritenzugriffe, 5 Aufrufstellen); create() prueft vor der Icon-Suche, dass das Ziel-Widget dem Aufrufer gehoert (T-GWH-05, Befund F aus Aufgabe 1 bestaetigt den Fremdschluessel-Durchgriff) -- Widget not found fuer alle drei Faelle (existiert nicht/Kollege/fremder Mandant) - favorites.controller.ts: reicht tenantId an alle fuenf Aufrufe durch, extractContext unveraendert (dashboard-Praezedenzfall) - settings.service.ts: getSmtpConfig/saveSmtpConfig/getDecryptedSmtpConfig je EIN Klient (3 gebundene Zugriffe, 3 Aufrufstellen) -- Befund K damit erfuellt; Startpfad umbenannt in loadAnySmtpConfigForStartupTransport(), bleibt bewusst ungebunden (sechster Fall der Hintergrunddienst-Falle, WINDOWS #TBD-GWH -- Aufgabe 3 vergibt die Nummer) - mail.module.ts: ruft den umbenannten Startpfad auf, Kommentar nennt beide Zustaende statt "single-tenant default" - Vier Falsifizierungsnachweise durchgefuehrt und zurueckgenommen (siehe SUMMARY): (a) 3 Faelle rot, (b) 4 Faelle rot, (c) 9 Faelle rot, (d) 4 Faelle rot Bekannt und erwartet (siehe SUMMARY, Praezedenzfall 260911-fh9): zwischen dieser Aufgabe und Aufgabe 3 ist rls-access-inventory.spec.ts rot (2 Faelle) -- die Klassifikationstabelle ist noch nicht nachgezogen, das ist Aufgabe 3. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
8f2c13a35f |
test(260911-gwh): RED — neue Testdateien fuer favorites/settings mit dem Zwei-Klienten-Nachbau
- favorites.service.spec.ts (NEU, 23 Faelle): ungebundener Nachbau hat KEIN favoriteLink/widgetInstance-Modell; erwartet die Zielsignatur list/create/update/remove/getIconBytes(tenantId, ...) und den Widget-Besitzriegel in create -- scheitert erwartungsgemaess an "Cannot read properties of undefined" gegen den heutigen Dienst (22/23 rot) - settings.service.spec.ts (NEU, 20 Faelle): ungebundener Nachbau bietet fuer smtpConfig NUR findFirst, gebundener Klient NUR findUnique/upsert; erwartet loadAnySmtpConfigForStartupTransport() (Startpfad-Umbenennung) und den Null-Klienten-Nachweis fuer den Startpfad -- 18/20 rot Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
f68beb379a |
docs(quick-260911-fh9): Mandantenquelle im auth-Controller festnageln, Klassifikation nachziehen, Ledger-Eintraege anlegen
- auth.controller.spec.ts: NEU. Mandantenquelle je Handler (das Claim fuer me/changePassword; fuer die oberste Rolle der Mandant des Ziels aus dem Fan-out), unbekanntes Ziel, null-Durchreichung von me, Rollen-Metadaten (ROLES_KEY) und Public-Metadaten (IS_PUBLIC_KEY) fuer alle sieben Handler — 23 Faelle; Falsifizierungsnachweis am Fan-out-Zweig durchgefuehrt und zurueckgenommen - docs/mandantentrennung-zugriffsklassifikation.md: Uebersichtszeile auth auf 3/10 (war 8/5), Summenzeile 78/167, Bestandsaufnahme-Zeile auth.service.ts/user auf gebunden (keine gemischt-Zeile mehr fuer diese Datei), Klassen-Verteilung unveraendert bei 64 Paaren mit Stand-Vermerk, Hintergrunddienst-Abschnitt ohne sechsten Fall, Etappe-3-Anmeldeweg-Punkt in "Was diese Etappe NICHT entscheidet"; beide Dokument-Falsifizierungen durchgefuehrt und zurueckgenommen - .planning/WINDOWS.md: zwei neue offene Eintraege (#28 verschluckte Leere im Frontend, Familie #23/#25/#26; #29 Rechteausweitung ADMIN->SUPER_ADMIN im Schwesterweg PATCH /users/:id, T-FH9-05) Baseline wiederhergestellt: 951/951 Tests gruen in 60 Dateien (927+23 neue Faelle plus der in Aufgabe 2 erwartungsgemaess rote Test), Werkzeug 120/120, Typpruefung sauber. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
92aa8c403b |
feat(260911-fh9): getMe/changePassword/adminResetPassword an den Mandanten aus dem Sitzungsnachweis binden
- auth.service.ts: die drei Nach-Anmeldungs-Methoden binden je ueber genau einen Klienten tenantPrisma; adminResetPassword verweigert einem Nicht-SUPER_ADMIN das Kennwort eines SUPER_ADMIN (T-FH9-04); die drei $queryRaw-Anmeldesuchen bleiben unveraendert auf dem ungebundenen Klienten - auth.controller.ts: me/changePassword reichen user.tenantId aus dem Claim durch; adminResetPassword verzweigt ueber resolveTargetTenantId nach Rolle (ADMIN: eigener Mandant; SUPER_ADMIN: gebundener Fan-out UserService.findByIdForPlatformAdmin) — schliesst die Rechteausweitung ueber die Mandantengrenze (T-FH9-01) - auth.module.ts: importiert UserModule, zyklusfrei gemessen - auth.service.spec.ts: Identitaets-Attrappe ersetzt durch zwei unterscheidbare Klienten (__makeBoundClient); 29 Faelle, drei Falsifizierungsnachweise durchgefuehrt und zurueckgenommen Bekannt und erwartet: rls-access-inventory.spec.ts ist nach diesem Commit kurzzeitig rot (Bestandsaufnahme-Zeile auth.service.ts/user zeigt noch "gemischt", gemessen ist jetzt "gebunden") — wird in Aufgabe 3 desselben Plans geschlossen (927/928 Tests gruen, ein bekannter, in Aufgabe 3 behobener Fehlschlag, kein neuer). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
c8de72e762 |
feat(260911-e2s): Benutzerzaehler im TenantController binden (Fan-out je Mandant)
findAll/findOne/remove zaehlen Benutzer je Mandant jetzt ueber drei
gebundene Aufrufstellen (tenantPrisma.user.count mit where: { tenantId
}, in remove zusaetzlich isActive: true) statt ueber den
Relationszaehler, der nach dem Scharfschalten unbemerkt unter der
Regel von User gelaufen waere (260911-e2s, Aufgabe 1, Pruefungen 5-7).
Fan-out-Muster aus UserService.findAllForPlatformAdmin uebernommen; die
vier tenant-Zugriffe bleiben ungebunden (Tenant ohne Regel). Antwortform,
Meldungen und Statuscodes unveraendert.
tenant.controller.spec.ts legt die Testlage aus dem Nichts an (20
Faelle): Zwei-Klienten-Nachweis ueber __makeBoundClient, Rollen-
Metadaten-Test (Klasse SUPER_ADMIN, kein Handler ueberschreibt), Wachhund
gegen mehrfache Klientenerzeugung. Falsifizierungsnachweis durchgefuehrt:
der probeweise ungebundene Zaehler in findOne macht 2 Faelle rot mit
"Cannot read properties of undefined (reading 'count')" — die dkv-Form
der Falsifizierung, nicht nur eine falsche Zahl —, danach zurueckgenommen.
Klassifikation und Entwicklungsanleitung nachgezogen: 64 Paare (ein
neues, tenant.controller.ts/user), Uebersichtszeile 8/3, Klassen-
Verteilung 32 muss-mandantengebunden, Erkennungsluecke fuer
Relationseinbindungen im Kopf der Bestandsaufnahme benannt, "Zwei
belegte Befunde" und "Was diese Etappe NICHT entscheidet" (erster
Punkt aufgeloest). Beide Dokument-Falsifizierungsnachweise durchgefuehrt
(falsche Klasse macht rls-access-inventory.spec.ts rot, falsche
Uebersichtszahl macht das herleitende Gate rot), zurueckgenommen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
|
||
|
|
17dca0dfad |
fix(260911-e2s): Guard-Umbau und Kommentarkorrekturen nachtragen (Aufgabe 2 vollstaendig)
Die vorige Aufgabe-2-Teilcommit (
|
||
|
|
11f5731029 |
feat(260911-e2s): TenantGuard setzt nur noch tenantId, Middleware geloescht
Die seit Etappe 1 offene Architekturfrage zum gebundenen Klienten auf dem Anfrageobjekt ist entschieden: neun umgestellte Bereiche binden ausnahmslos dienst-intern (ein Klient je Methode), ein Klient auf req.tenantPrisma ohne Leser war tote Verdrahtung, die wie ein Sicherheitsmechanismus aussah. tenant.guard.ts verliert die Prisma-Abhaengigkeit und setzt nur noch req.tenantId; die nie verdrahtete tenant.middleware.ts (identische Logik, in keinem Modul registriert) ist geloescht. tenant.guard.spec.ts legt die Testlage aus dem Nichts an — alle fuenf Zweige (kein Nutzer, USER, ADMIN mit ignorierter x-tenant-id-Kopfzeile T-04-03, SUPER_ADMIN mit/ohne Wechsel, mandantenloser Nicht-SUPER_ADMIN) sowie die Abwesenheit der alten Eigenschaft in jedem Durchlass-Fall. Falsifizierungsnachweis durchgefuehrt: das probeweise Wiedereinfuehren der alten Zuweisung macht 4 der 7 Faelle rot (u. a. "expected true to be false" auf 'tenantPrisma' in req), danach zurueckgenommen. rls-access-inventory.spec.ts: FORTENANT_ASSIGNMENT_EXCEPTIONS ist leer und selbstpruefend (neuer Wachhund gegen veraltete Eintraege). Drei Fremdkommentare (app.module.ts, module.guard.ts, dkv.controller.ts) korrigiert, die noch auf die nie verdrahtete Middleware verwiesen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
77cb124f59 |
feat(quick-260911-cwh): Bereich calendar binden — alle 12 Zugriffe ueber forTenant() (Aufgabe 2)
- calendar.service.spec.ts: NEU, Zwei-Klienten-Nachweis ueber __makeBoundClient
(Muster dkv.service.spec.ts), Attrappen fuer CryptoService und die drei
Provider, 23 Testfaelle: getSources/addSource, updateSource inkl. drei
Erhaltungsfaelle, alle drei Besitzpruefungen je Ausnahmeart, testConnection
Erfolgs-/Fehlerpfad, aggregateEvents/fetchAndCacheEvents inkl. beider
Synchronstatus-Rueckschreibungen, Cache-Verhalten inkl. Nutzer-Trennung,
testConnectionFromConfig ohne DB-Zugriff, Wachhund fuer genau einen
gebundenen Klienten je Aufruf
- calendar.service.ts: alle 12 Zugriffe auf forTenant() umgestellt, ein
tenantPrisma-Klient je Methode (getSources, addSource, updateSource,
deleteSource, testConnection, fetchAndCacheEvents); Cache-Schluessel-Urteil
und die kein-sechster-Hintergrunddienst-Begruendung als Kommentare
festgehalten
- calendar.controller.ts: alle sechs kontextnutzenden Handler reichen
Benutzer- UND Mandantenkennung aus extractContext durch, keine neue
Vertrauensquelle
- Falsifizierungsnachweis durchgefuehrt: eine Rueckschreibung testweise
entbunden, benannter Test ging rot ("aggregateEvents, Erfolgspfad"), Fund
bestaetigt, zurueckgenommen
- docs/mandantentrennung-zugriffsklassifikation.md: Bestandsaufnahme-Zeile
calendar.service.ts/calendarSource auf gebunden gezogen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
|
||
|
|
6e7120648b | fix(quick-260910-krx): Konfliktklasse ueber den generierten Client messen statt sie zu behaupten | ||
|
|
67b50240d6 |
feat(quick-260910-krx): Suchmaschinen gebunden, Katalog begruendet offen, Klassifikation nachgezogen
Aufgabe 3 — TDD zuerst (7 weitere Faelle in dashboard.service.spec.ts, 20 vorher/27 nach dieser Aufgabe), dann die Umstellung: - getSearchProviders/addSearchProvider/removeSearchProvider laufen ueber forTenant(); removeSearchProvider fuehrt Besitzpruefung UND Schreibzugriff ueber DENSELBEN gebundenen Klienten. Die drei Vorgabe-Suchmaschinen aus der Konstante bleiben unveraendert vorangestellt. - Der eine Katalogzugriff (this.prisma.module in getWidgets) bleibt begruendet ungebunden: Messung und Bedingung getrennt (Tabelle traegt heute keinen Zeilenschutz, wirkungslos statt katastrophal — katastrophal erst, wenn Etappe 3 eine Regel gibt), unter Berufung auf die bestehende Werkzeugpruefung module-tabelle-traegt-keinen-zeilenschutz statt einer neuen Behauptung. Ein Wachhund-Testfall haelt den Katalogzugriff aus dem Bindungsprotokoll heraus (und beweist zuerst, dass der Katalogpfad tatsaechlich durchlaufen wird, nicht nur theoretisch geprueft ist). - docs/mandantentrennung-zugriffsklassifikation.md an allen fuenf handgepflegten Stellen nachgezogen: vier Bestandsaufnahme-Zeilen (inkl. eigenstaendiger Nachpruefung der widerlegten SearchProvider-Praemisse), Uebersichtszeile (13/0 -> 1/12), Summenzeile (95/147), Klassen-Verteilung (unveraendert 63 Paare, ausdruecklich vermerkt), Hintergrunddienst- Abschnitt (dashboard hat keinen sechsten Fall, mit Messanweisung), "Was diese Etappe NICHT entscheidet" (dienst-interner forTenant()-Weg wie alle sieben Bereiche vor ihm). - .planning/WINDOWS.md traegt Eintrag #25 (offen, Tabelle + JSON): die beweisvernichtende Schleife (leeres Dashboard -> Neuaufbau -> automatisches Zurueckschreiben -> ueberschriebene Anordnung, Widget- Dubletten) samt der Vorabpruefung fuer Etappe 4 und dem Verweis auf #22 fuer die verwandte Eindeutigkeitsfrage. Zwei weitere Falsifizierungsnachweise durchgefuehrt: (1) den Katalogzugriff probeweise gebunden (tenantPrisma.module.findMany) — acht Tests werden rot mit "TypeError: Cannot read properties of undefined (reading 'findMany')", weil `module` bewusst nicht in der Testdouble-Bindungsliste steht; Rueckbau zurueckgenommen, 27/27 wieder gruen. (2) den Stand von dashboardLayout in der Klassifikationsdatei probeweise auf "ungebunden" gesetzt — rls-access-inventory.spec.ts wird rot mit "Abweichender Stand (Dokument vs. Quelltext): ... dokumentiert=ungebunden, gemessen=gebunden"; Ruecknahme, Testlauf wieder gruen (10/10). Baseline gehalten: 858 Tests / 56 Dateien gruen, Typpruefung sauber, Wegwerf-Werkzeug 87/87. Schalter bleibt aus. |
||
|
|
e0ce594c5a |
feat(quick-260910-krx): Anordnung und Widgets an forTenant() gebunden
Aufgabe 2 — TDD zuerst (20 Faelle in dashboard.service.spec.ts, 8 vorher/12 neu, Zwei-Klienten-Nachweis ueber __makeBoundClient nach dem Muster von module-access.service.spec.ts), dann die Umstellung: - getLayout/saveLayout laufen GEMEINSAM gebunden (ein Testfall nagelt das fest); saveLayout uebersetzt eine gebundene Konflikt-Schreibung (PrismaClientUnknownRequestError, gemessen in Aufgabe 1 — NICHT P2002) in eine deutsche ConflictException. - getWidgets/addWidget/updateWidgetConfig/removeWidget laufen gebunden; die drei Besitzpruefungen ueber die Benutzerkennung bleiben unveraendert bestehen (die Regeln dieses Bereichs kennen keine Benutzerdimension). updateWidgetConfig/removeWidget fuehren Besitzpruefung UND Schreibzugriff ueber DENSELBEN gebundenen Klienten. - dashboard.controller.ts reicht den bereits aufgeloesten Mandanten bei getLayout/updateWidgetConfig/removeWidget durch (keine neue Vertrauensquelle, weiterhin aus extractContext/Sitzungsnachweis). - Der Modulkatalog und die vier Suchmaschinenzugriffe bleiben in dieser Aufgabe unveraendert (Aufgabe 3). - Falsifizierungsnachweis durchgefuehrt: tenantPrisma.widgetInstance.delete probeweise auf this.prisma zurueckgebaut — Test "Widget entfernen: ebenso, beide Abfragen ueber denselben Klienten" wird rot mit "erwarteter gebundener Aufruf widgetInstance.delete(tenant=tenant-1) fehlt im Protokoll"; Rueckbau zurueckgenommen, Testlauf wieder gruen (20/20). Zwei dokumentierte Abweichungen (Rule 3): (1) Befund A hatte fuer widgetInstance sieben Treffer vorhergesagt, gemessen sind sechs (macht zusammen mit dashboardLayout acht statt neun) — der Verify-Schwellwert wird entsprechend auf >=8 gelesen. (2) Die Stand-Spalte fuer dashboardLayout/widgetInstance in der Klassifikationsdatei wird bereits hier minimal nachgezogen (nicht erst in Aufgabe 3), weil rls-access-inventory.spec.ts sonst am Ende dieser Aufgabe rot waere — derselbe Praezedenzfall wie 260910-exd, Aufgabe 2. Baseline gehalten: 851 Tests / 56 Dateien gruen (839 + 12 neue), Typpruefung sauber, Wegwerf-Werkzeug 87/87. |
||
|
|
6b237351e9 |
feat(quick-260910-jab): listForUser binden, die vier Aufzeichnungen im Quelltext richtigstellen
- 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 |
||
|
|
f4f3115d5a |
feat(quick-260910-jab): drei zu kurz greifende RLS-Regeln schliessen (T-JTS-02, T-JTS-03, WINDOWS #19)
- Neue, handgeschriebene Migration 20260910120000_rls_widen_membership_grant_and_platform_read: GroupMembership prueft jetzt beide Seiten der Beziehung (Gruppe UND Benutzer), ModuleGrant prueft zusaetzlich die referenzierte Gruppe/den referenzierten Benutzer (mit Leer-Zulassung, D-04), TenderRssFeedSource bekommt vier nach Befehl getrennte Regeln statt einer (Lesen schliesst plattformweite Zeilen ein, Schreiben verlangt weiterhin einen Mandanten). SearchProvider bewusst unveraendert (Befund E: Praemisse widerlegt). Lokal angewandt und gegen den Systemkatalog der lebenden Datenbank gemessen. Der Schalter bleibt aus (Rolle tessera). - rls-scratch-check.mjs: die drei loch-behauptenden Pruefungen umgekehrt (nicht geloescht), Gegenmessungen ueber die Wartungsrolle ergaenzt, vier Befehlsrichtungen fuer TenderRssFeedSource gemessen, neuer Abschnitt fuer SearchProvider, Extraktion auf die neue Migration umgeleitet und um eine mehrfach-treffer-faehige Form ergaenzt (extractAllPolicySql). - migration-sql.spec.ts: neuer Beschreibungsblock fuer die neue Migration. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
9c0eefee90 |
feat(quick-260910-exd): ModuleRegistryService binden, Klassifikation abschliessen
- module-registry.service.ts: findActiveForTenant, activateForTenant, isModuleActive binden je einen Aktivierungszugriff, deactivateForTenant bindet beide (Lesen+Schreiben) ueber EINEN Klienten unter tenantPrisma; alle sechs Katalogzugriffe (findAll/findBySlug/beide Existenzpruefungen/isModuleActive-Katalogsuche/seedModule) bleiben bewusst ungebunden, mit Kommentar der Messung von Bedingung trennt - isModuleActive-Kopfkommentar richtiggestellt: der Waechter ruft sie nicht auf (0 Aufrufer, TEIL 3 von Aufgabe 1) — Waechter nimmt findBySlug + ModuleAccessService.getAccessibleModuleIds - module-registry.service.spec.ts: NEU, Zwei-Klienten-Nachweis, deckt die bislang ungetestete Datei mit elf der siebzehn Zugriffe des Bereichs ab, inkl. der lauten (deactivate ohne Aktivierung) und stillen (isModuleActive ohne Aktivierung) Richtung und dem Katalog-Wachhund - tender-scheduler.service.spec.ts: forTenant() auf Identitaet gemockt (dieselbe Konvention wie ldap.service.spec.ts) — cross-area Bruch durch die Umstellung von activateForTenant behoben (Rule 1/3) - docs/mandantentrennung-zugriffsklassifikation.md: alle fuenf handgepflegten Stellen nachgezogen (Bestandsaufnahme, Uebersichtszeile 7/10, Summenzeile 108/134, Klassen-Verteilung unveraendert bei 63 Paaren, Hintergrunddienst-Abschnitt haelt die Abwesenheit eines sechsten Falls fest) — alle gemessen, nicht abgeschrieben, Befund K haelt exakt - docs/mandantentrennung-etappe2-fehlerrichtung.md: Nachtrag mit tatsaechlich umgesetzten Pfaden, beiden Falsifizierungsnachweisen (Testname+Meldung), und der Feststellung zum unveraenderten Controller-Kommentar - .planning/WINDOWS.md: neuer offener Eintrag #23 (deviation) — kein Signal unterscheidet "keine Freigabe" von "Abfrage fand nichts", mit Vorabpruefung fuer Etappe 4 und begruendeter Verwerfung einer Laufzeitwarnung - 833 Tests gruen (56 Dateien), Typpruefung sauber, Wegwerf-Werkzeug 66/66 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |
||
|
|
3df72687c1 |
feat(quick-260910-exd): ModuleAccessService an forTenant() binden
- module-access.service.ts: getAccessibleModuleIds (Kurzschlusszweig,
Direktweg, Gruppenweg, Schnittmenge) und getCatalogFlags' eigener
Aktivierungs-Lesezugriff laufen ueber forTenant(), EIN Klient je Methode
unter dem Namen tenantPrisma; der Katalogzugriff in findAccessibleModules
bleibt bewusst ungebunden (Modulkatalog traegt keine Regel), mit Kommentar
der Messung und Bedingung trennt
- Bestehende where-Filter mit tenantId bleiben als zweites Netz stehen
- module-access.service.spec.ts: Zwei-Klienten-Nachweis ueber
__makeBoundClient (Muster aus module-grants.service.spec.ts), alle 15
bestehenden Faelle erhalten, neue Faelle fuer jede in <behavior> genannte
Bindungseigenschaft inkl. Wachhund gegen eine kuenftige Katalogbindung
- module.guard.spec.ts: ein Fall, der die Abwesenheit eines
unterscheidenden Signals fuer "keine Freigabe" vs. "Abfrage fand nichts"
festnagelt
- Falsifizierungsnachweis durchgefuehrt: Gruppenweg-Bindung probeweise
zurueckgebaut, Test "USER-Zweig bindet BEIDE Freigabe-Lesezugriffe..."
wurde rot ("expected 1 to be 2"), Ruecknahme bestaetigt wieder gruen
- mandantentrennung-zugriffsklassifikation.md: Stand fuer
module-access.service.ts/moduleGrant und /tenantModuleActivation auf
gebunden nachgezogen (Rule 3 — sonst waere rls-access-inventory.spec.ts
rot geblieben); die uebrigen vier Bestandsaufnahme-Stellen bleiben
Aufgabe 3 vorbehalten
- 817 Tests gruen (55 Dateien), Typpruefung sauber, Wegwerf-Werkzeug 66/66
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
|
||
|
|
3a9391d9c8 |
feat(quick-260910-das): Steuerungsschicht binden, Selbstloesch-Riegel schliessen
- user.controller.ts: alle sieben Zugriffe binden. ADMIN-Zweig der Benutzerliste laeuft ueber forTenant() mit weiterhin bestehender Mandantenbedingung im where; SUPER_ADMIN-Zweig ueber die neue UserService.findAllForPlatformAdmin(). Die drei Wege ueber die Kennung loesen den Zielbenutzer rollenabhaengig ueber resolveTargetUser() auf (ADMIN gebunden an eigenen Mandanten, SUPER_ADMIN uebergreifend); der Schreibzugriff bei update/delete bindet an den Mandanten des Zielbenutzers, nicht des Aufrufers, damit die uebergreifende Verwaltung durch die oberste Rolle erhalten bleibt - Selbstloesch-Riegel (Befund H) repariert: verglich bisher gegen currentUser.sub, ein Feld, das der Sitzungsnachweis nicht traegt -- der Riegel griff nie. Jetzt gegen currentUser.id. Verhaltensaenderung: ein Administrator kann sein eigenes Konto nun nicht mehr loeschen - Alle fuenf Selbstbedienungszugriffe (Bild hochladen/loeschen/ ausliefern, Akzentfarbe) binden an die Mandantenkennung aus dem Sitzungsnachweis - user.controller.spec.ts (neu): Zwei-Klienten-Nachweis fuer die vorher testlose Steuerungsschicht, 8 Testfaelle, Falsifizierungsnachweis fuer eine gebundene Stelle sowie Rot-vor-Reparatur-Nachweis fuer den Selbstloesch-Riegel (siehe SUMMARY) - docs/mandantentrennung-zugriffsklassifikation.md: alle vier handgepflegten Stellen nachgezogen (Uebersichtszeile 8/14, Summenzeile 118/124, Klassen-Verteilung 63 Paare, Hintergrunddienst-Abschnitt auf fuenf Faelle inkl. admin-seed.service.ts als erster beidseitig korrekter Fall) sowie zwei Klassenkorrekturen (user.service.ts/user und admin-seed.service.ts/user je auf "beides") - docs/mandantentrennung-etappe2-fehlerrichtung.md: Nachtrag zum user-Abschnitt mit den tatsaechlich umgesetzten Pfaden, der geschlossenen Luecke und den Falsifizierungsnachweisen - 810 Tests gruen (8 neue in user.controller.spec.ts), Typpruefung sauber, Wegwerf-Werkzeug meldet weiterhin alle 53 Pruefungen bestanden Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR |