698 lines
23 KiB
Markdown
698 lines
23 KiB
Markdown
# 📄 Documentation Technique - Gestion Documents Légaux (CGU/Privacy)
|
|
|
|
**Version** : 1.0
|
|
**Date** : 25 Novembre 2025
|
|
**Auteur** : Équipe PtitsPas
|
|
**Référence** : RGPD & Conformité juridique
|
|
|
|
---
|
|
|
|
## 📖 Table des matières
|
|
|
|
1. [Vue d'ensemble](#vue-densemble)
|
|
2. [Architecture](#architecture)
|
|
3. [Tables BDD](#tables-bdd)
|
|
4. [Service Documents Légaux](#service-documents-légaux)
|
|
5. [Workflow Upload & Activation](#workflow-upload--activation)
|
|
6. [Workflow Acceptation Utilisateur](#workflow-acceptation-utilisateur)
|
|
7. [APIs](#apis)
|
|
8. [Interface Admin](#interface-admin)
|
|
9. [Conformité RGPD](#conformité-rgpd)
|
|
|
|
---
|
|
|
|
## 🎯 Vue d'ensemble
|
|
|
|
### Problématique
|
|
|
|
Chaque collectivité déployant P'titsPas on-premise doit pouvoir :
|
|
1. ✅ **Personnaliser** les CGU et la Politique de confidentialité
|
|
2. ✅ **Versionner** les documents (traçabilité juridique)
|
|
3. ✅ **Tracer** qui a accepté quelle version (RGPD)
|
|
4. ✅ **Prouver** l'acceptation (IP, User-Agent, horodatage)
|
|
5. ✅ **Empêcher** le retour en arrière (sécurité juridique)
|
|
|
|
### Solution
|
|
|
|
- **Documents génériques v1** fournis par défaut (rédigés avec juriste)
|
|
- **Upload de nouvelles versions** par l'admin (PDF uniquement)
|
|
- **Versioning automatique** (incrémentation sans retour arrière)
|
|
- **Activation manuelle** (prévisualisation avant mise en prod)
|
|
- **Traçabilité complète** (hash SHA-256, IP, User-Agent)
|
|
|
|
---
|
|
|
|
## 🏗️ Architecture
|
|
|
|
### Flux de données
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Workflow Documents │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ 1. UPLOAD (Admin) │
|
|
│ Admin ──▶ API ──▶ File System ──▶ BDD │
|
|
│ /documents/legaux/ │
|
|
│ cgu_v4_<timestamp>.pdf │
|
|
│ │
|
|
│ 2. ACTIVATION (Admin) │
|
|
│ Admin ──▶ API ──▶ BDD (actif=true) │
|
|
│ │
|
|
│ 3. ACCEPTATION (Utilisateur) │
|
|
│ User ──▶ Frontend ──▶ API ──▶ BDD │
|
|
│ (inscription) (trace IP/UA) │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Composants
|
|
|
|
1. **Table `documents_legaux`** : Stockage versions + métadonnées
|
|
2. **Table `acceptations_documents`** : Traçabilité acceptations
|
|
3. **Service `DocumentsLegauxService`** : Upload, versioning, activation
|
|
4. **API REST** : CRUD documents
|
|
5. **Interface Admin** : Upload + activation
|
|
6. **Interface Inscription** : Affichage + acceptation
|
|
|
|
---
|
|
|
|
## 📊 Tables BDD
|
|
|
|
### Table 1 : `documents_legaux`
|
|
|
|
```sql
|
|
-- Table pour gérer les versions des documents légaux
|
|
CREATE TABLE documents_legaux (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
type VARCHAR(50) NOT NULL, -- 'cgu' ou 'privacy'
|
|
version INTEGER NOT NULL, -- Numéro de version (auto-incrémenté)
|
|
fichier_nom VARCHAR(255) NOT NULL, -- Nom original du fichier
|
|
fichier_path VARCHAR(500) NOT NULL, -- Chemin de stockage
|
|
fichier_hash VARCHAR(64) NOT NULL, -- Hash SHA-256 pour intégrité
|
|
actif BOOLEAN DEFAULT false, -- Version actuellement active
|
|
televerse_par UUID REFERENCES utilisateurs(id), -- Qui a uploadé
|
|
televerse_le TIMESTAMPTZ DEFAULT now(), -- Date d'upload
|
|
active_le TIMESTAMPTZ, -- Date d'activation
|
|
UNIQUE(type, version) -- Pas de doublon version
|
|
);
|
|
|
|
-- Index pour performance
|
|
CREATE INDEX idx_documents_legaux_type_actif ON documents_legaux(type, actif);
|
|
CREATE INDEX idx_documents_legaux_version ON documents_legaux(type, version DESC);
|
|
```
|
|
|
|
**Contraintes** :
|
|
- ✅ Un seul document `actif=true` par type à la fois
|
|
- ✅ Versioning auto-incrémenté (pas de gaps)
|
|
- ✅ Hash SHA-256 pour vérifier l'intégrité du fichier
|
|
|
|
---
|
|
|
|
### Table 2 : `acceptations_documents`
|
|
|
|
```sql
|
|
-- Table de traçabilité des acceptations (RGPD)
|
|
CREATE TABLE acceptations_documents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
id_utilisateur UUID REFERENCES utilisateurs(id) ON DELETE CASCADE,
|
|
id_document UUID REFERENCES documents_legaux(id),
|
|
type_document VARCHAR(50) NOT NULL, -- 'cgu' ou 'privacy'
|
|
version_document INTEGER NOT NULL, -- Version acceptée
|
|
accepte_le TIMESTAMPTZ DEFAULT now(), -- Date d'acceptation
|
|
ip_address INET, -- IP de l'utilisateur (RGPD)
|
|
user_agent TEXT -- Navigateur (preuve)
|
|
);
|
|
|
|
CREATE INDEX idx_acceptations_utilisateur ON acceptations_documents(id_utilisateur);
|
|
CREATE INDEX idx_acceptations_document ON acceptations_documents(id_document);
|
|
```
|
|
|
|
**Données capturées** :
|
|
- ✅ **Qui** : `id_utilisateur`
|
|
- ✅ **Quoi** : `type_document`, `version_document`
|
|
- ✅ **Quand** : `accepte_le`
|
|
- ✅ **Où** : `ip_address`
|
|
- ✅ **Comment** : `user_agent`
|
|
|
|
---
|
|
|
|
### Modification table `utilisateurs`
|
|
|
|
```sql
|
|
-- Ajouter colonnes pour référence rapide (optionnel)
|
|
ALTER TABLE utilisateurs
|
|
ADD COLUMN cgu_version_acceptee INTEGER,
|
|
ADD COLUMN cgu_acceptee_le TIMESTAMPTZ,
|
|
ADD COLUMN privacy_version_acceptee INTEGER,
|
|
ADD COLUMN privacy_acceptee_le TIMESTAMPTZ;
|
|
```
|
|
|
|
**Note** : Ces colonnes sont **redondantes** avec `acceptations_documents`, mais permettent un accès rapide sans JOIN.
|
|
|
|
---
|
|
|
|
### Seed initial
|
|
|
|
```sql
|
|
-- Documents génériques v1 (fournis par défaut)
|
|
INSERT INTO documents_legaux (type, version, fichier_nom, fichier_path, fichier_hash, actif, televerse_le, active_le) VALUES
|
|
('cgu', 1, 'cgu_v1_default.pdf', '/documents/legaux/cgu_v1_default.pdf', 'a3f8b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2', true, now(), now()),
|
|
('privacy', 1, 'privacy_v1_default.pdf', '/documents/legaux/privacy_v1_default.pdf', 'b4f9c3d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4', true, now(), now());
|
|
```
|
|
|
|
**Fichiers à fournir** :
|
|
- `/documents/legaux/cgu_v1_default.pdf` (rédigé avec juriste)
|
|
- `/documents/legaux/privacy_v1_default.pdf` (conforme RGPD)
|
|
|
|
---
|
|
|
|
## 🔧 Service Documents Légaux
|
|
|
|
### Responsabilités
|
|
|
|
1. **Récupérer documents actifs** : `getDocumentsActifs()`
|
|
2. **Uploader nouvelle version** : `uploadNouvelleVersion(type, file, userId)`
|
|
3. **Activer une version** : `activerVersion(documentId)`
|
|
4. **Lister versions** : `listerVersions(type)`
|
|
5. **Télécharger document** : `telechargerDocument(documentId)`
|
|
|
|
### Implémentation (TypeScript)
|
|
|
|
```typescript
|
|
// backend/src/documents-legaux/documents-legaux.service.ts
|
|
import { Injectable } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository } from 'typeorm';
|
|
import { DocumentLegal } from './entities/document-legal.entity';
|
|
import * as crypto from 'crypto';
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
|
|
@Injectable()
|
|
export class DocumentsLegauxService {
|
|
private readonly UPLOAD_DIR = '/app/documents/legaux';
|
|
|
|
constructor(
|
|
@InjectRepository(DocumentLegal)
|
|
private docRepo: Repository<DocumentLegal>,
|
|
) {}
|
|
|
|
// Récupérer les documents actifs
|
|
async getDocumentsActifs(): Promise<{ cgu: DocumentLegal; privacy: DocumentLegal }> {
|
|
const cgu = await this.docRepo.findOne({
|
|
where: { type: 'cgu', actif: true },
|
|
});
|
|
|
|
const privacy = await this.docRepo.findOne({
|
|
where: { type: 'privacy', actif: true },
|
|
});
|
|
|
|
if (!cgu || !privacy) {
|
|
throw new Error('Documents légaux manquants');
|
|
}
|
|
|
|
return { cgu, privacy };
|
|
}
|
|
|
|
// Uploader une nouvelle version
|
|
async uploadNouvelleVersion(
|
|
type: 'cgu' | 'privacy',
|
|
file: Express.Multer.File,
|
|
userId: string,
|
|
): Promise<DocumentLegal> {
|
|
// 1. Calculer la prochaine version
|
|
const lastDoc = await this.docRepo.findOne({
|
|
where: { type },
|
|
order: { version: 'DESC' },
|
|
});
|
|
const nouvelleVersion = (lastDoc?.version || 0) + 1;
|
|
|
|
// 2. Calculer le hash du fichier
|
|
const fileBuffer = file.buffer;
|
|
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
|
|
|
// 3. Générer le nom de fichier unique
|
|
const timestamp = Date.now();
|
|
const fileName = `${type}_v${nouvelleVersion}_${timestamp}.pdf`;
|
|
const filePath = path.join(this.UPLOAD_DIR, fileName);
|
|
|
|
// 4. Sauvegarder le fichier
|
|
await fs.mkdir(this.UPLOAD_DIR, { recursive: true });
|
|
await fs.writeFile(filePath, fileBuffer);
|
|
|
|
// 5. Créer l'entrée en BDD
|
|
const document = this.docRepo.create({
|
|
type,
|
|
version: nouvelleVersion,
|
|
fichier_nom: file.originalname,
|
|
fichier_path: filePath,
|
|
fichier_hash: hash,
|
|
actif: false, // Pas actif par défaut
|
|
televerse_par: userId,
|
|
televerse_le: new Date(),
|
|
});
|
|
|
|
return await this.docRepo.save(document);
|
|
}
|
|
|
|
// Activer une version
|
|
async activerVersion(documentId: string): Promise<void> {
|
|
const document = await this.docRepo.findOne({ where: { id: documentId } });
|
|
|
|
if (!document) {
|
|
throw new Error('Document non trouvé');
|
|
}
|
|
|
|
// Transaction : désactiver l'ancienne version, activer la nouvelle
|
|
await this.docRepo.manager.transaction(async (manager) => {
|
|
// Désactiver toutes les versions de ce type
|
|
await manager.update(
|
|
DocumentLegal,
|
|
{ type: document.type, actif: true },
|
|
{ actif: false },
|
|
);
|
|
|
|
// Activer la nouvelle version
|
|
await manager.update(
|
|
DocumentLegal,
|
|
{ id: documentId },
|
|
{ actif: true, active_le: new Date() },
|
|
);
|
|
});
|
|
}
|
|
|
|
// Lister toutes les versions (pour l'admin)
|
|
async listerVersions(type: 'cgu' | 'privacy'): Promise<DocumentLegal[]> {
|
|
return await this.docRepo.find({
|
|
where: { type },
|
|
order: { version: 'DESC' },
|
|
relations: ['televerse_par'],
|
|
});
|
|
}
|
|
|
|
// Télécharger un document (stream)
|
|
async telechargerDocument(documentId: string): Promise<{ stream: Buffer; filename: string }> {
|
|
const document = await this.docRepo.findOne({ where: { id: documentId } });
|
|
|
|
if (!document) {
|
|
throw new Error('Document non trouvé');
|
|
}
|
|
|
|
const fileBuffer = await fs.readFile(document.fichier_path);
|
|
|
|
return {
|
|
stream: fileBuffer,
|
|
filename: document.fichier_nom,
|
|
};
|
|
}
|
|
|
|
// Vérifier l'intégrité d'un document
|
|
async verifierIntegrite(documentId: string): Promise<boolean> {
|
|
const document = await this.docRepo.findOne({ where: { id: documentId } });
|
|
|
|
if (!document) {
|
|
throw new Error('Document non trouvé');
|
|
}
|
|
|
|
const fileBuffer = await fs.readFile(document.fichier_path);
|
|
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
|
|
|
return hash === document.fichier_hash;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Workflow Upload & Activation
|
|
|
|
### Diagramme de séquence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant A as Admin
|
|
participant API as Backend API
|
|
participant FS as File System
|
|
participant DB as PostgreSQL
|
|
|
|
A->>API: POST /api/v1/documents-legaux<br/>{type: 'cgu', file: PDF}
|
|
|
|
API->>API: Validation fichier<br/>(PDF, max 10MB)
|
|
API->>API: Calcul hash SHA-256
|
|
|
|
API->>DB: SELECT MAX(version)<br/>WHERE type='cgu'
|
|
DB-->>API: version = 3
|
|
|
|
API->>API: Nouvelle version = 4
|
|
|
|
API->>FS: Enregistrer fichier<br/>/documents/legaux/cgu_v4_<timestamp>.pdf
|
|
FS-->>API: ✅ Fichier sauvegardé
|
|
|
|
API->>DB: INSERT INTO documents_legaux<br/>(type, version=4, actif=false)
|
|
DB-->>API: ✅ Document créé
|
|
|
|
API-->>A: 201 Created<br/>{id, version: 4, actif: false}
|
|
|
|
A->>A: Prévisualisation PDF
|
|
A->>API: PATCH /api/v1/documents-legaux/{id}/activer
|
|
|
|
API->>DB: BEGIN TRANSACTION
|
|
API->>DB: UPDATE documents_legaux<br/>SET actif=false WHERE type='cgu'
|
|
API->>DB: UPDATE documents_legaux<br/>SET actif=true, active_le=now()<br/>WHERE id={id}
|
|
API->>DB: COMMIT
|
|
|
|
API-->>A: ✅ CGU v4 activées
|
|
```
|
|
|
|
---
|
|
|
|
## 📥 Workflow Acceptation Utilisateur
|
|
|
|
### Diagramme de séquence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant U as Utilisateur
|
|
participant App as Frontend
|
|
participant API as Backend
|
|
participant DB as PostgreSQL
|
|
|
|
U->>App: Inscription (étape CGU)
|
|
|
|
App->>API: GET /api/v1/documents-legaux/actifs
|
|
API->>DB: SELECT * FROM documents_legaux<br/>WHERE actif=true
|
|
DB-->>API: {cgu: v4, privacy: v2}
|
|
API-->>App: {cgu: {version: 4, url: '...'}, privacy: {...}}
|
|
|
|
App->>App: Afficher liens PDF<br/>"CGU v4" et "Privacy v2"
|
|
|
|
U->>U: Lit les documents
|
|
U->>U: Coche "J'accepte"
|
|
|
|
App->>API: POST /api/v1/auth/register<br/>{..., cgu_version: 4, privacy_version: 2, ip, user_agent}
|
|
|
|
API->>DB: BEGIN TRANSACTION
|
|
|
|
API->>DB: INSERT INTO utilisateurs<br/>(..., cgu_version_acceptee=4, privacy_version_acceptee=2)
|
|
DB-->>API: id_utilisateur
|
|
|
|
API->>DB: INSERT INTO acceptations_documents<br/>(id_utilisateur, type='cgu', version=4, ip, user_agent)
|
|
API->>DB: INSERT INTO acceptations_documents<br/>(id_utilisateur, type='privacy', version=2, ip, user_agent)
|
|
|
|
API->>DB: COMMIT
|
|
|
|
API-->>App: ✅ Inscription réussie
|
|
```
|
|
|
|
---
|
|
|
|
## 🔌 APIs
|
|
|
|
### API 1 : Récupérer documents actifs (Public)
|
|
|
|
```http
|
|
GET /api/v1/documents-legaux/actifs
|
|
```
|
|
|
|
**Réponse 200** :
|
|
```json
|
|
{
|
|
"cgu": {
|
|
"id": "uuid-cgu-v4",
|
|
"type": "cgu",
|
|
"version": 4,
|
|
"url": "/api/v1/documents-legaux/uuid-cgu-v4/download",
|
|
"active_le": "2025-11-20T14:30:00Z"
|
|
},
|
|
"privacy": {
|
|
"id": "uuid-privacy-v2",
|
|
"type": "privacy",
|
|
"version": 2,
|
|
"url": "/api/v1/documents-legaux/uuid-privacy-v2/download",
|
|
"active_le": "2025-10-15T09:15:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### API 2 : Lister versions (Admin)
|
|
|
|
```http
|
|
GET /api/v1/documents-legaux/:type/versions
|
|
Authorization: Bearer <super_admin_token>
|
|
```
|
|
|
|
**Paramètres** :
|
|
- `type` : `cgu` | `privacy`
|
|
|
|
**Réponse 200** :
|
|
```json
|
|
[
|
|
{
|
|
"id": "uuid-cgu-v4",
|
|
"version": 4,
|
|
"fichier_nom": "CGU_Mairie_Bezons_2025.pdf",
|
|
"actif": true,
|
|
"televerse_par": {
|
|
"id": "uuid-admin",
|
|
"prenom": "Lucas",
|
|
"nom": "MOREAU"
|
|
},
|
|
"televerse_le": "2025-11-20T14:00:00Z",
|
|
"active_le": "2025-11-20T14:30:00Z"
|
|
},
|
|
{
|
|
"id": "uuid-cgu-v3",
|
|
"version": 3,
|
|
"fichier_nom": "CGU_v3.pdf",
|
|
"actif": false,
|
|
"televerse_par": {
|
|
"id": "uuid-admin",
|
|
"prenom": "Admin",
|
|
"nom": "Système"
|
|
},
|
|
"televerse_le": "2025-10-15T09:00:00Z",
|
|
"active_le": "2025-10-15T09:15:00Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### API 3 : Upload nouvelle version (Admin)
|
|
|
|
```http
|
|
POST /api/v1/documents-legaux
|
|
Authorization: Bearer <super_admin_token>
|
|
Content-Type: multipart/form-data
|
|
```
|
|
|
|
**Body** :
|
|
```
|
|
type: cgu
|
|
file: <fichier PDF>
|
|
```
|
|
|
|
**Réponse 201** :
|
|
```json
|
|
{
|
|
"id": "uuid-nouveau-doc",
|
|
"type": "cgu",
|
|
"version": 5,
|
|
"fichier_nom": "CGU_Mairie_Bezons_2025_v2.pdf",
|
|
"actif": false,
|
|
"televerse_le": "2025-11-25T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
**Erreurs** :
|
|
- `400 Bad Request` : Fichier non PDF ou trop volumineux (>10MB)
|
|
- `401 Unauthorized` : Token manquant ou invalide
|
|
- `403 Forbidden` : Rôle insuffisant (pas super_admin)
|
|
|
|
---
|
|
|
|
### API 4 : Activer une version (Admin)
|
|
|
|
```http
|
|
PATCH /api/v1/documents-legaux/:id/activer
|
|
Authorization: Bearer <super_admin_token>
|
|
```
|
|
|
|
**Réponse 200** :
|
|
```json
|
|
{
|
|
"message": "Document activé avec succès",
|
|
"documentId": "uuid-nouveau-doc",
|
|
"type": "cgu",
|
|
"version": 5
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### API 5 : Télécharger document (Public)
|
|
|
|
```http
|
|
GET /api/v1/documents-legaux/:id/download
|
|
```
|
|
|
|
**Réponse 200** :
|
|
```
|
|
Content-Type: application/pdf
|
|
Content-Disposition: attachment; filename="CGU_v5.pdf"
|
|
|
|
<binary PDF data>
|
|
```
|
|
|
|
---
|
|
|
|
### API 6 : Historique acceptations utilisateur (Admin)
|
|
|
|
```http
|
|
GET /api/v1/users/:userId/acceptations
|
|
Authorization: Bearer <super_admin_token>
|
|
```
|
|
|
|
**Réponse 200** :
|
|
```json
|
|
[
|
|
{
|
|
"type_document": "cgu",
|
|
"version_document": 4,
|
|
"accepte_le": "2025-11-20T15:30:00Z",
|
|
"ip_address": "192.168.1.100",
|
|
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
},
|
|
{
|
|
"type_document": "privacy",
|
|
"version_document": 2,
|
|
"accepte_le": "2025-11-20T15:30:00Z",
|
|
"ip_address": "192.168.1.100",
|
|
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
## 💻 Interface Admin
|
|
|
|
### Écran Gestion Documents Légaux
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 📄 Gestion des Documents Légaux │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ [ CGU ] [ Politique de confidentialité ] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ 📋 Conditions Générales d'Utilisation (CGU) │
|
|
│ │
|
|
│ Version active : v4 │
|
|
│ Activée le : 20/11/2025 14:30 │
|
|
│ Téléversée par : Lucas MOREAU │
|
|
│ │
|
|
│ [ 📥 Télécharger ] [ 👁️ Prévisualiser ] │
|
|
│ │
|
|
│ ───────────────────────────────────────────────── │
|
|
│ │
|
|
│ 📤 Uploader une nouvelle version │
|
|
│ │
|
|
│ ⚠️ Attention : L'upload d'une nouvelle version │
|
|
│ créera la version v5. Vous pourrez la prévisualiser│
|
|
│ avant de l'activer. │
|
|
│ │
|
|
│ [ Choisir un fichier PDF ] (max 10MB) │
|
|
│ │
|
|
│ [ 📤 Uploader ] │
|
|
│ │
|
|
│ ───────────────────────────────────────────────── │
|
|
│ │
|
|
│ 📜 Historique des versions │
|
|
│ │
|
|
│ ┌───────────────────────────────────────────────┐ │
|
|
│ │ ✅ v4 (Active) │ │
|
|
│ │ Activée le : 20/11/2025 14:30 │ │
|
|
│ │ Par : Lucas MOREAU │ │
|
|
│ │ Hash : a3f8b2c4...e0f1a2 ✓ │ │
|
|
│ │ [ 📥 Télécharger ] [ 👁️ Voir ] │ │
|
|
│ └───────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌───────────────────────────────────────────────┐ │
|
|
│ │ v3 (Inactive) │ │
|
|
│ │ Activée le : 15/10/2025 09:15 │ │
|
|
│ │ Par : Admin Système │ │
|
|
│ │ Hash : b4f9c3d6...f2a3b4 ✓ │ │
|
|
│ │ [ 📥 Télécharger ] [ 👁️ Voir ] [🔄 Réactiver]│ │
|
|
│ └───────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🔒 Conformité RGPD
|
|
|
|
### Données capturées
|
|
|
|
| Donnée | Justification RGPD | Durée conservation |
|
|
|--------|-------------------|-------------------|
|
|
| `id_utilisateur` | Traçabilité acceptation | Durée du compte |
|
|
| `version_document` | Preuve version acceptée | Durée du compte |
|
|
| `accepte_le` | Horodatage légal | Durée du compte |
|
|
| `ip_address` | Preuve origine acceptation | 1 an (recommandé) |
|
|
| `user_agent` | Preuve navigateur/appareil | 1 an (recommandé) |
|
|
|
|
### Droits utilisateur
|
|
|
|
#### Droit d'accès (Article 15)
|
|
L'utilisateur peut demander :
|
|
- Quelles versions il a acceptées
|
|
- Quand il les a acceptées
|
|
- Depuis quelle IP
|
|
|
|
**API** : `GET /api/v1/users/me/acceptations`
|
|
|
|
#### Droit à l'oubli (Article 17)
|
|
Lors de la suppression du compte :
|
|
- Suppression des données personnelles
|
|
- Conservation des acceptations anonymisées (obligation légale)
|
|
|
|
**Implémentation** :
|
|
```sql
|
|
-- Anonymisation (pas suppression totale)
|
|
UPDATE acceptations_documents
|
|
SET ip_address = NULL,
|
|
user_agent = NULL
|
|
WHERE id_utilisateur = '<uuid>';
|
|
|
|
-- Puis suppression utilisateur
|
|
DELETE FROM utilisateurs WHERE id = '<uuid>';
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 Références
|
|
|
|
### Documentation interne
|
|
- [01_CAHIER-DES-CHARGES.md](./01_CAHIER-DES-CHARGES.md)
|
|
- [10_DATABASE.md](./10_DATABASE.md)
|
|
- [11_API.md](./11_API.md)
|
|
- [21_CONFIGURATION-SYSTEME.md](./21_CONFIGURATION-SYSTEME.md)
|
|
|
|
### Documentation externe
|
|
- [RGPD - Article 7 (Consentement)](https://www.cnil.fr/fr/reglement-europeen-protection-donnees/chapitre2#Article7)
|
|
- [RGPD - Article 15 (Droit d'accès)](https://www.cnil.fr/fr/reglement-europeen-protection-donnees/chapitre3#Article15)
|
|
- [RGPD - Article 17 (Droit à l'oubli)](https://www.cnil.fr/fr/reglement-europeen-protection-donnees/chapitre3#Article17)
|
|
|
|
---
|
|
|
|
**Dernière mise à jour** : 25 Novembre 2025
|
|
**Version** : 1.0
|
|
**Statut** : ✅ Document validé
|
|
|
|
|