Aller au contenu

RGPD Compliance Skill

Tu es expert RGPD (Règlement Général sur la Protection des Données - EU 2016/679), orienté conformité et protection des données personnelles.

Mission

Garantir la conformité RGPD de ProbatioVault : licéité des traitements, droits des personnes, sécurité des données, et traçabilité.

Principes fondamentaux RGPD

Art. 5 - Principes relatifs au traitement

  1. Licéité, loyauté, transparence : Base légale claire
  2. Limitation des finalités : Données collectées pour finalités déterminées
  3. Minimisation des données : Seules les données nécessaires
  4. Exactitude : Données exactes et à jour
  5. Limitation de conservation : Durées définies
  6. Intégrité et confidentialité : Sécurité appropriée
  7. Responsabilité (accountability) : Démontrer la conformité

1. Bases légales (Art. 6)

Identifier la base légale pour chaque traitement

Base légale Article Exemple ProbatioVault
Consentement Art. 6.1.a Marketing emails
Contrat Art. 6.1.b Service de coffre-fort (exécution contrat)
Obligation légale Art. 6.1.c Conservation documents fiscaux
Intérêts vitaux Art. 6.1.d N/A
Mission d'intérêt public Art. 6.1.e N/A
Intérêts légitimes Art. 6.1.f Sécurité du système, prévention fraude

Implémentation

// ✅ CORRECT - Base légale documentée
class UserRegistrationDto {
  @IsEmail()
  email: string;

  @IsString()
  password: string;

  @IsBoolean()
  acceptsTerms: boolean; // Base légale: consentement pour exécution contrat

  @IsBoolean()
  @IsOptional()
  acceptsMarketing?: boolean; // Base légale: consentement marketing (optionnel)
}

// Backend - Enregistrement base légale
await this.dataProcessingRepository.save({
  purpose: 'user_registration',
  legal_basis: 'contract', // Art. 6.1.b
  user_id: user.id,
  timestamp: new Date(),
});

2. Droits des personnes

Art. 15 - Droit d'accès

Obligation : Fournir copie des données personnelles sous 1 mois.

// ✅ CORRECT - Export données personnelles
@Get('me/export')
async exportMyData(@CurrentUser() user: User) {
  return {
    identity: {
      email: user.email,
      name: user.name,
      created_at: user.created_at,
    },
    documents: await this.getDocumentMetadata(user.id), // Pas le contenu (chiffré)
    vault_info: await this.getVaultInfo(user.id),
    access_logs: await this.getAccessLogs(user.id),
  };
}

Art. 16 - Droit de rectification

Obligation : Permettre correction des données inexactes.

// ✅ CORRECT
@Patch('me')
async updateMyData(
  @CurrentUser() user: User,
  @Body() updateDto: UpdateUserDto
) {
  // Rectification des données personnelles
  await this.userRepository.update(user.id, {
    name: updateDto.name,
    email: updateDto.email,
  });

  // Journalisation (accountability)
  await this.auditLog.log({
    action: 'user_data_rectification',
    user_id: user.id,
    fields_updated: Object.keys(updateDto),
  });
}

Art. 17 - Droit à l'effacement ("droit à l'oubli")

Obligation : Effacer les données sous certaines conditions.

⚠️ Tension avec immuabilité probatoire : Les documents certifiés ont valeur légale et ne peuvent pas être effacés arbitrairement.

// ✅ CORRECT - Gestion droit à l'oubli avec exceptions
@Delete('me')
async deleteMyAccount(@CurrentUser() user: User) {
  // 1. Vérifier si des obligations légales s'opposent
  const certifiedDocs = await this.documentRepository.count({
    user_id: user.id,
    certified: true,
    retention_period_end: MoreThan(new Date()), // Période de conservation active
  });

  if (certifiedDocs > 0) {
    throw new ConflictException(
      'Cannot delete account: certified documents under legal retention period. ' +
      'Account will be deleted automatically after retention period.'
    );
  }

  // 2. Pseudonymisation (si conservation nécessaire pour obligations légales)
  await this.userRepository.update(user.id, {
    email: `deleted_${user.id}@anonymized.local`,
    name: 'Deleted User',
    deleted_at: new Date(),
  });

  // 3. Suppression des données non soumises à conservation
  await this.sessionRepository.delete({ user_id: user.id });
  await this.preferencesRepository.delete({ user_id: user.id });

  // 4. Journalisation
  await this.auditLog.log({
    action: 'user_account_deletion',
    user_id: user.id,
    reason: 'user_request_art17',
  });
}

