← All manuals Operator Guide for this screen IM2

Permissions

Permissions
Technical Spec Ver 2 Updated 2026-09-06 Matches the live screen

Permissions — Technical Spec

app/im2.py, template app/templates/perms.html. Reached from /settings ("Open Permissions"); there is no nav link.

Routes

MethodPathAuthNotes
GET/permsrole == 'admin' (403 "Admins only")Renders both matrices.
POST/api/permsadminkind (action\field), key, role, allowed (1/true/yes/on = on). Returns the resulting state.

Tables

Code definitions

Hard-coded guards (save_perms)

Caching

_PERM_CACHE and _ACTION_CACHE, PERM_TTL = 60 seconds, keyed by role / action key. save_perms clears both — in its own process only. Under more than one uvicorn worker or gunicorn child, other workers keep serving stale permissions for up to 60 s; that is the "within a minute" the operator page promises. The cache exists because editable_fields() is called once per field per row during a bulk upload.

Known limits / defects

  1. The GET /perms render for actions computes each cell as "any row exists for this key? then table membership, else coded default". Correct, but note the consequence: with rows present, an action with zero roles ticked other than admin is indistinguishable in storage from "defaults" only until the first toggle writes the defaults out.
  2. no_cost_expected and no_cost_reason are offered in the field matrix but are not in WH_FIELDS; ticking them has no effect on the Item Maintenance grid's editable set.
  3. No CSRF token on POST /api/perms (cookie auth + samesite=lax only).
  4. There is no "reset to defaults" action: once a key has rows, deleting the last non-admin row leaves an explicit table state, not a fall-back to ACTION_DEFAULTS.
  5. Permissions are per-role only. There is no per-user override anywhere in the build.

Permission gate (added 2026-09-06)

Every route in ro.py now depends on ro_user() instead of current_user directly. ro_user() calls require_action(user, "ro_view"), so revoking View Requirements Orders screens on the Field Permissions screen actually blocks the RO screens (403) — before this the key existed and nothing read it. All four roles hold ro_view by default, so nothing changed for anyone until someone revokes it. Pick and Rush screens live in ro_pick.py / rush.py and keep their own ro_write / rush_* checks.