Skip to content

feat(auth): password change, admin reset and break-glass reset (#117) - #122

Merged
amar-python merged 1 commit into
mainfrom
feat/117-password-reset
Oct 27, 2026
Merged

amar-python merged 1 commit into
mainfrom
feat/117-password-reset

Conversation

@amar-python

Copy link
Copy Markdown
Owner

Fixes #117. Part of ART Milestone 2 follow-up (BR-36). Builds the full proposed scope, as confirmed by the product owner on 27 Oct 2026.

What changes

Part Route or file Behaviour
Change your own password POST /api/auth/password Needs the current password. The new one must be at least 12 characters. This session stays signed in and every other session ends. A wrong current password returns 400 (not 401, so the browser keeps the session) and counts towards the lockout (#102).
Admin reset POST /api/auth/users/{username}/password tenant_admin only (403 for other roles, 401 without a session). It sets the password, clears any lockout, ends all of that user's sessions, and writes a password_reset row to the tenant's audit log. A user in another tenant gets 404.
Break-glass reset scripts/reset_password.py --username <u> For an operator with database access. It sets the password, clears the lockout and ends all sessions. The password comes from TE_NEW_PASSWORD or a prompt.
Frontend key icon in the header Change-password dialog with a confirmation field and the 12-character check.

Decisions for the product owner

  1. Shared users are refused (409). Passwords are global, but memberships are per tenant. If tenant A's admin could reset a user who is also in tenant B, they could take over that user's tenant B access. So that case returns 409 and points to the operator script. Alternative: allow it. Recommendation: keep the refusal.
  2. The audit row is interim. audit_log only has file columns until M3's audit_events (ART M3: immutable source and append-only audit (BR-23, BR-24, BR-35) #107). So the row records the target in file_name and the actor in mode (by <admin>). It's marked with a ponytail: comment for M3 to replace.
  3. The break-glass script isn't audited. It has no tenant context. It prints its result to the operator's terminal.

Docs

Checks

  • Local CI replica: 308 passed, 0 failed, 0 skipped (it was 297 on main). Evals 26/26.
  • New tests: tests/test_password_reset.py has 11 integration tests. tests/test_roles.py now tries reset_password with every role.
  • Mutation checks: each control was removed in turn and at least one test failed each time:
    • no session revocation: 3 failed;
    • own session revoked too: 1 failed;
    • no cross-tenant refusal: 1 failed;
    • no role check: 4 failed;
    • wrong current password not counted: 1 failed;
    • lockout not cleared: 2 failed;
    • no audit row: 1 failed.
  • Frontend:
    • vitest 126 passed (it was 122);
    • the bearer-header mutation fails 1 test;
    • tsc exit 0, eslint clean on changed files, production build OK.
  • make lint (flake8 + bandit): exit 0.
  • tests/test_api_hardening.py lists the allowed audit actions, so it's updated to include password_reset.

Done when

This PR is merged with CI green, and the evidence is attached to #117.


Generated by Claude Code

- POST /api/auth/password: change your own password; this session
  stays, all others end; a wrong current password is 400 and counts
  towards the lockout.
- POST /api/auth/users/{username}/password: a tenant_admin resets a
  member of their own tenant; unlocks, ends that user's sessions and
  writes a password_reset row to the tenant's audit log. A user who is
  also in another tenant is refused (409).
- scripts/reset_password.py: operator reset by username; clears the
  lockout and ends all sessions.
- Frontend: change-password dialog from the header key icon.
- Docs: ROLES.md, QUICKSTART, offline-install known limits, VCRM BR-36.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YPoXzBSj89ZHgw7nfTmbB
@amar-python
amar-python merged commit a5d8544 into main Oct 27, 2026
3 checks passed
@amar-python
amar-python deleted the feat/117-password-reset branch October 27, 2026 08:35
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.

Password change and admin reset for local accounts

2 participants