feat: self-service account erasure / consent withdrawal (#585) - #699
feat: self-service account erasure / consent withdrawal (#585)#699Joe-Heffer-Shef wants to merge 1 commit into
Conversation
Add a "Delete my account" action on the profile page so withdrawing consent is as easy as giving it (UK GDPR Art. 7(3), 17(1)(b)). Reuses the existing UserService.anonymise erasure mechanism, erasing immediately in the common case, or deferring to staff (via a new ErasureRequest + email notification) when the requester is the sole admin of an organisation with other members. Also closes a gap where staff-initiated erasure never recorded a DataProtectionEvent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review: PR #699 — Self-service account erasure / consent withdrawalOverviewAdds a "Delete my account" self-service flow (Art. 7(3)/17(1)(b) UK GDPR), backed by the existing 🔴 Correctness bug: erasure audit event pseudonymises the anonymised email, not the originalIn user.email = f"deleted-{uuid.uuid4().hex}@{DELETED_ACCOUNT_EMAIL_DOMAIN}"
...
user.save()
OrganisationMembership.objects.filter(user=user).delete()
data_protection_service.record_event(
event_type=DataProtectionEvent.EventType.ERASURE,
subject_user=user,
...
)
This defeats the stated purpose of this PR's own fix ("staff-initiated erasure ... never recorded a Fix: capture 🟠 Self-service erasure has no staff/superuser guardThe console's if target_user == request.user or target_user.is_staff or target_user.is_superuser:
raise PermissionDeniedBut 🟡 Minor issues
Style / conventions
Test coverageGood coverage of the branching logic (immediate erasure, solo-org non-blocking, sole-admin-with-others deferral, co-admin non-blocking, anonymous redirect) and of the console-completion path clearing a pending request. Missing:
Security/privacy
Bottom line: solid feature with good UX and test discipline, but the audit-trail pseudonymisation bug should be fixed before merge since it directly undermines the accountability logging this PR advertises as a fix. 🤖 Generated with Claude Code |
Summary
Closes #585. UK GDPR Art. 7(3) requires withdrawing consent to be as easy as giving it, but the only way to erase an account was previously a staff-only console action — there was no self-service route at all.
/profile/delete/). In the common case this anonymises the account immediately (reusing the existingUserService.anonymiseerasure logic from the staff console) and logs the user out.ErasureRequestis created, staff are emailed, and the user is told their request will be completed within 30 days. Staff complete it via the existing "Delete user" console action — no new completion UI was needed.DataProtectionEvent, even thoughEventType.ERASUREexists for exactly this. Both erasure paths now log a consistent audit event (requested_by/actioned_by).Test plan
python manage.py test home/tests --parallel=auto --failfast— 164 tests passmake check— Django checks + migration check passmake lint— flake8 cleanhome/tests/test_account_deletion.pycovers: immediate self-erasure, deferral when sole-admin-with-other-members, non-blocking when sole admin of a solo org, non-blocking when a co-admin exists, anonymous redirecttest_console_views.pyto assert the audit event is now recorded and that completing a staff deletion closes out any pendingErasureRequest🤖 Generated with Claude Code