Skip to content

#78 — data-deletion-apdp

PLANIFIÉ

Priorité: 🟠 HAUTE · Type: TYPE C · Conteneur: rgz-beat · Code: app/tasks/apdp.py

Dépendances: #4 rgz-db, #46 module-apdp


Description

Tâche Celery d'anonymisation automatique des données personnelles des abonnés ayant exercé leur droit à l'oubli (APDP Bénin). Exécutée chaque jour à 05:00 UTC, traite toutes les demandes dont le délai de 90 jours est dépassé.

Conformément à la loi APDP du Bénin, un abonné peut demander la suppression de ses données personnelles. RGZ dispose de 90 jours pour s'y conformer. Cette tâche automatise la conformité en anonymisant les champs sensibles tout en préservant les données agrégées nécessaires aux obligations légales (ARCEP, comptabilité).

Chaque anonymisation est tracée dans immutable_logs (#45) pour preuve de conformité.


Architecture Interne

Celery Beat → rgz.apdp.purge_expired → 05:00 UTC daily

SELECT FROM subscribers
WHERE forget_requested_at < NOW() - INTERVAL '90 days'
AND anonymized_at IS NULL

Pour chaque abonné:
  UPDATE subscribers SET
    msisdn = 'ANONYMIZED_' || id,
    msisdn_e164 = 'ANONYMIZED_' || id,
    full_name = 'ANONYMIZED',
    id_document_hash = 'ANONYMIZED',
    anonymized_at = NOW()
  WHERE id = subscriber_id;

  DELETE FROM subscriber_devices WHERE subscriber_id = id;

  INSERT INTO immutable_logs (event_type, data, hash, prev_hash)
  VALUES ('APDP_ANONYMIZATION', {subscriber_id, requested_at, anonymized_at}, ...);

Rapport: N abonnés anonymisés, log dans ELK

Données anonymisées vs préservées

ChampActionRaison
msisdnAnonymiséDonnée personnelle
msisdn_e164AnonymiséDonnée personnelle
full_nameAnonymiséDonnée personnelle
id_document_hashAnonymiséDonnée personnelle
subscriber_refConservéRéférence comptable
total_sessionsConservéStats agrégées ARCEP
total_data_mbConservéStats agrégées ARCEP
created_atConservéObligation légale
subscriber_devicesSuppriméMACs = données perso

Configuration

env
APDP_RETENTION_DAYS=90          # Délai légal avant anonymisation
APDP_BATCH_SIZE=100             # Abonnés traités par batch
DATABASE_URL=postgresql://rgz:password@rgz-db:5432/rgz
CELERY_BROKER_URL=redis://:password@rgz-redis:6379/0

Celery Task

python
# app/tasks/apdp.py
@app.task(name='rgz.apdp.purge_expired', acks_late=True)
def purge_expired_apdp_requests():
    """
    Anonymise les données des abonnés dont la demande APDP
    date de plus de 90 jours.
    """
    cutoff = datetime.utcnow() - timedelta(days=settings.APDP_RETENTION_DAYS)
    # SELECT subscribers WHERE forget_requested_at < cutoff AND anonymized_at IS NULL
    # UPDATE + DELETE + INSERT immutable_log

Schedule (app/celery_app.py)

python
'rgz.apdp.purge_expired': {
    'task': 'rgz.apdp.purge_expired',
    'schedule': crontab(hour=5, minute=0),  # 05:00 UTC daily
    'options': {'queue': 'rgz.compliance'}
},

Commandes Utiles

bash
# Lancer manuellement (test)
docker exec rgz-beat celery -A app.celery_app call rgz.apdp.purge_expired

# Vérifier demandes en attente
docker exec rgz-db psql -U postgres -c \
  "SELECT id, subscriber_ref, forget_requested_at,
          NOW() - forget_requested_at AS age,
          CASE WHEN NOW() - forget_requested_at > INTERVAL '90 days'
               THEN 'DUE' ELSE 'PENDING' END AS status
   FROM subscribers
   WHERE forget_requested_at IS NOT NULL AND anonymized_at IS NULL
   ORDER BY forget_requested_at;"

# Vérifier anonymisations effectuées
docker exec rgz-db psql -U postgres -c \
  "SELECT COUNT(*), DATE(anonymized_at) FROM subscribers
   WHERE anonymized_at IS NOT NULL
   GROUP BY DATE(anonymized_at) ORDER BY 2 DESC LIMIT 30;"

# Voir logs immutables APDP
docker exec rgz-db psql -U postgres -c \
  "SELECT created_at, data->>'subscriber_id' as subscriber_id
   FROM immutable_logs
   WHERE event_type = 'APDP_ANONYMIZATION'
   ORDER BY created_at DESC LIMIT 10;"

Sécurité

RègleImplémentation
AtomicitéTransaction DB — tout ou rien par abonné
IdempotenceWHERE anonymized_at IS NULL — rejeu sûr
TraçabilitéChaque anonymisation → immutable_logs (SEC-08)
LogsLL#27: max-size 10m max-file 3
APDPDélai configurable via APDP_RETENTION_DAYS

Implémentation TODO

  • [ ] app/tasks/apdp.py — tâche Celery anonymisation
  • [ ] Migration: ajout colonnes forget_requested_at, anonymized_at à subscribers
  • [ ] Transaction atomique par abonné
  • [ ] Insert immutable_logs après chaque anonymisation
  • [ ] Rapport journalier: N anonymisations effectuées → ELK
  • [ ] Alerte si demande APDP en retard >85 jours (buffer avant 90j)
  • [ ] Tests unitaires (mock DB, vérifier champs anonymisés)

Dernière mise à jour: 2026-02-21

PROJET MOSAÏQUE — 81 outils, 22 conteneurs, 500+ revendeurs WiFi Zone