Skip to content

feat: suspend user accounts from the staff console (GDPR Art. 18)#607

Open
Joe-Heffer-Shef wants to merge 1 commit into
mainfrom
feat/suspend-user-account
Open

feat: suspend user accounts from the staff console (GDPR Art. 18)#607
Joe-Heffer-Shef wants to merge 1 commit into
mainfrom
feat/suspend-user-account

Conversation

@Joe-Heffer-Shef

Copy link
Copy Markdown
Collaborator

Closes #584

Summary

Implements the Right to Restriction of Processing (UK GDPR Article 18): staff can temporarily suspend a user account — the user can no longer log in or submit data, but all their personal data and survey responses are retained, not deleted. The action is fully reversible. This is distinct from full erasure (#570).

Built on Django's existing User.is_active flag, so there is no migration and no cascade deletion.

Changes

  • Views (home/views/console.py)
    • ConsoleSuspendUserView — GET renders a confirmation page, POST sets is_active=False. Guarded against suspending yourself or a superuser (raises PermissionDenied).
    • ConsoleUnsuspendUserView — POST restores is_active=True.
    • ConsoleUserListView now lists all users (previously active-only, which would have hidden suspended accounts).
  • URLs (home/urls.py) — admin_suspend_user, admin_unsuspend_user.
  • Templates
    • New console/suspend_user_confirm.html — mirrors the existing remove-member confirm dialog (type the email to enable submit); copy makes clear data is retained.
    • console/users.html — new Status column (Active / Suspended badges).
    • console/user_detail.htmlSuspended badge + Suspend account / Lift suspension actions (hidden for self and superusers).
  • Login (home/views/auth.py) — a suspended user with correct credentials sees a clear "Your account has been suspended" message instead of the generic error. Only shown when the password actually matches, so it does not reveal which emails exist.

Acceptance criteria

  • Staff console includes a "Suspend account" action per user
  • A suspended user cannot log in (clear error message)
  • Suspension is reversible by staff (toggle on/off)
  • Suspension state is visible in the staff console user list
  • Suspension does not cascade-delete any related data (test-verified)

Testing

  • python manage.py test home.tests.test_console_views home.tests.test_user_views → 45 tests OK
  • python manage.py check --fail-level WARNING → no issues
  • makemigrations --check --dry-run → no changes detected

10 new tests cover staff-only access, the suspend/unsuspend round-trip, no-cascade (memberships survive), the self/superuser guards (403), suspended-user visibility in the list, and the clear-vs-generic login messages.

Follow-up

An audit trail (who/when) is out of scope here — a separate audit-log PR is in flight. Follow-up issue to wire suspension into it will be linked.

🤖 Generated with Claude Code

Implements the Right to Restriction of Processing: staff can temporarily
freeze a user account so they cannot log in, while retaining all their data.

- Add ConsoleSuspendUserView (confirm + suspend) and ConsoleUnsuspendUserView
  to the staff console, reusing Django's is_active flag (no migration, no
  cascade delete). Guards prevent suspending yourself or a superuser.
- Show all users in the console list with an Active/Suspended status column,
  and add suspend/lift-suspension actions on the user detail page.
- Show a clear "account has been suspended" message on login for suspended
  users with valid credentials, instead of the generic error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Joe-Heffer-Shef

Copy link
Copy Markdown
Collaborator Author

Follow-up for the audit trail tracked in #608.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Right to Restriction: ability to suspend a user account without deleting data

1 participant