Users, Roles & Permissions¶
Login accounts (users) are how people operate TimeKeeper. They are separate from team members (see Overview).
The Users view¶
Admin-only table of Username + Roles (chips), searchable by username or role.
The default admin user is hidden from the list.
Creating / editing a user:
- Username
- Password — on edit, "Password (leave blank to keep)"
- Roles — a checkbox list of available roles, each with its description.
"This is not team membership — students and mentors live under Team and have no login. Without a role a user can sign in but do nothing else."
The two built-in roles¶
Built by migration 0005_default_roles:
| Role | is_super |
Effective access |
|---|---|---|
| admin | yes | Everything, on every resource — super roles bypass per-resource checks entirely. |
| kiosk | no | Unattended check-in station: read team_members, sessions, locations, rfid_tags, settings; write team_member_sessions (check-in/out). Nothing else — users and statistics are unreachable. |
More roles can be created; each is either super or carries explicit grants.
Resources¶
Permissions are attached to resources. TimeKeeper's registry
(0001_init/up.sql):
locations · notifications · rfid_tags · sessions · session_rsvps ·
settings · statistics · team_members · team_member_sessions · users
Permission levels¶
Levels are ceiling-ordered:
A write grant satisfies a read requirement; delete satisfies both. The
effective level on a resource is the max across all of a user's roles
(user_permissions view), and a user with no roles has no permissions.
How enforcement works¶
- At login, the server resolves the user's effective permissions and bakes
them into the JWT as
"resource:level"claims. - Every mutation re-checks the claim server-side. Client-side gating (the rail, buttons) is convenience, not a security boundary.
- Because the snapshot is taken at sign-in, a role change takes effect on the user's next login — a freshly demoted admin stays admin until they re-sign-in.
Tokens expire after 7 days; sessions survive until then.
Access sanity rules¶
- Admin rail =
settings: write(whatisAdminmeans on the client). - RFID scanning UI = any permission at all (logged-in non-admin kiosk operator).
- Quick PIN is server-authoritative: resolving a PIN requires the
quick_pin_enabledsetting plus a login — the client never compares PINs.