GDPR self-service (export / erasure)¶
Repod exposes two GDPR self-service endpoints for the personal data tied to a user account: Article 15 (right of access — export) and Article 17 (right to erasure). Both are usable by the account owner directly, or by an administrator acting on another account.
1. Prerequisites¶
- A logged-in Repod session. No special role is required to export or erase your own data.
- To act on someone else's account, the caller must have the
adminrole.
2. Article 15 — Export your own data¶
Step 1 — Call the export endpoint¶
{
"user": {
"username": "alice",
"full_name": "Alice Martin",
"email": "alice@example.com",
"role": "maintainer",
"created_at": "2025-11-02 09:14:00",
"last_login": "2026-08-19 17:03:11",
"auth_source": "local",
"mfa_enabled": true
},
"api_tokens": [
{"name": "ci-runner", "role": "uploader", "created_at": "...", "expires_at": null, "last_used": "..."}
],
"audit_entries": [
{"timestamp": "...", "action": "UPLOAD", "result": "SUCCESS", "user": "alice", "package": "nginx", "detail": "..."}
],
"decisions": [
{"package": "openssl", "version": "3.0.2", "action": "accept_risk", "justification": "...", "decided_at": "..."}
],
"groups": [
{"name": "security-team", "added_at": "..."}
]
}
The export covers: account profile fields, API tokens you created, every audit-log entry where you are the acting user, CVE decisions you recorded, and group memberships. It does not include your password hash or MFA secret.
Who can call this¶
requesting_user["username"] == username (exporting your own data) or requesting_user["role"] == "admin" (an admin exporting anyone's data). Any other combination returns 403.
3. Article 17 — Erase your own data (self-service)¶
Erasure anonymizes the account rather than deleting the row outright — this preserves the legally required audit trail while removing every personally identifying field.
Step 1 — Call the erasure endpoint with your password¶
curl -X DELETE https://repod.example.com/api/v1/auth/users/alice/gdpr \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"password": "your-current-password"}'
{
"status": "ok",
"anonymized_as": "anonymized_a1b2c3d4",
"message": "Données personnelles de alice anonymisées conformément au RGPD Art. 17."
}
Irreversible, and ends your own session
Self-erasure requires your current password as confirmation because it is irreversible — your username, full name, email, and password hash are wiped, your account is deactivated, your API tokens are deleted, and your existing JWT stops working (the account it refers to no longer authenticates). There is no undo.
What erasure does, precisely¶
usersrow:username→anonymized_XXXXXXXX(random),full_name/emailcleared,active = false,hashed_passwordcleared, TOTP secret/pending secret/recovery codes all cleared (MFA is fully wiped too).api_tokenscreated by the account: deleted outright.decision_records.decided_by: rewritten to the same anonymized identifier (the fact that a CVE decision was made is preserved for audit purposes; the identity behind it is not).group_members.username: rewritten to the same anonymized identifier.- Audit log entries are never deleted or modified — legal traceability requirement. They still reference the original username as it was recorded at the time; this is a documented, accepted characteristic of the anonymization (past audit history is a historical record, not personal data actively linked to a live account going forward).
Who can call this¶
- The account owner (
is_self), with mandatory password confirmation in the request body. - An admin acting on a different account — no password confirmation required (same authority convention as the regular
delete_user()admin action).
The one exception: admins cannot self-erase¶
{
"detail": "Un administrateur ne peut pas supprimer son propre compte via cette voie (risque de verrouillage total du système) — demandez à un autre administrateur."
}
If the caller is an admin erasing their own account, the request is rejected with 400 regardless of password. This is a deliberate safety rail: erasing the last (or only) admin account would permanently lock the whole installation out of admin-level access, with no recovery path. An admin who wants their own data erased must ask a second administrator to perform the erasure on their behalf.
4. Article 17 — Admin erasing another user's account¶
curl -X DELETE https://repod.example.com/api/v1/auth/users/bob/gdpr \
-H "Authorization: Bearer $ADMIN_JWT"
No request body is required — an admin acting on someone else's account skips the password-confirmation step entirely. The response shape is identical to the self-service case.
5. Verify it worked¶
- Export: confirm the JSON response includes recognizable data for the account (username, audit entries, tokens) and returns
403when called by a non-admin, non-owner user. - Self-erasure: after calling
DELETE .../gdprwith your password, confirm your old JWT no longer authenticates (GET /api/v1/auth/mewith the same token returns401), and thatGET /api/v1/auth/users(as an admin) shows the account under its newanonymized_XXXXXXXXusername withactive: false. - Admin self-erasure block: as an admin, call
DELETE /api/v1/auth/users/<your-own-username>/gdprand confirm you get400, not200. - Audit trail preserved: confirm
GET /api/v1/audit/logs?action=GDPR_DELETEshows the erasure event itself, and that older entries recorded under the now-anonymized username are still present and unaltered.