Spécification canonique contractuelle — PD-63
Story : PD-63 Endpoint : GET /documents/:id/download Epic : PD-192 (DOCS-API)
1. Objectif
Permettre à un utilisateur authentifié d'obtenir un accès temporaire au blob chiffré d'un document dont il est légitimement détenteur, en garantissant simultanément :
- le respect strict du modèle zero-knowledge,
- la non-divulgation de toute clé de chiffrement,
- la traçabilité probatoire complète de la demande d'accès,
- l'application des règles de révocation et de délégation,
- la compatibilité avec l'architecture multi-cloud (OVH + AWS Glacier).
2. Périmètre / Hors périmètre
2.1 Périmètre
- Vérification d'authentification de l'appelant.
- Vérification de légitimité d'accès au document (
owner ou délégation/enveloppe valide). - Émission d'un accès temporaire au contenu chiffré (TTL 5 minutes).
- La réponse succès retourne un JSON contenant une URL pré-signée S3.
- Journalisation probatoire de chaque demande de téléchargement (succès et rejet).
- Respect des règles de révocation au moment de la demande d'accès.
- Comportement homogène sur les verticales B2C, RH, Mineurs, B2B2C, PRE Legal.
2.2 Hors périmètre
- Déchiffrement côté serveur.
- Exposition de clés (
K_doc, K_master, clés dérivées). - Quotas de téléchargement.
- Streaming de fichiers > 1 Go.
- Reprise partielle (
Range requests). - Téléchargement batch (ZIP).
- Politique UX client de téléchargement.
- Règles non testables techniquement dans cet endpoint : hors périmètre (cf. section 10).
3. Définitions
- Document chiffré : objet binaire stocké chiffré, jamais manipulé en clair par le backend.
- Téléchargement (dans PD-63) : action d'obtenir un droit d'accès temporaire au blob chiffré.
- Légitimité d'accès : possession d'un droit valide (propriété, co-détention, enveloppe/délégation active).
- Révocation : invalidation d'un droit d'accès; bloque toute nouvelle autorisation.
- Traçabilité probatoire : enregistrement auditable append-only de l'événement de demande.
- TTL : durée maximale de validité de l'accès temporaire, fixée à 5 minutes (configurable par environnement).
- Accès implicite/latéral : accès non explicitement accordé par les règles de détention/délégation.
- Suppression logique : état métier où le document reste référencé mais n'est plus téléchargeable; champ modèle requis :
deleted_at (timestamp nullable) et deletion_reason (enum/code). - Verrouillage juridique : état bloquant temporaire imposé par contrainte légale/probatoire; champ modèle requis :
legal_lock (bool), legal_lock_reason (code), legal_lock_until (timestamp nullable).
3.1 Statuts document autorisés
| Statut document | Téléchargeable |
| PENDING | Non (attente validation) |
| SEALED | Oui |
| EXPIRED | Non (rétention dépassée) |
| ARCHIVED | Oui (si restauré) |
4. Invariants (non négociables)
| ID | Règle | Justification |
| INV-63-01 | Le serveur ne voit jamais le document en clair, avant, pendant ou après la demande de téléchargement. | Zero-knowledge contractuel. |
| INV-63-02 | Le serveur n'expose jamais K_doc, K_master ni aucune clé dérivée dans les réponses, logs, erreurs ou métadonnées. | Confidentialité cryptographique. |
| INV-63-03 | L'accès délivré est strictement temporel (TTL nominal 5 min). L'URL pré-signée est réutilisable durant la fenêtre TTL (comportement S3 natif) et devient invalide après expiration du TTL. | Réduction de surface d'attaque. |
| INV-63-04 | Aucun téléchargement n'est autorisé sans authentification valide. | Contrôle d'accès minimal. |
| INV-63-05 | Chaque demande de téléchargement (acceptée ou rejetée) produit un événement journalisé probatoire; si la journalisation probatoire échoue, la requête est rejetée (fail-closed). | Auditabilité et non-répudiation. |
| INV-63-06 | Toute révocation effective bloque immédiatement l'émission d'un nouvel accès temporaire. | Sécurité dynamique des droits. |
| INV-63-07 | Le comportement contractuel est compatible multi-cloud (OVH + AWS Glacier) sans divergence de règles de sécurité. | Portabilité réglementée. |
| INV-63-08 | Aucun accès implicite/latéral n'est autorisé; seul un droit explicite valide ouvre l'accès. | Prévention des accès abusifs. |
| INV-63-09 | L'endpoint ne modifie ni le contenu ni l'état WORM du document. | Intégrité légale de conservation. |
| INV-63-10 | Les réponses d'erreur ne divulguent ni clés, ni chemin de stockage, ni informations excessives sur la politique d'accès. | Réduction des fuites d'information. |
5. Flux nominaux
5.1 Flux A — Owner (B2C)
- Utilisateur authentifié appelle
GET /documents/:id/download. - Le système vérifie que l'utilisateur est détenteur propriétaire du document.
- Le système vérifie absence de révocation bloquante.
- Le système journalise l'événement probatoire de demande d'accès.
- Si la journalisation probatoire échoue, le système rejette la requête (fail-closed,
ERR-63-11). - Le système émet un accès temporaire au blob chiffré (TTL conforme).
- Réponse de succès JSON avec URL pré-signée S3, sans exposition de clé ni contenu en clair.
5.2 Flux B — Partage actif (délégation / PRE / transfert)
- Utilisateur authentifié appelle l'endpoint.
- Le système vérifie l'existence d'un droit de partage/délégation valide et non expiré.
- Le système vérifie absence de révocation.
- Le système journalise l'événement probatoire avec contexte de délégation.
- Si la journalisation probatoire échoue, le système rejette la requête (fail-closed,
ERR-63-11). - Le système émet un accès temporaire chiffré (même règles de sécurité que Owner).
- Réponse de succès JSON avec URL pré-signée S3, sans fuite de secrets.
5.3 Flux C — B2B (co-détention)
- Utilisateur authentifié appelle l'endpoint.
- Le système vérifie co-détention explicite active sur le document.
- Le système applique les mêmes contrôles de révocation et TTL.
- Le système journalise l'événement probatoire avec contexte B2B.
- Si la journalisation probatoire échoue, le système rejette la requête (fail-closed,
ERR-63-11). - Le système émet l'accès temporaire au blob chiffré.
- Réponse de succès JSON avec URL pré-signée S3, homogène (pas d'exception logique verticale).
5bis. Diagrammes
5bis.1 Diagramme d'états — Statut document et téléchargeabilité
Réf. : INV-63-09 (l'endpoint ne modifie ni le contenu ni l'état WORM), section 3.1.
stateDiagram-v2
[*] --> PENDING : Upload initié
PENDING --> SEALED : Validation complète
SEALED --> EXPIRED : Rétention dépassée
SEALED --> ARCHIVED : Archivage froid (Glacier)
ARCHIVED --> SEALED : Restauration
state PENDING {
note right of PENDING
Téléchargement INTERDIT
(attente validation)
end note
}
state SEALED {
note right of SEALED
Téléchargement AUTORISÉ
(INV-63-03 : TTL 5 min)
end note
}
state EXPIRED {
note right of EXPIRED
Téléchargement INTERDIT
(rétention dépassée)
end note
}
state ARCHIVED {
note right of ARCHIVED
Téléchargement AUTORISÉ
si restauré (INV-63-07)
end note
}
note left of [*]
INV-63-09 : PD-63 ne modifie
jamais l'état du document
end note
5bis.2 Diagramme de séquence — Flux nominal Owner (Flux A)
Réf. : INV-63-01 (zero-knowledge), INV-63-02 (aucune clé exposée), INV-63-03 (TTL 5 min), INV-63-04 (auth obligatoire), INV-63-05 (audit fail-closed), INV-63-06 (révocation), INV-63-08 (droit explicite), INV-63-10 (pas de fuite d'info).
sequenceDiagram
actor Client
participant API as GET /documents/:id/download
participant Auth as Auth Guard
participant ACL as Access Control
participant Audit as Audit Probatoire
participant S3 as OVH/AWS S3
Client->>API: GET /documents/:id/download (Bearer token)
activate API
API->>Auth: Vérifier authentification
Note right of Auth: INV-63-04 : auth obligatoire
alt Token absent/invalide/expiré
Auth-->>API: 401 (ERR-63-02)
API->>Audit: Journaliser rejet (DENY)
API-->>Client: 401 Unauthorized
end
Auth-->>API: userId, tenantId
API->>ACL: Vérifier droit explicite (owner/délégation/co-détention)
Note right of ACL: INV-63-08 : droit explicite requis
ACL->>ACL: Vérifier absence de révocation
Note right of ACL: INV-63-06 : révocation bloquante
alt Pas de droit / droit révoqué
ACL-->>API: DENY
API->>Audit: Journaliser rejet (DENY, reason)
API-->>Client: 403 Forbidden (ERR-63-03/04)
Note right of Client: INV-63-10 : message uniforme (anti-énumération)
end
ACL-->>API: ALLOW
API->>Audit: Journaliser demande (ALLOW)
Note right of Audit: INV-63-05 : fail-closed
alt Audit indisponible
Audit-->>API: Échec
API-->>Client: 503 (ERR-63-11) — aucun accès délivré
end
Audit-->>API: OK (correlation_id, timestamp_utc)
API->>S3: Générer URL pré-signée (TTL 5 min)
Note right of S3: INV-63-03 : TTL borné<br/>INV-63-07 : multi-cloud<br/>INV-63-01 : blob chiffré uniquement
alt S3 indisponible
S3-->>API: Échec
API-->>Client: 503 (ERR-63-10)
end
S3-->>API: presigned_url (blob chiffré)
API-->>Client: 200 JSON { presigned_url }
deactivate API
Note over Client,S3: INV-63-02 : aucune clé (K_doc, K_master) dans la réponse<br/>INV-63-09 : aucune modification du document/WORM
6. Cas d'erreur
| ID | Code HTTP | Condition | Réponse attendue |
| ERR-63-01 | 400 | :id invalide (format non conforme) | Erreur structurée, aucune donnée sensible. |
| ERR-63-02 | 401 | Authentification absente/invalide/expirée | Erreur d'authentification standardisée. |
| ERR-63-03 | 403 | Authentifié mais sans droit explicite valide (owner/délégation/co-détention), y compris document existant mais non autorisé | Erreur d'autorisation sans divulgation de secrets (anti-énumération). |
| ERR-63-04 | 403 | Droit révoqué | Erreur d'autorisation, accès refusé. |
| ERR-63-05 | 404 | Document réellement introuvable (identifiant inexistant) | Erreur de non-disponibilité sans metadata sensible. |
| ERR-63-06 | 410 | Document supprimé logiquement ou indisponible définitivement selon politique de conservation | Erreur explicite de non-disponibilité définitive. |
| ERR-63-07 | 423 | Document temporairement verrouillé (conflit d'état juridique/probatoire) | Erreur de verrouillage avec message neutre. |
| ERR-63-08 | 429 | Limitation de débit (si activée globalement par plateforme) | Erreur de throttling standard; optionnelle si feature active. |
| ERR-63-09 | 500 | Erreur interne non qualifiée | Erreur générique sans information sensible. |
| ERR-63-10 | 503 | Dépendance de délivrance d'accès temporaire indisponible | Erreur de service indisponible, retry possible côté client. |
| ERR-63-11 | 503 | Journalisation probatoire indisponible/échouée | Requête rejetée en fail-closed; aucun accès temporaire délivré. |
Note de compatibilité des codes d'erreur : Les codes ERR-63-* remplacent les codes ERR-46-* pour cet endpoint. Mapping de référence : ERR-46-01→ERR-63-02, ERR-46-02→ERR-63-03, ERR-46-03→ERR-63-04, ERR-46-04→ERR-63-05, ERR-46-05→ERR-63-10.
7. Critères d'acceptation (testables)
| ID | Critère | Observable |
| CA-63-01 | Un utilisateur authentifié propriétaire reçoit une réponse succès avec accès temporaire au blob chiffré. | HTTP 2xx + JSON contenant une URL pré-signée S3 + absence de clé. |
| CA-63-02 | Un utilisateur non authentifié est systématiquement rejeté. | HTTP 401. |
| CA-63-03 | Un utilisateur authentifié sans droit explicite est rejeté. | HTTP 403. |
| CA-63-04 | Un droit révoqué bloque l'accès, même si antérieurement valide. | HTTP 403 après révocation. |
| CA-63-05 | La validité de l'accès temporaire est bornée à 5 minutes (ou valeur d'environnement documentée). | Accès valide dans TTL, invalide après TTL. |
| CA-63-06 | Aucune réponse succès/erreur n'expose de clés cryptographiques ni contenu en clair. | Inspection payload/headers/logs : aucune clé/cleartext. |
| CA-63-07 | Chaque demande produit une trace probatoire append-only consultable a posteriori. | Événement audit présent pour succès et rejet avec champs obligatoires : actor_id, document_id, tenant_id, decision (ALLOW/DENY), reason, timestamp_utc, correlation_id. |
| CA-63-08 | L'endpoint n'altère pas le document ni son statut WORM. | État stockage identique avant/après appel. |
| CA-63-09 | Les 3 flux nominaux (Owner, Partage actif, B2B) passent avec règles identiques de sécurité. | Jeux de tests verticaux homogènes. |
| CA-63-10 | Le comportement contractuel est identique quel que soit le backend de stockage cloud. | Résultats de tests contractuels équivalents OVH/AWS. |
8. Scénarios de test (Given/When/Then)
8.1 Nominal
| ID | Given | When | Then |
| ST-63-01 | Utilisateur owner authentifié, document existant, non révoqué | Appel GET /documents/:id/download | 2xx + accès temporaire chiffré + audit créé |
| ST-63-02 | Utilisateur avec délégation active authentifié | Appel endpoint | 2xx + accès temporaire + audit avec contexte délégation |
| ST-63-03 | Utilisateur co-détenteur B2B authentifié | Appel endpoint | 2xx + accès temporaire + audit B2B |
| ST-63-04 | Accès délivré, horloge dans fenêtre TTL | Consommation de l'accès | Accès accepté |
| ST-63-05 | Accès délivré, horloge hors TTL | Consommation de l'accès | Accès refusé |
8.2 Sécurité / droits
| ID | Given | When | Then |
| ST-63-06 | Requête sans authentification | Appel endpoint | 401 |
| ST-63-07 | Authentifié sans droit explicite | Appel endpoint | 403 + pas de métadonnée sensible |
| ST-63-08 | Droit révoqué avant appel | Appel endpoint | 403 |
| ST-63-09 | Tentative d'accès latéral inter-tenant | Appel endpoint | 403 (anti-énumération), audit rejet |
| ST-63-10 | Réponse succès et erreur collectées | Analyse payload/log | Aucune clé, aucun cleartext |
| ID | Given | When | Then |
| ST-63-11 | Appel succès | Traitement complet | 1 événement probatoire append-only créé |
| ST-63-12 | Appel rejeté (401/403/404) | Traitement complet | 1 événement probatoire append-only créé |
| ST-63-13 | Série d'appels sur même document | Vérification audit | Événements horodatés, ordonnés, non modifiables |
| ST-63-14 | Appel endpoint répété | Comparaison état stockage | Aucun changement WORM |
8.4 Résilience
| ID | Given | When | Then |
| ST-63-15 | Service de délivrance d'accès temporaire indisponible | Appel endpoint | 503 + pas de fuite sensible + audit rejet |
| ST-63-16 | Erreur interne non qualifiée | Appel endpoint | 500 générique + audit rejet |
| ST-63-17 | Service audit indisponible | Appel endpoint | 503 (fail-closed) + aucun accès délivré |
9. Hypothèses explicites
| ID | Hypothèse |
| H-63-01 | Le mécanisme de délivrance d'accès temporaire sécurisé de PD-46 est disponible et contractuellement stable. |
| H-63-02 | Les règles de légitimité (owner, délégation, co-détention) sont déterminables de façon univoque au moment de l'appel. |
| H-63-03 | Les événements probatoires d'accès (succès/rejet) sont admissibles dans la chaîne de preuve composite et sont signés via PD-37 (HSM audit signature) ou chaînés cryptographiquement selon l'architecture cible. |
| H-63-04 | Le TTL nominal est de 5 minutes, avec possibilité de variation par environnement sans changer la sémantique de sécurité. |
| H-63-05 | La politique de révocation est déjà définie au niveau domaine et exposable à cet endpoint en lecture décisionnelle. |
| H-63-06 | Les exigences RGPD applicables à cet endpoint se limitent à minimisation des données retournées et auditabilité d'accès. |
| H-63-07 | Les guards de vérification partage/co-détention sont implémentés et testables. |
10. Points à clarifier
- Code attendu pour document archivé/gelé :
410 vs 423 selon statut métier/légal. - Niveau minimal de métadonnées retournées en succès (ex. nom logique, type MIME) pour conformité RGPD (minimisation).
- Compatibilité AWS Glacier : contraintes de latence/récupération froide et impact contractuel sur SLA de l'endpoint.
- Événements "hors périmètre" non testables ici : preuve cryptographique composite finale (assemblage inter-services) à cadrer dans une spec transverse.
Règles marquées hors périmètre (non testables dans PD-63 seul)
- Validation juridique complète de la preuve composite bout-en-bout inter-systèmes.
- Performance/latence garanties sous restauration Glacier (dépend d'opérations de tier externe).
- Contrôle réglementaire documentaire hors API (exigences organisationnelles non observables par tests endpoint).
Généré par : ChatGPT (OpenCode, gpt-5.3-codex) Date : 2026-02-20 Version : v2 — Corrections Gate 3 v1