Aller au contenu

PD-278 — Agent Developer Report: dip-controller

Module: dip-controller Agent: agent-developer Date: 2026-03-01 Story: PD-278 — NF Z42-013: ajout contractuel de l'etat DIP


1. Fichiers modifies

Fichier Action Description
src/modules/documents/controllers/dissemination.controller.ts CREE Controller REST DIP avec 2 endpoints POST
src/modules/documents/documents.module.ts MODIFIE Enregistrement controller + service + guard + filter + config + entity attestation

2. Implementation du controller

2.1 Endpoint F1 — POST /documents/disseminations (SEALED -> DIP)

POST /api/v1/documents/disseminations
Guards: JwtAuthGuard -> AuthorizationGuard(PA, SA, auditor) -> DisseminationRateLimitGuard
Body: CreateDisseminationDto { documents: UUID[], motif_communication?: string }
Response: 201 Created + DisseminationResponseDto

Invariants satisfaits: - INV-278-02: gardes chain JwtAuthGuard + AuthorizationGuard + DisseminationRateLimitGuard + gardes metier dans le service - INV-278-05: 1 attestation par requete (delegue au service) - INV-278-11: package atomique (delegue au service) - CA-04: actorId extrait de user.sub via @CurrentUser() (jamais du body) - CA-08: bornes documents[] 1..100 via class-validator dans CreateDisseminationDto

2.2 Endpoint F2 — POST /documents/:id/dissemination-return (DIP -> SEALED)

POST /api/v1/documents/{id}/dissemination-return
Guards: JwtAuthGuard -> AuthorizationGuard(PA, SA, auditor, retention_service)
Param: id validated by ParseUUIDPipe (E-400-ID-FORMAT)
Body: DisseminationReturnDto (empty)
Response: 200 OK + DisseminationReturnResponseDto

Invariants satisfaits: - INV-278-03: retour explicite uniquement, pas de timeout implicite - INV-278-13: returned_at >= disseminated_at (delegue au service via GREATEST) - INV-278-14: role retention_service autorise pour anti-contournement retention

2.3 Exception filter (INV-278-04)

Le controller est decore @UseFilters(DisseminationAuditExceptionFilter) au niveau classe. Cela capture les exceptions des guards decores via @UseGuards() car dans NestJS, les guards declares sur le controller sont dans l'exception zone du filter du meme controller.

Refus audites synchroniquement: 401 (auth), 403 (role/RLS), 429 (rate-limit), 409-RETENTION-DUE.


3. Enregistrement module (DocumentsModule)

Modifications dans documents.module.ts:

Element Section Ajout
disseminationConfig imports ConfigModule.forFeature(disseminationConfig)
DisseminationAttestation imports TypeOrmModule.forFeature([..., DisseminationAttestation])
DisseminationController controllers Ajout dans le tableau controllers
DisseminationService providers Provider injectable
DisseminationAuditExceptionFilter providers Requis pour @UseFilters() (pattern PD-60)
DisseminationRateLimitGuard providers Provider injectable (Redis dependency via DI)

4. Verification invariants code contract

Invariant Statut Mecanisme
POST /api/v1/documents/disseminations pour SEALED -> DIP OK @Post('disseminations') + @HttpCode(HttpStatus.CREATED)
POST /api/v1/documents/{id}/dissemination-return pour DIP -> SEALED OK @Post(':id/dissemination-return') + @HttpCode(HttpStatus.OK)
Guards F1: JwtAuthGuard + AuthorizationGuard + DisseminationRateLimitGuard OK @UseGuards(JwtAuthGuard, AuthorizationGuard, DisseminationRateLimitGuard) sur disseminate()
Guards F2: JwtAuthGuard + AuthorizationGuard (roles incluant retention_service) OK @UseGuards(JwtAuthGuard, AuthorizationGuard) + @Roles('PA', 'SA', 'auditor', 'retention_service') sur returnFromDissemination()

5. Verification forbidden

Forbidden Statut Justification
GET endpoint qui modifie l'etat OK Aucun @Get() dans le controller — uniquement @Post()
Bypass de ParseUUIDPipe sur document_id OK @Param('id', ParseUUIDPipe) sur F2 ; documents[] valides par @IsUUID('4', { each: true }) dans DTO
Enregistrement dans un module autre que DocumentsModule OK Controller enregistre dans DocumentsModule.controllers

6. Decisions architecturales

architectural_decisions:
  - decision: "Delegation totale de la logique metier au DisseminationService"
    rationale: "Pattern RestitutionController etabli  controller thin, service fat. Separer validation HTTP de la logique ACID."
    alternatives_considered: ["Logique dans le controller", "Middleware chain"]
    trade_offs: ["Controller simple et testable unitairement, mais ajout d'un appel supplementaire"]
  - decision: "@UseFilters au niveau classe (pas par methode)"
    rationale: "Les deux endpoints F1/F2 doivent auditer les refus securite  scope classe evite la duplication"
    alternatives_considered: ["@UseFilters par methode", "APP_GUARD global"]
    trade_offs: ["Filter applique a tout le controller, mais c'est le comportement voulu"]

7. Matrice de couverture tests (controller)

Le controller lui-meme est un thin wrapper. Les tests contractuels ci-dessous le concernent via les tests d'integration:

Test ID Couverture controller Fichier test
TC-NOM-01 Flux F1 complet (route + guards + service) dissemination.controller.spec.ts
TC-NOM-03 Flux F2 complet (route + guards + service) dissemination.controller.spec.ts
TC-ERR-01 ParseUUIDPipe rejet UUID invalide (F2) dissemination.controller.spec.ts
TC-ERR-02 DTO rejette champs serveur interdits (F1) dissemination.controller.spec.ts
TC-ERR-03 JwtAuthGuard rejet 401 + audit filter dissemination.controller.spec.ts
TC-ERR-04 AuthorizationGuard rejet 403 + audit filter dissemination.controller.spec.ts
TC-ERR-08A DTO ArrayMinSize(1) rejet documents=[] dissemination.controller.spec.ts
TC-ERR-08B DTO ArrayMaxSize(100) rejet documents[101] dissemination.controller.spec.ts
TC-ERR-12 Role non autorise pour F2 (DIP -> SEALED) dissemination.controller.spec.ts
TC-ERR-13 Rate-limit guard rejet 429 + audit filter dissemination.controller.spec.ts

8. Hypotheses et dependances

ID Hypothese Statut
H-02 Roles IAM PA, SA, auditor, retention_service existent Confirme: AuthorizationGuard lit les roles via @Roles() + Reflector
V-05 Exception filter scope: guards decores @UseGuards() sont dans l'exception zone de @UseFilters() du meme controller Confirme: pattern NestJS standard

9. Verification TypeScript

npx tsc --noEmit --pretty → 0 erreurs

10. Fichiers hors perimetre identifies

Aucun besoin de modification hors perimetre identifie. Toutes les dependances (service, guard, filter, DTOs, config, entities) existent deja et sont consommees telles quelles.