LIVE · localhost:8001

User & Access Management — Campus+

A single, point-and-click console to manage every user's access across Campus+ — assign roles, grant or revoke individual pages, and audit every change. Built on the existing custom RBAC without touching roles, the sidebar engine, or the mobile app. Every screenshot below is the live local Docker build running on real production data.

Scope · Per-user permissions + roles
Gated by · manage-access (User Admin)
Catalog · 194 permissions · 34 roles
Stack · Django · base_app

How access works

A user's effective access is resolved by a single source of truth shared by the page gate, the sidebar, and this module — so they can never disagree. Precedence, highest first:

superuser (bypass) revoke (per-user deny) grant — role ∪ direct

Roles (membership)

Assign a whole role like Venue Admin in one click — the user inherits all its pages and the role reaches the mobile app, which gates features by role name.

Direct grants

Give one user a single extra page without assigning a role — stored on the user, affecting nobody else.

Revokes (surgical deny)

Remove one page from one user even when a role grants it — the role stays intact for everyone else.

Audit

Every grant, revoke, role change and undo is written to an immutable trail with actor, time and reason.

ConceptWhere it livesAffects the mobile app?
Role membershipUserRecord.user_groupsYes — app reads role names
Direct grantUserRecord.permissionsNo — web only
RevokeUserPermissionRevoke (new)No — web only
AuditAccessAuditLog (new)
Only two new tables were added; everything else reuses the existing model. With zero revokes, the resolver is byte-identical to the old behaviour — so shipping this changed nothing until an admin used the screen.

1 Users directory

/users/access/users/
User AdminSuperuser

The entry point — a searchable directory of every user with their role chips, status and a one-click Manage access. Search by name, email or roll number; filter by active / blocked.

Users directory
Every user with role chips and status; the whole module is gated by the manage-access permission.

2 Access editor — the whole screen

/users/access/users/<id>/
User Admin

One page to manage a user's entire access. Three zones: Roles (tiles you click to grant a whole role), Add a specific permission (search instead of scrolling), and What this user can access (a clean module→page tree with one-click remove). The live "can access" counter and a Recent-changes feed sit at the top-right.

Full access editor
The full editor for RAGUL ADHITHYA M — role tiles (Student, Whatsapp Admin, User Admin assigned), search, and the access tree.

4 Choose specific pages

role popup → Choose specific…

Expands the role into its individual pages, each a toggle reflecting the user's current state. Flip only the ones you want — they're granted directly to this user, leaving the role itself untouched for everyone else.

Choose specific pages
The Department Head role expanded into its pages — toggle individual ones for just this user.

6 What this user can access

access editor → lower panel

A clean tree grouped by module. Each module folds; under it every page is listed with its name, codename and a source badgerole (from a role), direct (granted to this user), revoked. One-click Remove on any page.

Current access tree
Module → page tree with source badges; removing a role-granted page only blocks it for this user.

7 Audit log

/users/access/audit/
User Admin

An immutable, filterable trail of every access change — grants, revokes, role assigns/removes and undos — each with the actor, timestamp, the permission/role, and a reason. Filter by action type and paginate.

Audit log
Every access change recorded with actor, time and reason — nothing changes silently.

What was built & how it stays safe

Bugs found & fixed during this build (verified live in a headless browser): a sidebar-grouping collision that blanked the menu for superusers; an invisible popup button caused by CSS variables scoped outside the modal; a role tile showing "+" when fully granted; and "grant whole role" not clearing pre-existing revokes — all fixed.
No existing feature was touched: roles, the permission_objects.py sync, the legacy type field and the mobile API are all untouched. The only edits to existing files add a revoke-aware check that is a no-op until a revoke exists.