Commit Graph

2 Commits

Author SHA1 Message Date
knacky
48a1c756bf feat(backend): add USER_MANAGE permission + delta migration (D-015)
Adds `Permission.USER_MANAGE = "user.manage"` to the F11 matrix. rt_lead
already holds ALL_PERMISSIONS so GROUP_PERMISSIONS is unchanged — rt_lead
gets the new permission automatically, rt_operator and soc_analyst get 403.

Alembic migration `202605230001_add_user_manage_permission`:
- inserts the `user.manage` row into `permission`,
- inserts the `(rt_lead, user.manage)` link into `group_permission`,
- exposes `_DELTA_PERMISSIONS` / `_DELTA_GROUP_PERMISSIONS` for parity tests.

The previous `test_frozen_*_matches_runtime` invariant (MA3) is generalised
to "runtime = initial frozen ∪ deltas of every migration in `_DELTAS`". New
migrations register themselves there without editing the historical one.

Verbatim wording from spec-analyst is recorded as D-015 in
`tasks/spec-decisions.md` (separate commit).
2026-05-23 15:52:47 +02:00
knacky
7f4ad85a68 feat(backend): add local auth + group-based RBAC matching F11 (B0.6)
- Permission enum + GroupName enum + GROUP_PERMISSIONS mapping mirror
  the F11 matrix in code (verifiable against the spec table in tests).
- @require_perm decorator: 401 on anonymous, 403 on missing permission,
  passes through otherwise. Pure-function user_has() for unit-testing.
- AuthUser (Flask-Login wrapper) resolves the permission set from a
  User's groups; load_user is the Flask-Login user_loader.
- bcrypt password hashing helpers (12 rounds by default, configurable).
- SOC opaque token (D-006): secrets.token_urlsafe(32), bcrypt-hashed at
  rest, plain value returned once at creation and never re-displayable.
- Group-based RBAC from day one (D-003) — Keycloak OIDC in v2 maps onto
  the same group model.
2026-05-21 20:36:03 +02:00