PD-17 — Retour d'Expérience¶
Navigation User Story
| Document | | | ---------- | -- | | [Spécification](PD-17-specification.md) | | | [Plan d'implémentation](PD-17-plan.md) | | | [Critères d'acceptation](PD-17-acceptability.md) | | | **Retour d'expérience** | *(ce document)* | [Retour à backend-core](../PD-186-epic.md) · [Index User Story](index.md)1. Résumé exécutif¶
PD-17 visait à implémenter une extension probatoire du journal PD-37 pour tracer les accès ALLOW/DENY avec un payload canonique signé HSM.
La première revue d'acceptabilité a identifié deux écarts : E-01 (BLOQUANT) — flux ALLOW/DENY non implémentés ; E-02 (MAJEUR) — schéma canonique non validé.
Ces écarts ont été corrigés par la création de AccessAuditService, AccessAuditGuard, AccessAuditInterceptor, et d'une taxonomie Pd17DenyCode avec validation stricte.
Le verdict final est ACCEPTÉ avec 56 tests unitaires couvrant les composants PD-17.
2. Points fluides¶
- Les contre-mesures de sécurité (§14 du plan) étaient déjà implémentées :
AuditViolationListenerService,AuditSecurityMonitorService, prohibition dblink - Les action types
ACCESS_ALLOWetACCESS_DENYexistaient déjà dansaudit-action.types.ts - L'architecture NestJS (Guard + Interceptor + Decorator) est adaptée au pattern de logging déclaratif
- La réutilisation de
AuditLogService.logAsync()a permis l'intégration transparente avec le pipeline de signature HSM - La structure RFC 8785 (JSON canonique) de PD-37 accepte directement le payload PD-17 dans
metadata - Les 56 tests ont été écrits rapidement grâce aux patterns de mock établis par PD-37
3. Points difficiles¶
| Obstacle | Contexte |
|---|---|
| Écart E-01 détecté tardivement | La revue d'acceptabilité a révélé l'absence totale des flux ALLOW/DENY alors que les contre-mesures sécurité étaient présentes |
| Divergence taxonomie deny_code | La spec §10.1 proposait AUTH_INVALID, ACL_DENY, etc. ; l'implémentation utilise UNAUTHORIZED, FORBIDDEN, etc. (plus proche HTTP) |
| Structure context différente | La spec §5.0 prévoyait actor_type, entity_type ; l'implémentation utilise resource_type, action, http_method (plus REST-centric) |
| Absence de mode strict | Le plan §4.2 prévoyait un rejet d'accès si HSM indisponible ; l'implémentation utilise logAsync() avec queue fallback |
4. Hypothèses révélées tardivement¶
| Hypothèse | Découverte |
|---|---|
| La taxonomie deny_code pouvait être adaptée | Les codes HTTP (401, 403, 404, 429) sont plus universels que les codes métier proposés |
| Le guard devait s'exécuter APRÈS la décision d'accès | Le pattern NestJS impose Guard avant Controller ; l'Interceptor capture le résultat après |
context.actor_type n'était pas nécessaire | L'information est déduite de actorId (UUID = USER, 'system' = SYSTEM) |
| Les JWT claims devaient être inclus | Nécessaire pour renforcer la validation actor_id (contre-mesure §14.3) |
5. Invariants complexes¶
| Invariant | Difficulté |
|---|---|
| Primauté PD-37 | Aucune modification de AuditLog entity ni des colonnes signées — respecté |
| Encodage unique dans entry_canonical | Le payload PD-17 est dans metadata, sérialisé dans entry_canonical — conforme |
| Signature obligatoire ALLOW et DENY | Les deux passent par AuditLogService.log() → signature HSM — conforme |
| Neutralité décisionnelle | AccessAuditService reçoit la décision (exception catchée par interceptor), ne la prend pas — conforme |
6. Dette technique¶
| Dette | Justification | Impact |
|---|---|---|
| Taxonomie deny_code divergente de la spec | Codes HTTP plus universels, mais incohérence documentaire | MINEUR — à harmoniser dans la spec |
| Structure context simplifiée | resource_type/action au lieu de actor_type/entity_type | MINEUR — clarifier dans la spec |
| Pas de mode strict implémenté | logAsync() avec queue préféré pour résilience | ACCEPTABLE — comportement PD-37 |
@AuditedResource non appliqué aux endpoints | Décorateur créé mais non déployé sur les contrôleurs | À PLANIFIER — intégration endpoints |
7. Risques résiduels¶
| Risque | Probabilité | Mitigation |
|---|---|---|
| Endpoints non décorés génèrent 0 événement PD-17 | Élevée (aucun endpoint n'utilise @AuditedResource) | Déploiement progressif sur DocumentsController, FoldersController |
| Volumétrie DENY non monitorée | Moyenne | Ajouter métriques Prometheus sur ACCESS_DENY |
| Taxonomie deny_code incomplète | Faible (11 codes couvrent les cas HTTP) | Extensible via enum |
| Guard global non activé | Élevée (guard exporté mais non APP_GUARD) | Décision architecturale à prendre |
8. Améliorations processus¶
| Suggestion | Bénéfice attendu |
|---|---|
| Revue d'acceptabilité AVANT corrections | Évite de découvrir des écarts BLOQUANTS en fin d'implémentation |
| Valider la taxonomie avec les équipes API/front | Aligne les codes DENY sur les besoins UX (messages utilisateur) |
| Template de payload PD dans les specs | Évite les divergences structure context/deny_code |
| Tests d'intégration end-to-end | Vérifie que le payload signé contient effectivement pd: 'PD-17' |
| Checklist "invariants PD-37" dans chaque PR | Garantit la non-régression des colonnes probatoires |
9. Enseignements clés¶
-
Les contre-mesures de sécurité ne remplacent pas les flux métier — PD-17 avait dblink prohibition et pg_notify, mais pas le cœur fonctionnel (ALLOW/DENY)
-
La taxonomie spec doit être challengée tôt — Les codes métier (
ACL_DENY) sont moins universels que les codes HTTP (FORBIDDEN) -
Le pattern Guard+Interceptor est adapté au logging déclaratif — Le Guard capture le contexte avant, l'Interceptor log après
-
56 tests en < 2h grâce aux patterns PD-37 — La réutilisation des mocks
AuditLogService,AuditSignatureServiceaccélère le développement -
Le mode strict (accès refusé si HSM down) n'est pas implémenté — Décision implicite de privilégier la disponibilité via queue fallback
Annexe — Fichiers livrés¶
| Fichier | Lignes | Tests |
|---|---|---|
types/pd17-access-audit.types.ts | 194 | 18 |
services/access-audit.service.ts | 188 | 13 |
guards/access-audit.guard.ts | 120 | 14 |
interceptors/access-audit.interceptor.ts | 140 | 11 |
decorators/audited-resource.decorator.ts | 25 | — |
| Total | 667 | 56 |