Test Cases: Permission Assignment & Boundaries
Module: RBAC - Permission Assignment
Backend Endpoints:
GET /api/admin/permissions— list all permissions grouped by modulePUT /api/admin/roles/:roleId/permissions— full replacement of a role's permission setPUT /api/membership/members/:id/roles— assign roles to a member (full replacement)
Frontend Page: frontend/pwa-app/src/pages/AdminRolesPermissionsPage.tsx
MODULE:ACTION:resource — e.g. FINANCE:VIEW:invoices, EVENTS:CREATE:events. There are 38 permission strings across 6 modules (MEMBERS, FINANCE, EVENTS, COMMUNICATIONS, ORGANIZATION, ADMIN). The ADMIN role holds all 38 via wildcard expansion.
Permission Catalog (38 permissions)
| Module | Permission keys |
|---|---|
| MEMBERS | VIEW:members, CREATE:members, EDIT:members, DELETE:members, VIEW:prospects, EDIT:prospects, VIEW:invitations, CREATE:invitations, CREATE:import |
| FINANCE | VIEW:dashboard, VIEW:invoices, CREATE:invoices, EDIT:invoices, VIEW:payments, CREATE:payments, EDIT:payments, VIEW:dues, EDIT:dues, VIEW:donations, VIEW:bank-accounts, EDIT:bank-accounts |
| EVENTS | VIEW:events, CREATE:events, EDIT:events, DELETE:events, VIEW:registrations, EDIT:registrations |
| COMMUNICATIONS | VIEW:broadcasts, CREATE:broadcasts, EDIT:broadcasts, DELETE:broadcasts |
| ORGANIZATION | VIEW:settings, EDIT:settings, VIEW:workflows, EDIT:workflows |
| ADMIN | VIEW:dashboard, VIEW:roles, EDIT:roles, VIEW:audit-log, VIEW:content, EDIT:content |
Each row's keys are prefixed by the module — e.g. the MEMBERS row's first key is MEMBERS:VIEW:members.
Per-Role Permission Sets (system roles)
| Role | Granted permissions (summary) |
|---|---|
ADMIN | All 38 (via MODULE:*:* wildcards). |
OFFICER | MEMBERS:VIEW:members, EVENTS:VIEW:events, EVENTS:VIEW:registrations, FINANCE:VIEW:bank-accounts, ADMIN:VIEW:dashboard |
FINANCE_MANAGER | All FINANCE:* (dashboard, invoices C/V/E, payments C/V/E, dues V/E, donations, bank-accounts V/E) + MEMBERS:VIEW:members, EVENTS:VIEW:events, EVENTS:VIEW:registrations, ADMIN:VIEW:dashboard |
EVENT_MANAGER | All EVENTS:* + all COMMUNICATIONS:* + MEMBERS:VIEW:members, MEMBERS:VIEW:prospects, ADMIN:VIEW:dashboard |
MEMBERSHIP_MANAGER | MEMBERS: view/create/edit members, view/edit prospects, view/create invitations, create import + EVENTS:VIEW:events/registrations, ORGANIZATION:VIEW:workflows, ADMIN:VIEW:dashboard |
COMMUNICATIONS_MANAGER | All COMMUNICATIONS:* + MEMBERS:VIEW:members, EVENTS:VIEW:events/registrations, ADMIN:VIEW:dashboard |
MEMBER / PENDING_MEMBER / EXPIRED_MEMBER / GUEST | Only EVENTS:VIEW:events. (Member directory access is granted by the requireApprovedMember guard, not by a permission key.) |
Test Cases
TC-RBAC-016: View All Permissions
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as ADMIN. Open Settings → Roles & Permissions. Select any role. | Detail panel shows module tabs. |
| 2 | Click through the module tabs: Members, Finance, Events, Communications, Organization, Admin. | Each tab shows a matrix of resources × actions (View/Create/Edit/Delete). |
| 3 | API: GET /api/admin/permissions as ADMIN. | 200 with { modules: [ { module, permissions: [...] } ] } — 38 permissions total across 6 modules. |
TC-RBAC-017: Grant a Permission to a Role
| Step | Action | Expected Result |
|---|---|---|
| 1 | Select FINANCE_MANAGER. Open the Members tab. | Matrix shown; only VIEW:members currently checked. |
| 2 | Check EDIT:members. Click "Save Changes". | Calls PUT /api/admin/roles/:roleId/permissions with the full new set. "Save Changes" disappears. |
| 3 | Reload the page; reselect FINANCE_MANAGER → Members tab. | MEMBERS:EDIT:members persists checked. |
| 4 | (Restore) Uncheck EDIT:members, Save Changes. | Reverted. |
Tip: "Grant All" / "Revoke All" buttons toggle every permission in the current module tab.
TC-RBAC-018: Revoke a Permission from a Role
| Step | Action | Expected Result |
|---|---|---|
| 1 | Select EVENT_MANAGER → Events tab. Uncheck DELETE:events. Save Changes. | Permission removed; persists on reload. |
| 2 | Verify effect: a user with EVENT_MANAGER calling DELETE /api/events/admin/events/:id now gets 403. | Action blocked. (Restore the permission afterward.) |
TC-RBAC-019: Assign a Role to a Member (UI)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Go to All Members → click Maria Cruz → Actions ▾ → Manage Roles. | "Manage Roles" modal with 6 built-in staff-role cards. |
| 2 | Check Events Manager (EVENT_MANAGER). Click "Save Roles". | Calls PUT /api/membership/members/:id/roles with ["EVENT_MANAGER"] (MEMBER auto-retained). Member chips show MEMBER + EVENT_MANAGER. |
TC-RBAC-020: Remove a Role from a Member
| Step | Action | Expected Result |
|---|---|---|
| 1 | Maria Cruz → Manage Roles → uncheck Events Manager → Save Roles. | PUT with ["MEMBER"]. Only MEMBER remains. |
| 2 | Maria refreshes / re-logs in. | Event-management sidebar items gone (allow for the 5-min permission cache / token refresh). |
TC-RBAC-021: Assign Multiple Roles
| Step | Action | Expected Result |
|---|---|---|
| 1 | Maria Cruz → Manage Roles → check Events Manager + Communications → Save Roles. | PUT with ["EVENT_MANAGER","COMMUNICATIONS_MANAGER"]; member has the union of both permission sets (+ MEMBER). |
TC-RBAC-022: Non-Admin Cannot Open Manage Roles
| Step | Action | Expected Result |
|---|---|---|
| 1 | Login as Elena Torres (MEMBERSHIP_MANAGER). Open All Members → a member → Actions ▾. | The "Manage Roles" item is not shown (requires ADMIN's canEditMember). |
| 2 | API: PUT /api/membership/members/:id/roles as MEMBERSHIP_MANAGER. | Status 403 (lacks ADMIN:EDIT:roles). |
API Test Cases
TC-RBAC-API-007: GET /api/admin/permissions
Expected: 200 OK — { "modules": [ { "module": "FINANCE", "permissions": [ { "id", "action": "VIEW", "resource": "invoices", "description" }, ... ] }, ... ] }.
TC-RBAC-API-008: PUT /api/admin/roles/:roleId/permissions
Request: PUT /api/admin/roles/<FINANCE_MANAGER id>/permissions
{ "permissions": ["FINANCE:VIEW:dashboard", "FINANCE:VIEW:invoices", "FINANCE:VIEW:payments"] }
Expected: 200 OK — replaces the role's full permission set (this example would narrow FINANCE_MANAGER to read-only; don't run against the live role without restoring). Invalidates the permission cache.
TC-RBAC-API-009: PUT /api/membership/members/:id/roles
Request: PUT /api/membership/members/<memberId>/roles
{ "roles": ["MEMBER", "EVENT_MANAGER"] }
Expected: 200 OK — returns the updated member; both Member.roles and the RoleAssignment table are updated in one transaction. Method is PUT (not POST).
TC-RBAC-API-010: GET /api/admin/roles/:roleId/users
GET /api/admin/roles/<FINANCE_MANAGER id>/users → 200 OK { "users": [ { "id", "email", "firstName", "lastName", "assignedAt" } ] }.