Files
gesthub/web/tests/test_rgpd.py
2026-08-21 09:12:02 +02:00

98 lines
3.8 KiB
Python

"""
Tests RGPD — droit à l'effacement (art. 17) : anonymisation d'un `sub`
OIDC dans toutes les tables qui le référencent (routes/rgpd.py,
services/rgpd_service.py, models/rgpd_model.py).
"""
from models import annonce_model, fichier_model, evenement_model, audit_model
from services import annonce_service, evenement_service, rgpd_service
def _seed_user_data(db, sub):
"""Crée une ligne dans chaque table référençant `sub`, y compris
audit_log (via les services, comme le ferait l'application réelle).
create_annonce() et create_evenement() journalisent déjà chacun une
ligne dans audit_log (CREATE_ANNONCE, CREATE_EVENEMENT) ; on ajoute un
LOGIN explicite pour obtenir 3 lignes audit_log au total pour ce sub.
"""
annonce_id = annonce_service.create_annonce(db, "Titre", "Contenu", sub)
fichier_model.insert(db, "doc.pdf", "uuid-stocke", "application/pdf", 10, sub)
evenement_service.create_evenement(
db, "Réunion", "desc", "2026-09-10T10:00:00", "2026-09-10T11:00:00", sub
)
from services import audit_service
audit_service.log(db, "LOGIN", None, "SUCCESS", sub, "127.0.0.1")
return annonce_id
# --- Accès -------------------------------------------------------------------
def test_rgpd01_sans_auth_401(client):
resp = client.delete("/api/admin/users/sub-cible/data")
assert resp.status_code == 401
def test_rgpd02_non_admin_403(user_session):
resp = user_session.delete("/api/admin/users/sub-cible/data")
assert resp.status_code == 403
def test_rgpd03_sub_inconnu_404(admin_session):
resp = admin_session.delete("/api/admin/users/sub-jamais-vu/data")
assert resp.status_code == 404
# --- Anonymisation effective ---------------------------------------------
def test_rgpd04_anonymisation_remplace_le_sub_partout(db, admin_session):
target_sub = "sub-a-effacer-0001"
annonce_id = _seed_user_data(db, target_sub)
resp = admin_session.delete(f"/api/admin/users/{target_sub}/data")
assert resp.status_code == 200
body = resp.get_json()
assert body["status"] == "anonymized"
pseudonym = body["pseudonym"]
assert pseudonym.startswith("anon:")
assert body["rows_updated"] == {
"annonces": 1, "fichiers": 1, "evenements": 1, "audit_log": 3,
}
# Le sub d'origine n'apparaît plus nulle part...
assert annonce_model.get(db, annonce_id)["auteur_sub"] == pseudonym
fichiers = fichier_model.fetch_all(db)
assert fichiers[0]["uploader_sub"] == pseudonym
evenements = evenement_model.fetch_all(db)
assert evenements[0]["createur_sub"] == pseudonym
recent = audit_model.fetch_recent(db)
subs_restants = [row["utilisateur_sub"] for row in recent]
assert target_sub not in subs_restants
# ... mais une entrée d'audit trace l'action d'anonymisation elle-même,
# journalisée sous l'identité de l'ADMIN qui l'a déclenchée (pas la
# cible, déjà pseudonymisée à ce stade).
actions = {row["action"] for row in recent}
assert "RGPD_ANONYMIZE_USER" in actions
def test_rgpd05_pseudonyme_deterministe_pour_un_meme_sub(db):
target_sub = "sub-deterministe-0002"
_seed_user_data(db, target_sub)
result = rgpd_service.anonymize_user(db, target_sub, pepper="pepper-de-test")
assert result["anonymized"] is True
# Toutes les tables ont reçu EXACTEMENT le même pseudonyme.
pseudo = result["pseudonym"]
assert annonce_model.fetch_all(db)[0]["auteur_sub"] == pseudo
assert fichier_model.fetch_all(db)[0]["uploader_sub"] == pseudo
def test_rgpd06_deja_anonymise_refuse(db, admin_session):
target_sub = "sub-double-appel-0003"
_seed_user_data(db, target_sub)
first = admin_session.delete(f"/api/admin/users/{target_sub}/data")
pseudonym = first.get_json()["pseudonym"]
second = admin_session.delete(f"/api/admin/users/{pseudonym}/data")
assert second.status_code == 400