- New DELETE /users/{id}: hard-deletes a user only if they have zero
activity footprint (no tests, evidence, worklogs, audit entries, Jira
links, procedure/template suggestions, reviewed imports) — otherwise
rejects with a clear message to deactivate instead, to keep the audit
trail intact. Cleans up the user's own notifications/password-setup
tokens as part of deletion. Self-delete is blocked. Wired to a trash
icon in the Users page with a confirm dialog.
- Registered the EvaluationImport model in app/models/__init__.py — it
was missing, so its table never existed in the SQLite test DB; only
surfaced once the new delete-user footprint check queried it.
- Fixed a redirect bug: the axios response interceptor's on-401 redirect
to /login didn't exempt /set-password, so the auth provider's routine
mount-time /auth/me check (401 for a logged-out visitor) bounced anyone
opening their emailed set-password link away before they could use it.
- A lead opening a test with a pending procedure suggestion awaiting
their review now gets a blocking popup (approve/reject only) instead
of discovering it later in a separate queue.
- Users can be granted more than one role via extra_roles; only one is
ever active at a time (no permission mixing) and a top-bar switcher
lets the user swap which one, taking effect immediately since role
is read fresh from the DB on every request.
- The password-setup/reset webhook now posts the agreed Power Automate
contract ({to, subject, body} with the platform's standard greeting
and signature template) and supports an admin-configured API key
sent as an x-api-key header.
Admins now create users with a name + email (no password) and role;
a Send Email action (per-user, also reusable for resets) issues a
one-time token and POSTs it to an admin-configurable webhook URL —
intended for a Power Automate flow that delivers the actual email.
The old admin-sets-the-password flow is kept server-side
(LegacyUserCreateWithPassword) but no longer wired into the UI.
Every user-facing surface (top bar, sidebar, user management, audit
log, assignee pickers, dispute notifications) now shows full_name
instead of username, falling back to username when unset.
Also fixes long attack_procedure/expected_detection/suggested_text
text overflowing its container in the template review panels and
Red/Blue team fields (missing break-words on whitespace-pre-wrap
blocks).
'manager' has been a fully-functional role throughout the app —
notifications, operator assignment, dispute mediation — but was
missing from both the user creation/update whitelist and the SSO
auto-provisioning role list, so a manager account could never
actually be created.
Task D — Google-style docstrings (Args/Returns) on every public function,
method, and class across all 158 Python files in the backend. Zero ruff D
violations (pydocstyle Google convention).
Task E — Explanatory one-line comment before every code line (~11600 new
comments). ruff check passes clean after isort re-sort.