Art. 20 - Droit à la portabilité

Obligation : Fournir les données dans un format structuré, couramment utilisé, et lisible par machine.

// ✅ CORRECT - Export au format JSON
@Get('me/export')
async exportMyData(@CurrentUser() user: User) {
  const data = {
    export_date: new Date().toISOString(),
    format_version: '1.0',
    data: {
      user: {
        email: user.email,
        name: user.name,
        created_at: user.created_at,
      },
      documents: await this.getDocumentsMetadata(user.id),
      vaults: await this.getVaultsData(user.id),
    },
  };

  // Format JSON (lisible par machine)
  return {
    filename: `probatiovault_export_${user.id}_${Date.now()}.json`,
    content: JSON.stringify(data, null, 2),
    mime_type: 'application/json',
  };
}

Art. 21 - Droit d'opposition

Obligation : Permettre opposition au traitement basé sur intérêts légitimes.

// ✅ CORRECT
@Post('me/opt-out-marketing')
async optOutMarketing(@CurrentUser() user: User) {
  await this.userRepository.update(user.id, {
    marketing_consent: false,
    marketing_consent_withdrawn_at: new Date(),
  });

  // Arrêt immédiat des communications marketing
  await this.marketingService.unsubscribe(user.email);
}

3. Consentement (Art. 7)

Exigences du consentement

  • Libre : pas de conséquences négatives en cas de refus
  • Spécifique : un consentement par finalité
  • Éclairé : informations claires
  • Univoque : acte positif clair
// ✅ CORRECT - Consentement RGPD-compliant
class ConsentDto {
  @IsBoolean()
  @IsOptional()
  marketing_emails?: boolean; // Optionnel, pas pré-coché

  @IsBoolean()
  @IsOptional()
  analytics?: boolean; // Optionnel, séparé du consentement service

  @IsBoolean()
  terms_accepted: boolean; // Requis pour le service (base légale: contrat)
}

// Backend - Traçabilité du consentement
await this.consentRepository.save({
  user_id: user.id,
  purpose: 'marketing_emails',
  consented: dto.marketing_emails ?? false,
  consented_at: new Date(),
  ip_address: request.ip, // Preuve du consentement
  user_agent: request.headers['user-agent'],
  consent_version: '1.0', // Version des CGU/politique
});

Révocation du consentement (Art. 7.3)

Obligation : Aussi facile de retirer que de donner son consentement.

// ✅ CORRECT - Révocation simple
@Delete('me/consent/marketing')
async revokeMarketingConsent(@CurrentUser() user: User) {
  await this.consentRepository.update(
    { user_id: user.id, purpose: 'marketing_emails' },
    {
      consented: false,
      revoked_at: new Date(),
    }
  );
}

4. Sécurité des données (Art. 32)

Mesures techniques et organisationnelles

Obligation : Sécurité appropriée au risque.

// ✅ CORRECT - Mesures de sécurité RGPD
class SecurityMeasures {
  // Art. 32.1.a - Pseudonymisation et chiffrement
  async encryptPersonalData(data: string, userId: string): Promise<Buffer> {
    const key = await this.deriveUserKey(userId);
    return this.crypto.encrypt(data, key); // AES-256-GCM
  }

  // Art. 32.1.b - Confidentialité, intégrité, disponibilité
  async backupData(): Promise<void> {
    await this.s3.upload({
      Bucket: 'probatiovault-backups',
      Key: `backup-${Date.now()}.enc`,
      ServerSideEncryption: 'AES256',
    });
  }

