feat: add password change

Password page with the AD policy listed first, ordered service (throttle limit below the lockout threshold, verification, local hints, directory change, ended sessions, audit and confirmation mail in one transaction), plain-language errors, German text and tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3t4WL7bd9bdHHNjEsxzqC
This commit is contained in:
2026-10-18 10:08:17 +02:00
co-authored by Claude Sonnet 5.5
parent a1a766adef
commit 49c7fd4a0b
22 changed files with 1346 additions and 17 deletions
@@ -4,7 +4,7 @@ type: 'feature'
ticket: '1'
epic: 'epic-account-self-service'
created: '2026-10-18'
status: 'approved'
status: 'built'
baseline_revision: 'ead5670'
route: 'full'
route_source: 'auto'
@@ -102,3 +102,11 @@ context:
- `uv run python manage.py makemigrations --check --dry-run` -- expected: no changes detected
**Done checkpoint (you, on the Linux host against the test OU):** sign in as `ptest-user1`; the rules match the domain policy; a wrong current password shows the message; five wrong ones block the sixth without touching AD (check that `ptest-user1` is not locked out in AD); a too-short and a history-rejected password show the right messages; a good change works, a second session of the same user in another browser is signed out, and the confirmation mail arrives with the time and no password; `ptest-user1`'s new password works at authentik. Reset the test password afterwards with the test-environment script.
## Implementation Notes
- Built inline in the `profile` app. `pytest` 355 passed, 6 skipped; `ruff check` clean; `makemigrations --check` no changes (the app has no tables).
- Judgement calls: the rules are shown above all three fields (the mock-up put them under the new-password field) so they come first in reading order, as FR-5 asks. A form mistake re-reads the policy only to redisplay the rules; it never verifies or changes a password. `ngettext` is used for counts, so the translation test now reads Python with `ast` and checks plural entries. Cancelling and the done page link to the start page. German text added for formal and informal; the confirmation mail is German formal only.
- The mail is rendered once at queue time with the change time in the display zone; the body is removed from the database after sending (see the outbox).
- Not verified: the real directory (the done checkpoint below) and the browser look of the new form, spinner and error summary.
- Not covered by a test: a double submit from two tabs at once (the second attempt fails as a wrong current password, which is harmless).