feat(quick-260907-let): Verbindungstest fuer Postfach-Endpunkt im API

- TenderEmailConfigService.testConnection(userId, dto) mit Rueckfall auf
  gespeicherte, entschluesselte Zugangsdaten bei leeren Feldern
- TendersController: POST email-config/test, userId aus Auth-Kontext,
  deklariert vor @Get(':id')
- Beide Provider (ImapProvider/ExchangeInboxProvider) optional angehaengt,
  bestehende 2-Arg-Konstruktoraufrufe bleiben typkorrekt
- Reihenfolge-Waechter und IDOR-Testfall ergaenzt

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
This commit is contained in:
2026-09-07 15:40:28 +02:00
parent 5da58ad182
commit 3bf550bc65
4 changed files with 219 additions and 2 deletions
@@ -361,6 +361,31 @@ export class TendersController {
return this.tenderEmailConfig.saveConfig({ userId, tenantId }, dto);
}
/**
* POST /modules/tender-radar/email-config/test — tests the connection to
* the requesting USER's own portal-alert mailbox WITHOUT saving anything
* (Quick 260907-let, WINDOWS #16). `userId` comes exclusively from the
* auth context, exactly like `getEmailConfig`/`saveEmailConfig` above —
* `dto` carries no ownership field at all, so a body value under any key
* can never redirect the test at a different user's mailbox
* (T-14-03-05 / T-17-01 / T-QT16-01 — IDOR).
*
* Declared directly after `saveEmailConfig`, keeping the whole
* email-config block together, and — like every other handler in this
* block — placed before `@Get(':id')` below. NestJS actually resolves
* routes per HTTP verb, so a `GET :id` placeholder could never shadow
* this `POST` route today; the ordering here is defensive consistency
* with the surrounding email-config handlers (and the guard test below),
* not a live 404 risk, in case a `POST :id`-shaped placeholder is ever
* added to this controller in the future.
*/
@Post('email-config/test')
@UseModule('tender-radar')
async testEmailConnection(@Body() dto: TenderEmailConfigDto, @Req() req: Request) {
const { userId } = this.extractTriageContext(req);
return this.tenderEmailConfig.testConnection(userId, dto);
}
/**
* GET /modules/tender-radar/coverage — distribution of active tenders
* by sourcePortal (D-12, UI-05). Feeds the frontend CoverageBanner so a