Skip to content

feat: audit log for data protection requests (erasure, export, restriction)#589

Open
Joe-Heffer-Shef wants to merge 5 commits into
mainfrom
586-audit-log-for-data-protection-requests-erasure-export-restriction
Open

feat: audit log for data protection requests (erasure, export, restriction)#589
Joe-Heffer-Shef wants to merge 5 commits into
mainfrom
586-audit-log-for-data-protection-requests-erasure-export-restriction

Conversation

@Joe-Heffer-Shef

@Joe-Heffer-Shef Joe-Heffer-Shef commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator

Closes #586

Summary

  • Adds an append-only DataProtectionEvent model that stores a permanent record of data protection actions (erasure, subject access export, account restriction/unrestriction, consent withdrawal). Entries survive deletion of the subject user, so identity is stored as a stable string rather than a FK. The model prevents mutations and deletes at the ORM level to enforce GDPR Article 5(2) accountability.
  • Adds DataProtectionService (data_protection_service singleton) with record_event (write-only, no permission gate — callers are already authorised) and list_events (staff-only, supports filtering by event type, subject user, and date range).
  • Adds a staff-only console view at /console/data-protection/ with event-type and subject-user-ID filtering, 25-per-page pagination, and a nav link in the admin sidebar.
  • Hooks the existing remove-member action to automatically record a RESTRICTION event (member removed from organisation).
  • Migration 0013_dataprotectionevent with composite indexes on (event_type, actioned_at) and subject_user_id.

Screenshots

Data protection log with dummy data

image

Test plan

  • DataProtectionEvent append-only enforcement (save on existing instance raises, delete raises)
  • record_event persists all expected fields; falls back to deleted-user-<pk> when user has no email
  • list_events denies non-staff; filters correctly by event type
  • Console view: anonymous → 302, regular user → 403, staff → 200 with event content
  • Remove-member POST records a RESTRICTION event attributed to the acting staff user
python manage.py test home.tests.test_data_protection --parallel=auto --failfast
# 10 tests, all pass

🤖 Generated with Claude Code

Adds an append-only DataProtectionEvent model (UK GDPR Article 5(2)
accountability) with event types for erasure, export, restriction,
unrestriction, and consent withdrawal. Includes a staff-only console
view at /console/data-protection/ with filtering and pagination.
Removing an organisation member now automatically records a RESTRICTION
event.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Joe-Heffer-Shef Joe-Heffer-Shef linked an issue Apr 22, 2026 that may be closed by this pull request
4 tasks
@Joe-Heffer-Shef Joe-Heffer-Shef self-assigned this Apr 22, 2026
@Joe-Heffer-Shef Joe-Heffer-Shef added the enhancement New feature or request label Apr 22, 2026
Joe-Heffer-Shef and others added 4 commits April 22, 2026 16:51
- Fix nav active-state check ('/admin-portal/data-protection' → '/console/data-protection')
- Block QuerySet.delete() via pre_delete signal to close bulk-delete bypass on append-only model
- Replace PermissionDenied with ValueError for immutability violations (save/delete on existing records)
- Pass filter_qs context variable to simplify pagination URL construction in template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Joe-Heffer-Shef

Copy link
Copy Markdown
Collaborator Author

can we use https://pypi.org/project/django-GDPR/ instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit log for data protection requests (erasure, export, restriction)

1 participant