docs(quick-260907-let): Plan fuer Postfach-Verbindungstest im Ausschreibungs-Radar
Schliesst WINDOWS #16. Zwei Tasks: Endpunkt POST email-config/test (Dienst + Route + Reihenfolge-Waechter) und Knopf 'Verbindung testen' im Formular unter Meine Quellen samt Beschriftungen in beiden Sprachen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
This commit is contained in:
+352
@@ -0,0 +1,352 @@
|
||||
---
|
||||
phase: quick-260907-let
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- apps/api/src/tenders/tender-email-config.service.ts
|
||||
- apps/api/src/tenders/tenders.controller.ts
|
||||
- apps/api/src/tenders/tender-email-config.service.spec.ts
|
||||
- apps/api/src/tenders/tenders.controller.spec.ts
|
||||
- apps/web/src/lib/tender-radar-api.ts
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.test.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx
|
||||
- apps/web/src/messages/de.json
|
||||
- apps/web/src/messages/en.json
|
||||
autonomous: true
|
||||
requirements: [WINDOWS-16]
|
||||
|
||||
estimate:
|
||||
tokens: 55000
|
||||
raw_tokens: 55000
|
||||
tasks: 2
|
||||
confidence: low
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Ein angemeldeter Nutzer kann auf der Seite Meine Quellen die Verbindung zu seinem Postfach pruefen, ohne vorher zu speichern, und sieht entweder eine Erfolgsmeldung oder den Klartext-Fehler des Mailservers."
|
||||
- "Ein bereits gespeichertes Postfach laesst sich erneut pruefen, ohne dass das Passwort neu eingetippt werden muss — der Server greift dann auf die verschluesselt hinterlegten Zugangsdaten des Nutzers zurueck."
|
||||
- "Der Test laeuft ausschliesslich gegen das Postfach des angemeldeten Nutzers; eine im Rumpf mitgeschickte fremde Kennung kann daran nichts aendern."
|
||||
- "Weder die Antwort des Servers noch seine Protokollzeilen enthalten Benutzername oder Passwort."
|
||||
- "Die neuen Beschriftungen liegen in beiden Sprachdateien vor (Deutsch und Englisch)."
|
||||
artifacts:
|
||||
- "apps/api/src/tenders/tender-email-config.service.ts — Methode testConnection"
|
||||
- "apps/api/src/tenders/tenders.controller.ts — Route POST email-config/test, deklariert vor @Get(':id')"
|
||||
- "apps/web/src/lib/tender-radar-api.ts — Funktion testEmailConnection"
|
||||
- "apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx — Knopf plus Rueckmeldung"
|
||||
- "apps/web/src/messages/de.json und apps/web/src/messages/en.json — Schluessel unter tenderRadar.emailAlerts"
|
||||
- "apps/api/src/tenders/tenders.controller.spec.ts — Deklarationsreihenfolge-Waechter um testEmailConnection erweitert"
|
||||
key_links:
|
||||
- "Controller-Handler -> extractTriageContext(req).userId -> Service-Aufruf (userId kommt nie aus dem Rumpf)"
|
||||
- "Service -> ImapProvider.testConnection bzw. ExchangeInboxProvider.testConnection, ausgewaehlt nach dto.protocol"
|
||||
- "Leeres Passwort im Rumpf -> entschluesselte encryptedInboxCreds derselben userId-Zeile"
|
||||
- "EmailAlertConfigForm -> testEmailConnection -> POST /modules/tender-radar/email-config/test"
|
||||
- "vi.mock-Fabriken fuer @/lib/tender-radar-api in EmailAlertConfigForm.test.tsx UND my-sources.test.tsx muessen testEmailConnection auffuehren — sonst wirft Vitest beim Import der Komponente"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Der Ausschreibungs-Radar bekommt fuer das Postfach unter "Meine Quellen" einen
|
||||
Verbindungstest — genau den, den das DKV-Modul seit Phase 7 hat. Schliesst
|
||||
WINDOWS-Eintrag #16.
|
||||
|
||||
Purpose: Heute faellt ein Tippfehler in der EWS-Endpunkt-URL oder ein falsches
|
||||
Passwort erst dadurch auf, dass dauerhaft nichts ankommt — und dann ist nicht
|
||||
unterscheidbar, ob die Verbindung scheitert oder schlicht keine Alarm-Mail da
|
||||
war. Das blockiert unmittelbar den offenen Live-Test WINDOWS #12, dessen ganzer
|
||||
Zweck der Beleg des handgeschriebenen NTLM/SOAP-Wegs ist.
|
||||
|
||||
Output: Ein per-Nutzer-Endpunkt POST /modules/tender-radar/email-config/test,
|
||||
ein Knopf "Verbindung testen" im Formular, Beschriftungen in beiden
|
||||
Sprachdateien, und ein erweiterter Deklarationsreihenfolge-Waechter.
|
||||
|
||||
Die Faehigkeit selbst wird NICHT neu gebaut: beide Inbox-Provider bringen
|
||||
testConnection() bereits mit (apps/api/src/inbox/imap.provider.ts:343,
|
||||
apps/api/src/inbox/exchange-inbox.provider.ts:294, deklariert in
|
||||
apps/api/src/inbox/inbox-provider.interface.ts:31). Dieser Plan verdrahtet sie
|
||||
nur — das Vorbild ist DkvController.testConnection (dkv.controller.ts:106) und
|
||||
DkvService.testConnection (dkv.service.ts:201).
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.claude/gsd-core/workflows/execute-plan.md
|
||||
@~/.claude/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/STATE.md
|
||||
@CLAUDE.md
|
||||
|
||||
Vorbild Backend (gemessen, nicht aus Notizen):
|
||||
@apps/api/src/dkv/dkv.service.ts
|
||||
@apps/api/src/tenders/tender-email-config.service.ts
|
||||
@apps/api/src/tenders/tenders.controller.ts
|
||||
|
||||
Vorbild Frontend:
|
||||
@apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx
|
||||
@apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx
|
||||
@apps/web/src/lib/tender-radar-api.ts
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="tracer" tdd="true">
|
||||
<name>Task 1: Endpunkt POST email-config/test — vom Formularrumpf bis zum Mailserver</name>
|
||||
<files>apps/api/src/tenders/tender-email-config.service.ts, apps/api/src/tenders/tenders.controller.ts, apps/api/src/tenders/tender-email-config.service.spec.ts, apps/api/src/tenders/tenders.controller.spec.ts</files>
|
||||
<read_first>
|
||||
apps/api/src/dkv/dkv.service.ts (Methode testConnection, Zeilen 195-235) — die Vorlage inklusive Rueckfall auf die gespeicherten Zugangsdaten bei leerem Passwort.
|
||||
apps/api/src/tenders/tender-email-config.service.ts (Methode saveConfig) — dort steht die bereits erprobte Entschluessel-und-Ergaenzen-Logik dieses Dienstes.
|
||||
apps/api/src/tenders/tenders.controller.ts Zeilen 328-365 (die beiden email-config-Handler) und Zeile 582 (@Get(':id')).
|
||||
apps/api/src/tenders/tenders.controller.spec.ts Zeilen 91-93 (makeFakeRequest), 132-140 (makeFakeEmailConfigService), 411-423 (der bestehende email-config-Reihenfolgetest).
|
||||
</read_first>
|
||||
<behavior>
|
||||
- TenderEmailConfigService.testConnection(userId, dto) mit gefuelltem Passwort im Rumpf: reicht genau dieses Passwort an den Provider durch, liest die Datenbank gar nicht erst.
|
||||
- Mit leerem Passwort im Rumpf und vorhandener gespeicherter Zeile: entschluesselt encryptedInboxCreds derselben userId und uebergibt das gespeicherte Passwort an den Provider.
|
||||
- Mit leerem Benutzernamen im Rumpf: ergaenzt analog den gespeicherten Benutzernamen (gleiche Semantik wie saveConfig in derselben Datei).
|
||||
- dto.protocol 'exchange' waehlt den Exchange-Provider, alles andere den IMAP-Provider.
|
||||
- Die Rueckgabe ist unveraendert das, was der Provider liefert: { success: true } oder { success: false, message }. Kein Feld mehr, kein Feld weniger.
|
||||
- TendersController.testEmailConnection nimmt userId aus extractTriageContext(req) und reicht es als erstes Argument weiter; ein im Rumpf mitgeschicktes Fremdfeld erreicht den Dienst nicht.
|
||||
- Der bestehende Reihenfolgetest fuer email-config schlaegt fehl, sobald testEmailConnection nach getTender deklariert wird.
|
||||
</behavior>
|
||||
<action>
|
||||
Schritt 1 — Dienst. In apps/api/src/tenders/tender-email-config.service.ts eine
|
||||
Methode testConnection(userId: string, dto: TenderEmailConfigDto) ergaenzen, die
|
||||
{ success: boolean; message?: string } liefert. Sie baut ein InboxConfig-Objekt
|
||||
(Typ aus '../inbox/inbox-provider.interface') aus den DTO-Feldern protocol, host,
|
||||
port, username, password, encryption, folder, senderFilter, domain — mit denselben
|
||||
Ersatzwerten wie DkvService.testConnection (host '' , port 993, folder 'INBOX').
|
||||
Fehlen username oder password im DTO, wird die Zeile der uebergebenen userId gelesen
|
||||
und encryptedInboxCreds entschluesselt; nur der jeweils fehlende Wert wird daraus
|
||||
ergaenzt. Diese Rueckfall-Logik ist strukturell dieselbe, die saveConfig in derselben
|
||||
Datei schon fuehrt — dort abschauen, nicht neu erfinden. Schlaegt das Entschluesseln
|
||||
fehl, wird der Fehler still verschluckt und ohne Zugangsdaten weiterprobiert; eine
|
||||
etwaige Protokollzeile nennt nur die userId, niemals einen Wert aus dem
|
||||
Zugangsdatensatz (T-05-13, T-QT16-03). Anschliessend
|
||||
protocol === 'exchange' ? exchangeProvider : imapProvider aufrufen und dessen
|
||||
Ergebnis unveraendert zurueckgeben.
|
||||
|
||||
Die beiden Provider werden per Konstruktor injiziert. Sie MUESSEN als optionale
|
||||
Parameter ANGEHAENGT werden (ImapProvider, ExchangeInboxProvider aus
|
||||
'../inbox/imap.provider' bzw. '../inbox/exchange-inbox.provider') — genau nach dem
|
||||
in diesem Repository dokumentierten Muster von TendersController.tenderIngestionService
|
||||
(tenders.controller.ts Zeilen 95-103): so bleiben die acht bestehenden
|
||||
new TenderEmailConfigService(prisma, crypto)-Aufrufe in der Spec ohne Aenderung
|
||||
typkorrekt, waehrend Nest im Betrieb immer beide aufloest (InboxModule ist in
|
||||
tenders.module.ts bereits importiert, ImapProvider und ExchangeInboxProvider werden
|
||||
dort exportiert — nichts an der Modulverdrahtung ist anzufassen). Ist der benoetigte
|
||||
Provider wider Erwarten nicht vorhanden, liefert testConnection
|
||||
{ success: false, message: 'Inbox-Anbieter nicht verfuegbar' } statt zu werfen, damit
|
||||
die Antwortform der Route in jedem Fall stabil bleibt.
|
||||
|
||||
Schritt 2 — Route. In apps/api/src/tenders/tenders.controller.ts unmittelbar nach
|
||||
saveEmailConfig (also klar VOR @Get(':id') in Zeile 582) einen Handler
|
||||
testEmailConnection mit @Post('email-config/test') und @UseModule('tender-radar')
|
||||
ergaenzen. Er liest const { userId } = this.extractTriageContext(req) und gibt
|
||||
this.tenderEmailConfig.testConnection(userId, dto) zurueck. Der Konstruktor der
|
||||
Klasse bleibt unangetastet — tenderEmailConfig ist bereits injiziert; das ist der
|
||||
Grund, warum die Logik in den Dienst und nicht in den Controller gehoert (42
|
||||
Konstruktoraufrufe in der Spec wuerden sonst brechen).
|
||||
|
||||
Den Handler mit einem Kommentarblock im Stil der Nachbarhandler versehen. Er soll
|
||||
zwei Dinge festhalten: erstens, dass userId ausschliesslich aus dem
|
||||
Authentifizierungskontext stammt und der DTO gar kein Kennungsfeld traegt (T-17-01 /
|
||||
T-14-03-05, IDOR); zweitens, warum der Handler trotzdem oberhalb von @Get(':id')
|
||||
steht, obwohl Nest Routen je HTTP-Verb aufloest und ein GET-Platzhalter eine
|
||||
POST-Route heute nicht verdecken kann — der ganze email-config-Block bleibt beisammen,
|
||||
und der Waechter greift auch dann noch, wenn spaeter einmal ein Platzhalter fuer POST
|
||||
hinzukaeme. Diese Einordnung bitte woertlich so treffen, nicht als schlichtes
|
||||
"sonst 404".
|
||||
|
||||
Schritt 3 — Tests. In apps/api/src/tenders/tender-email-config.service.spec.ts drei
|
||||
Faelle ergaenzen, die den bestehenden Fabriken (makeFakePrisma, makeFakeCrypto)
|
||||
folgen und zusaetzlich zwei winzige Provider-Attrappen mit einer testConnection-Zusage
|
||||
uebergeben: (a) getipptes Passwort geht unveraendert an den Provider,
|
||||
(b) leeres Passwort holt das gespeicherte, (c) protocol 'exchange' waehlt die
|
||||
Exchange-Attrappe. In apps/api/src/tenders/tenders.controller.spec.ts erstens
|
||||
makeFakeEmailConfigService um eine testConnection-Attrappe erweitern, zweitens einen
|
||||
Fall ergaenzen, der mit makeFakeRequest('u1', ...) belegt, dass der Dienst mit 'u1'
|
||||
aufgerufen wird, auch wenn der Rumpf ein abweichendes Kennungsfeld mitfuehrt, und
|
||||
drittens den vorhandenen Reihenfolgetest in Zeile 411 um den Index von
|
||||
testEmailConnection erweitern (kleiner als der Index von getTender).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd apps/api && npx vitest run src/tenders/tender-email-config.service.spec.ts src/tenders/tenders.controller.spec.ts</automated>
|
||||
<automated>cd apps/api && npx tsc --noEmit -p tsconfig.json</automated>
|
||||
<automated>grep -n "email-config/test" apps/api/src/tenders/tenders.controller.ts</automated>
|
||||
</verify>
|
||||
<done>
|
||||
Beide Vitest-Dateien laufen gruen (Ausgangsstand: 8 + 52 Faelle, jetzt mehr), der
|
||||
API-Typecheck bleibt fehlerfrei, und die Route POST email-config/test steht in
|
||||
tenders.controller.ts an einer Zeilennummer unterhalb von saveEmailConfig und
|
||||
oberhalb von @Get(':id').
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Knopf "Verbindung testen" im Postfach-Formular samt Beschriftungen</name>
|
||||
<files>apps/web/src/lib/tender-radar-api.ts, apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json, apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx</files>
|
||||
<read_first>
|
||||
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx Zeilen 140-205 (Zustandsfelder, update, handleTestConnection) und 520-580 (Knopfreihe und Rueckmeldung) — das nachzubildende Verhalten.
|
||||
apps/web/src/lib/dkv-api.ts Zeilen 125-141 (testConnection) und apps/web/src/lib/tender-radar-api.ts Zeilen 518-571 (fetchEmailConfig/saveEmailConfig samt extractErrorMessage) — die Hausform des jeweiligen Moduls.
|
||||
apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx Zeilen 14-22 — die vi.mock-Fabrik, die zwingend mitwachsen muss.
|
||||
</read_first>
|
||||
<behavior>
|
||||
- Klick auf den Knopf ruft testEmailConnection genau einmal mit demselben Rumpf auf, den auch das Speichern erzeugt (formToPayload), also ohne Passwortfeld, solange keins getippt wurde.
|
||||
- Waehrend der Anfrage tragen Test- und Speicherknopf den disabled-Zustand, und der Testknopf zeigt die Wartebeschriftung.
|
||||
- Eine Antwort mit success false zeigt die Fehlermeldung des Servers sichtbar an.
|
||||
- Eine Aenderung an irgendeinem Formularfeld raeumt die vorherige Rueckmeldung wieder weg.
|
||||
- Die bestehenden fuenf Faelle in EmailAlertConfigForm.test.tsx bleiben unveraendert gruen.
|
||||
</behavior>
|
||||
<action>
|
||||
Schritt 1 — Sprachdateien zuerst, damit die Komponente auf vorhandene Schluessel
|
||||
zugreift. Unter tenderRadar.emailAlerts in apps/web/src/messages/de.json und
|
||||
apps/web/src/messages/en.json jeweils vier Schluessel ergaenzen: testConnection,
|
||||
testTesting, testSuccess und testFailed. Wortlaut und Platzhalter woertlich von
|
||||
dkvFleet.form uebernehmen, wo dieselben vier Schluessel bereits stehen — deutsch
|
||||
"Verbindung testen" / "Verbindung wird getestet..." / "Verbindung erfolgreich" /
|
||||
"Verbindung fehlgeschlagen: {error}", englisch entsprechend. Deutsch ist die
|
||||
Hauptsprache dieser Installation, aber beide Dateien muessen den Satz tragen.
|
||||
Die 401-Hinweisliste des DKV-Formulars NICHT mitnehmen: sie gehoert dort zu einem
|
||||
eigenen Hilfetext-Block, den dieses Formular nicht fuehrt.
|
||||
|
||||
Schritt 2 — Api-Klient. In apps/web/src/lib/tender-radar-api.ts eine exportierte
|
||||
Funktion testEmailConnection ergaenzen, die denselben Nutzlast-Typ wie
|
||||
saveEmailConfig entgegennimmt und Promise<{ success: boolean; message?: string }>
|
||||
liefert. Sie sendet POST an ${API_URL}/modules/tender-radar/email-config/test mit
|
||||
'Content-Type': 'application/json' und credentials: 'include'. Bei nicht-ok-Antwort
|
||||
wird extractErrorMessage genutzt — so machen es die beiden Nachbarfunktionen in
|
||||
derselben Datei; die knappere Fehlerbehandlung aus dkv-api.ts hier bewusst nicht
|
||||
uebernehmen.
|
||||
|
||||
Schritt 3 — Formular. In EmailAlertConfigForm.tsx testEmailConnection importieren,
|
||||
die Zustandsfelder isTesting und testResult ergaenzen (Form wie in InboxConfigForm),
|
||||
in update() zusaetzlich setTestResult(null) aufrufen und einen Handler
|
||||
handleTestConnection schreiben, der formToPayload(form) sendet und Erfolg wie
|
||||
Fehlschlag in testResult ablegt; ein geworfener Fehler landet als
|
||||
{ success: false, message } dort. In der Knopfreihe am Ende (heute nur "Speichern")
|
||||
den Testknopf VOR dem Speicherknopf einsetzen, mit der Rahmen-Optik des DKV-Vorbilds,
|
||||
disabled bei isTesting || isSaving; der Speicherknopf bekommt zusaetzlich isTesting
|
||||
in seine disabled-Bedingung. Unter der Knopfreihe die Rueckmeldung rendern, gruen bei
|
||||
Erfolg und in der Warnfarbe bei Fehlschlag, mit denselben Farbwerten, die das
|
||||
Formular bereits fuer saveSuccess und saveError verwendet.
|
||||
|
||||
Der Beschreibungsblock ueber der Komponente (heute Zeilen 119-135) behauptet
|
||||
ausdruecklich, dieses Formular fuehre keinen solchen Knopf und es gebe serverseitig
|
||||
nichts, wogegen er pruefen koennte. Diese Passage ist ab jetzt falsch und muss
|
||||
umgeschrieben werden: der Knopf ist da, er spricht die neue Route an, und die
|
||||
Abrufhaeufigkeit bleibt weiterhin plattformweit geregelt (D-15) — nur Letzteres bleibt
|
||||
als Begruendung stehen, warum es hier kein Intervallfeld gibt.
|
||||
|
||||
Schritt 4 — Attrappen nachziehen. Beide Testdateien, die
|
||||
'@/lib/tender-radar-api' per vi.mock mit einer Fabrik ersetzen, muessen
|
||||
testEmailConnection mitfuehren: EmailAlertConfigForm.test.tsx (Zeilen 8-11) und
|
||||
my-sources.test.tsx (Zeilen 14-22). Fehlt der Eintrag, wirft Vitest bereits beim
|
||||
Import der Komponente, weil der Named Export auf der Attrappe nicht existiert — das
|
||||
ist der wahrscheinlichste Weg, diesen Task rot zu machen. In
|
||||
EmailAlertConfigForm.test.tsx zusaetzlich die vier neuen Uebersetzungen in die
|
||||
hand gepflegte Schluesseltabelle des next-intl-Mocks aufnehmen und zwei Faelle
|
||||
ergaenzen: (a) Klick auf den Testknopf ruft testEmailConnection einmal mit einem
|
||||
Rumpf ohne Passwortfeld auf, (b) eine Antwort { success: false, message: '401 ...' }
|
||||
laesst die Fehlermeldung im Dokument erscheinen.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd apps/web && npx vitest run "src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.test.tsx" "src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx"</automated>
|
||||
<automated>cd apps/web && npx tsc --noEmit</automated>
|
||||
<automated>node -e "const de=require('./apps/web/src/messages/de.json'),en=require('./apps/web/src/messages/en.json');const k=['testConnection','testTesting','testSuccess','testFailed'];for(const key of k){if(!de.tenderRadar.emailAlerts[key])throw new Error('de fehlt '+key);if(!en.tenderRadar.emailAlerts[key])throw new Error('en fehlt '+key);}console.log('locale keys ok');"</automated>
|
||||
<human-check>
|
||||
Abnahme im Browser — der Test beweist sich nur an einem echten Mailserver, nie
|
||||
an Attrappen. Der Neubau und Neustart der Container liegt beim Nutzer; dieser
|
||||
Plan fasst weder Docker noch den Testserver an.
|
||||
|
||||
1. Portal oeffnen, Ausschreibungs-Radar, Seite "Meine Quellen", Abschnitt
|
||||
"Mein Postfach". Unterhalb der Felder steht jetzt neben "Speichern" ein
|
||||
zweiter Knopf "Verbindung testen".
|
||||
2. Absichtlich falsch pruefen: einen unsinnigen Servernamen eintragen (zum
|
||||
Beispiel gibtesnicht.example) und den Testknopf druecken. Erwartung: kurz
|
||||
"Verbindung wird getestet...", danach in Rot "Verbindung fehlgeschlagen:"
|
||||
gefolgt vom Klartext des Mailservers.
|
||||
3. Richtig pruefen: echte Postfachdaten samt Passwort eintragen und erneut
|
||||
druecken. Erwartung: gruene Zeile "Verbindung erfolgreich".
|
||||
4. Ohne Passwort nachpruefen: speichern, Seite neu laden (das Passwortfeld ist
|
||||
danach absichtlich leer) und nur den Testknopf druecken. Erwartung: wieder
|
||||
"Verbindung erfolgreich" — der Server hat auf die gespeicherten Zugangsdaten
|
||||
zurueckgegriffen. Das ist der Punkt, der den Test im Alltag brauchbar macht.
|
||||
5. Kurzer Blick in die API-Protokollzeilen des Laufs: weder Benutzername noch
|
||||
Passwort duerfen dort auftauchen.
|
||||
|
||||
Punkt 2 und 3 muessen sich unterscheiden — eine gruene Meldung bei absichtlich
|
||||
falschem Server waere ein Fehlalarm und schlimmer als gar kein Knopf. Faellt
|
||||
einer der Punkte durch, bleibt WINDOWS #16 offen; die gruenen Unit-Tests taugen
|
||||
dann nicht als Gegenargument, sie pruefen ausschliesslich gegen Attrappen.
|
||||
</human-check>
|
||||
</verify>
|
||||
<done>
|
||||
Beide Web-Testdateien laufen gruen (Ausgangsstand EmailAlertConfigForm: 5 Faelle,
|
||||
jetzt 7), der Web-Typecheck bleibt fehlerfrei, und der Sprachdatei-Check meldet
|
||||
"locale keys ok" fuer beide Sprachen. Die Browser-Abnahme oben wandert in die
|
||||
Abnahmeliste am Phasenende (human_verify_mode = end-of-phase).
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Browser -> API (POST /modules/tender-radar/email-config/test) | Nutzereingaben aus dem Formular, inklusive frei waehlbarem Servernamen und Zugangsdaten |
|
||||
| API -> Mailserver (IMAP/EWS) | Ausgehende Verbindung mit entschluesselten Zugangsdaten |
|
||||
| API -> Datenbank (encryptedInboxCreds) | Ruhende, verschluesselte Zugangsdaten eines einzelnen Nutzers |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-QT16-01 | Elevation of Privilege | TendersController.testEmailConnection | high | mitigate | userId stammt ausschliesslich aus extractTriageContext(req); TenderEmailConfigDto traegt gar kein Kennungsfeld, ein mitgeschicktes Fremdfeld erreicht den Dienst nicht (T-17-01 / T-14-03-05). Belegt durch den Controller-Testfall aus Task 1, Schritt 3 |
|
||||
| T-QT16-02 | Information Disclosure | Antwort und Protokoll des Tests | high | mitigate | Rueckgabe ist unveraendert das { success, message? } des Providers — kein Durchreichen des Konfigurationsobjekts. Der Dienst protokolliert im Fehlerfall hoechstens die userId; die Provider-Protokollzeilen sind seit T-07-03 bereits zugangsdatenfrei |
|
||||
| T-QT16-03 | Information Disclosure | entschluesselte gespeicherte Zugangsdaten | medium | mitigate | Entschluesselte Werte existieren nur im Methoden-Scope von testConnection, wandern ausschliesslich in das InboxConfig-Objekt und werden weder zurueckgegeben noch protokolliert (T-05-13) |
|
||||
| T-QT16-04 | Spoofing | frei waehlbarer host / EWS-Endpunkt im Testrumpf | medium | accept | Identische Oberflaeche und identisches Risiko wie das bereits bestehende PUT /email-config, das denselben Wert entgegennimmt und dauerhaft speichert. Der Test fuegt keine neue Angriffsflaeche hinzu; das Modul-Gate @UseModule('tender-radar') bleibt unveraendert davor |
|
||||
| T-QT16-05 | Denial of Service | wiederholtes Ausloesen des Tests | low | accept | Der Test bindet eine ausgehende Verbindung pro Klick, genau wie POST /dkv/test-connection seit Phase 7. Kein neues Muster, keine eigene Drosselung — dieselbe Bewertung wie beim Vorbild |
|
||||
|
||||
Keine neuen Pakete: dieser Plan installiert nichts ueber npm, das
|
||||
Legitimitaets-Gate fuer Paketinstallationen ist daher nicht anwendbar.
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Nach Abschluss beider Tasks, aus dem Repository-Wurzelverzeichnis:
|
||||
|
||||
```
|
||||
cd apps/api && npx vitest run && npx tsc --noEmit -p tsconfig.json
|
||||
cd apps/web && npx vitest run && npx tsc --noEmit
|
||||
```
|
||||
|
||||
Gemessener Ausgangsstand vor Beginn dieses Plans: beide Typechecks fehlerfrei
|
||||
(Exit 0), apps/api/src/tenders/tender-email-config.service.spec.ts 8 Faelle
|
||||
gruen, apps/api/src/tenders/tenders.controller.spec.ts 52 Faelle gruen,
|
||||
EmailAlertConfigForm.test.tsx 5 Faelle gruen. Die Zahlen duerfen nur steigen.
|
||||
|
||||
Der Reihenfolge-Waechter ist die dokumentierte Ausnahme von der
|
||||
Reparatur-auf-Zuruf-Regel dieses Repositories (Entscheidung vom 2026-08-11):
|
||||
er muss laut werden, wenn jemand die Handler spaeter umsortiert.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- POST /modules/tender-radar/email-config/test existiert, ist mit
|
||||
@UseModule('tender-radar') abgesichert und liefert { success, message? }.
|
||||
- Die Kennung des Postfachbesitzers stammt nachweislich aus dem
|
||||
Authentifizierungskontext, nicht aus dem Rumpf — durch einen Testfall belegt.
|
||||
- Ein leer gelassenes Passwortfeld greift auf die gespeicherten,
|
||||
verschluesselten Zugangsdaten desselben Nutzers zurueck.
|
||||
- Der Knopf "Verbindung testen" steht im Postfach-Formular unter "Meine
|
||||
Quellen", zeigt einen Wartezustand und eine sichtbare Rueckmeldung.
|
||||
- Beide Sprachdateien tragen die vier neuen Schluessel.
|
||||
- Der Deklarationsreihenfolge-Waechter deckt testEmailConnection mit ab.
|
||||
- Alle vier Prueflaeufe (zwei Testsuiten, zwei Typechecks) bleiben gruen.
|
||||
- Die Browser-Abnahme aus dem human-check-Block von Task 2 ist bestaetigt (sie laeuft am Phasenende, nicht mitten im Lauf).
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/quick/260907-let-verbindungstest-fuer-das-postfach-im-aus/260907-let-SUMMARY.md` when done
|
||||
</output>
|
||||
Reference in New Issue
Block a user