Commit graph

3 commits

Author SHA1 Message Date
mwiegand
0e2a78e065
secure(l4d2web): block non-admin writes on system overlays; last-admin guard on deactivate
_load_files_overlay docs already promised "owner or admin" for mutations, but the check only filtered by overlay.type — system overlays (user_id IS NULL) were writable by any logged-in user. Add the explicit 403 for non-admins; read-only routes remain open across all overlay types.

Mirror the delete-route last-admin guard on /admin/users/<id>/deactivate so a future auth-model change (service accounts bypassing require_admin, etc.) can't accidentally lock out the system.
2026-05-14 22:24:19 +02:00
mwiegand
e75f379dcb
auth: reject sessions older than user.password_changed_at
load_current_user now treats a session whose pw_changed_at marker
is missing, malformed, or older than the user's current
password_changed_at as logged-out. Same shape as the existing
user.active check.

Forced fan-out updates to every test fixture that forges a session
via session_transaction(): each now stamps a current pw_changed_at
marker. test_deactivated_user_existing_session_invalidated keeps
its meaning — the deactivation still flips the user to inactive,
and load_current_user rejects the session via the user.active
branch before reaching the freshness branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:54:13 +02:00
mwiegand
c594d4b5e8
tests: admin user management
14 tests covering /admin/users/<id>/{deactivate,activate,delete}:

  - deactivate/activate flips and 404 on unknown user
  - deactivate-self refused (409)
  - deactivated user cannot log in (same 401 as wrong-password)
  - existing sessions stop working after deactivation (load_current_user
    returns None for inactive users → @require_login redirects to /login)
  - delete-self refused (409)
  - delete refuses when user owns Server, Blueprint, or custom Overlay
  - delete on orphan succeeds (302 → /admin/users)
  - delete nulls out Job.user_id (jobs survive as audit trail)
  - delete-other-admin succeeds when more than one admin exists

The "last admin" branch in the delete endpoint is defense-in-depth and
unreachable via normal flow (any path that triggers it is shadowed by
self-delete) — covered by a comment, not a test.
2026-05-10 21:19:03 +02:00