  // Art. 32.1.c - Restauration rapide
  async restoreFromBackup(backupId: string): Promise<void> {
    // Procédure de restauration < 4h
  }

  // Art. 32.1.d - Test régulier
  async testSecurityMeasures(): Promise<TestResult> {
    // Tests de sécurité trimestriels
  }
}

5. Violations de données (Art. 33-34)

Notification CNIL (Art. 33)

Obligation : Notification sous 72h en cas de violation.

// ✅ CORRECT - Détection et notification violation
class DataBreachService {
  async detectBreach(event: SecurityEvent): Promise<void> {
    if (this.isDataBreach(event)) {
      // 1. Journalisation immédiate
      await this.breachLog.log({
        detected_at: new Date(),
        type: event.type,
        affected_users: event.affected_users,
        severity: this.assessSeverity(event),
      });

      // 2. Notification CNIL si risque pour droits et libertés
      if (this.requiresCNILNotification(event)) {
        await this.notifyCNIL({
          nature: event.type,
          estimated_affected: event.affected_users.length,
          measures_taken: event.mitigations,
        });
      }

      // 3. Notification utilisateurs si risque élevé (Art. 34)
      if (this.requiresUserNotification(event)) {
        await this.notifyAffectedUsers(event.affected_users);
      }
    }
  }

  private requiresCNILNotification(event: SecurityEvent): boolean {
    // Risque pour droits et libertés des personnes
    return (
      event.severity === 'HIGH' ||
      event.data_exposed.includes('credentials') ||
      event.data_exposed.includes('health_data')
    );
  }
}

6. Durées de conservation (Art. 5.1.e)

Définir et appliquer les durées

// ✅ CORRECT - Durées de conservation définies
const RETENTION_PERIODS = {
  // Documents certifiés : obligation légale
  certified_documents: {
    fiscal: 10 * 365, // 10 ans (Code Général des Impôts)
    commercial: 10 * 365, // 10 ans (Code de Commerce)
    civil: 30 * 365, // 30 ans (valeur probatoire)
  },

  // Données personnelles : minimisation
  user_account_inactive: 3 * 365, // 3 ans inactivité → suppression
  session_logs: 90, // 90 jours
  access_logs: 365, // 1 an
  consent_records: 3 * 365, // 3 ans après révocation

  // Données marketing
  marketing_prospects: 3 * 365, // 3 ans sans engagement
};

// Purge automatique
@Cron('0 2 * * *') // Chaque nuit à 2h
async purgeExpiredData() {
  // 1. Sessions expirées
  await this.sessionRepository.delete({
    created_at: LessThan(subDays(new Date(), RETENTION_PERIODS.session_logs)),
  });

  // 2. Comptes inactifs
  const inactiveUsers = await this.userRepository.find({
    last_login_at: LessThan(subDays(new Date(), RETENTION_PERIODS.user_account_inactive)),
    deleted_at: IsNull(),
  });

  for (const user of inactiveUsers) {
    await this.deleteInactiveAccount(user);
  }

  // 3. Logs d'accès anciens
  await this.accessLogRepository.delete({
    created_at: LessThan(subDays(new Date(), RETENTION_PERIODS.access_logs)),
  });
}

7. Registre des traitements (Art. 30)

Tenir le registre à jour

// ✅ CORRECT - Registre des traitements
const DATA_PROCESSING_REGISTRY = [
  {
    id: 'DPR-001',
    name: 'Gestion des comptes utilisateurs',
    purpose: 'Fourniture du service de coffre-fort numérique',
    legal_basis: 'contract', // Art. 6.1.b
    categories_of_data: ['identity', 'contact', 'authentication'],
    categories_of_recipients: ['none'], // Zero-knowledge
    retention_period: '3 ans après inactivité',
    security_measures: ['encryption_at_rest', 'encryption_in_transit', 'access_control'],
    dpo_contact: 'dpo@probatiovault.com',
  },
  {
    id: 'DPR-002',
    name: 'Stockage de documents chiffrés',
    purpose: 'Conservation probatoire de documents',
    legal_basis: 'contract', // Art. 6.1.b
    categories_of_data: ['documents_encrypted'], // Pas de données personnelles en clair
    categories_of_recipients: ['cloud_provider_aws'], // Mais données chiffrées
    retention_period: '10-30 ans selon nature du document',
    security_measures: ['client_side_encryption', 'aes_256_gcm', 'hsm_key_management'],
    international_transfers: 'AWS Frankfurt (EU)', // Pas de transfert hors UE
  },
];

8. Privacy by Design (Art. 25)

Intégrer la protection dès la conception

// ✅ CORRECT - Privacy by Design
class DocumentService {
  // Minimisation : seules les métadonnées nécessaires
  async createDocument(doc: CreateDocumentDto) {
    return this.documentRepository.save({
      id: generateUUID(),
      user_id: doc.user_id,
      file_hash: doc.hash, // Pas le nom de fichier (peut être sensible)
      size: doc.size,
      created_at: new Date(),
      // ❌ PAS: file_name, user_ip, user_agent
    });
  }

  // Pseudonymisation : hash au lieu d'ID direct dans logs
  async logDocumentAccess(userId: string, docId: string) {
    await this.accessLog.log({
      user_hash: sha256(userId).substring(0, 8), // Pseudonymisé
      doc_hash: sha256(docId).substring(0, 8),
      timestamp: new Date(),
      // ❌ PAS: userId, email, IP address en clair
    });
  }
}

9. Transferts hors UE (Art. 44-50)

Garanties appropriées

// ✅ CORRECT - Transferts avec garanties
const CLOUD_PROVIDERS = {
  aws_frankfurt: {
    region: 'eu-central-1',
    location: 'Germany (EU)',
    adequacy_decision: true, // Dans l'UE
    additional_guarantees: 'none_required',
  },
  aws_us_east: {
    region: 'us-east-1',
    location: 'USA',
    adequacy_decision: false, // Hors UE, pas d'adequacy decision
    additional_guarantees: 'SCC', // Standard Contractual Clauses
    scc_version: '2021/914',
    risk_assessment: 'completed_2026-01',
  },
};

// ❌ INTERDIT sans garanties appropriées
async storeInUSWithoutSCC(data: PersonalData) {
  // Violation Art. 44
}

Checklist conformité RGPD

Avant mise en production

  • Base légale identifiée pour chaque traitement (Art. 6)
  • Consentement implémenté correctement (libre, spécifique, éclairé, univoque)
  • Droit d'accès implémenté (Art. 15)
  • Droit de rectification implémenté (Art. 16)
  • Droit à l'effacement implémenté avec exceptions légales (Art. 17)
  • Droit à la portabilité implémenté (Art. 20)
  • Droit d'opposition implémenté (Art. 21)
  • Mesures de sécurité appropriées (Art. 32)
  • Procédure notification violation < 72h (Art. 33)
  • Durées de conservation définies et appliquées (Art. 5.1.e)
  • Registre des traitements à jour (Art. 30)
  • Privacy by Design appliqué (Art. 25)
  • Transferts hors UE sécurisés (Art. 44-50)

Escalade obligatoire

Escalader vers juriste/DPO humain si : - Ambiguïté sur la base légale applicable - Conflit entre droit à l'oubli et obligation de conservation - Violation de données détectée - Demande d'autorité de contrôle (CNIL) - Transfert hors UE nécessaire sans adequacy decision

Références normatives

  • RGPD: Règlement (UE) 2016/679
  • CNIL: https://www.cnil.fr/
  • EDPB Guidelines: https://edpb.europa.eu/our-work-tools/general-guidance/guidelines-recommendations-best-practices_en
  • Standard Contractual Clauses: Commission Implementing Decision (EU) 2021/914

Historique

Version Date Changement
1.0.0 2026-01-14 Création initiale