Test Plan: Role-Based Access Control (RBAC)
♻️ Reconciled against current code — 2026-07-01. Rewritten to match the live
dev branch. Key corrections vs. the prior version: 10 system roles (all non-deletable); permission strings use the MODULE:ACTION:resource format (not dot-notation); member role assignment is PUT (not POST); the Roles & Permissions page is reached via Settings (no direct sidebar link); role codes are FINANCE_MANAGER / EVENT_MANAGER (not TREASURER / EVENTS_MANAGER); and custom roles cannot be assigned to a member through the UI. See the green banner below for what's new this cycle.
🆕 New on stage — 2026-06-19 → 2026-07-01.
- PII + check-in-code redaction on guest-registration responses (#486, security, Jun 19 release):
GET /api/events/:eventId/registrations/guestsnow redactsattendeeEmail/memberEmail/guestEmail/checkInCode/displayTicketCode/invoice/invitedBy.emailfor any row that is neither the caller's own registration nor viewed by a holder ofEVENTS:VIEW:registrations. Privileged callers (ADMIN, EVENT_MANAGER, etc.) still see the full row. See TC-RBAC-004. - Bank-accounts view for ADMIN/OFFICER/FINANCE_MANAGER (#363), landed as an additive backfill (#372):
FINANCE:VIEW:bank-accountsis now granted to ADMIN, OFFICER (read-only), and FINANCE_MANAGER viabackfillRBACPermissionsForAllTenantsrun on every startup/deploy — no destructive reseed, no per-env SQL ticket. Existing custom role/permission grants are preserved (additive-onlycreateMany({{ skipDuplicates: true }}), never adeleteMany). A manual scriptscripts/sync-bank-accounts-permission.tsalso exists but is not auto-run and must NOT be run on prod without review (it deletes+recreates system-role permission joins). See TC-RBAC-004. - Platform Check-in gated by
PLATFORM_SUPPORT(#376/#377, Jun 19 release): the admin portal's Tenant Detail → Events tab and the Event Attendees page (/tenants/:id/events/:eventId/attendees) are read-only cross-tenant surfaces gated byrequirePlatformSupport(allows SUPER_ADMIN or PLATFORM_SUPPORT). A platform user with neither role gets403from the underlying API — the admin-portal client only auto-acts on401(silent refresh), so a403renders as an in-page error and does not log the operator out. See TC-RBAC-004. - Member-role allowlist + admin-safety guards (#389–#395, Jun 19 release):
PUT /api/membership/members/:id/rolesnow validates each role against an explicit enum (TENANT_ROLE_CODES) — platform codes (SUPER_ADMIN,PLATFORM_*) and any arbitrary string are rejected400(#390). Also added: schema-route guards (#389), custom-field validation on member save (#392), an atomic admin email change with no half-applied state (#393), orphan-role prune (#394), and role-assignment reconciliation (#395). See TC-RBAC-004.
1. Introduction
This document outlines the test strategy, scope, and approach for the Role-Based Access Control (RBAC) module of MemberVu. The module manages roles, permissions, and access enforcement across the organization (tenant) portal.
2. Scope
- Role Management: Viewing system roles; creating / editing / duplicating / deleting custom roles.
- Permission Management: Granting and revoking permissions on a role via the permission matrix.
- Role Assignment: Assigning built-in staff roles to members.
- Access Control: Verifying UI navigation and API permissions are enforced per role.
- Role Audit: Tracking role changes via the audit log.
3. Test Strategy
- UI / E2E: Role CRUD and access control via the org portal.
- API: Direct endpoint permission enforcement (403 / 401 matrix).
- Security: Verify unauthorized access is blocked (fail-closed on DB error).
4. Environment
| Concern | Location (current code) |
|---|---|
| Roles & Permissions page | Route /admin/roles. No direct sidebar link — reach it via sidebar Organization > Settings (/admin/settings) then the "Roles & Permissions" card. Guard: ADMIN or SUPER_ADMIN. |
| Assign roles to a member | Route /admin/members → click a member → Actions ▾ > Manage Roles modal (visible to ADMIN only). |
| Roles API | /api/admin/roles family (legacy alias /admin/roles); permissions at GET /api/admin/permissions. |
| Member role-assignment API | PUT /api/membership/members/:id/roles — body { "roles": ["CODE", ...] } (full replacement; MEMBER auto-appended server-side). |
| Enforcement code | auth-service/src/rbac.ts (requirePermission, SUPER_ADMIN bypass, 5-min permission cache) + auth-service/src/services/rbac/ (role/permission seed). |
| Frontend page | frontend/pwa-app/src/pages/AdminRolesPermissionsPage.tsx + components/roles/. |
5. Predefined Roles (10 system roles)
All ten ship as system roles (isSystem: true) and cannot be deleted. Their name / description / colour and permission assignments can be edited; the role code is immutable. Permissions use the MODULE:ACTION:resource format.
| Role Code | Display Name | Description | System? |
|---|---|---|---|
ADMIN | Administrator | Full administrative access (all 38 permissions via wildcard) | Yes |
OFFICER | Officer | Board officer — read-only visibility into org health | Yes |
FINANCE_MANAGER | Treasurer | Manages invoices, payments, dues, bank accounts | Yes |
EVENT_MANAGER | Events Manager | Creates/manages events, registrations, check-in; can view+send broadcasts | Yes |
MEMBERSHIP_MANAGER | Membership Manager | Member applications, approvals, invitations, import | Yes |
COMMUNICATIONS_MANAGER | Communications Manager | Announcements and email broadcasts | Yes |
MEMBER | Member | Standard active-member access | Yes |
PENDING_MEMBER | Pending Member | Awaiting approval — limited access | Yes |
EXPIRED_MEMBER | Expired Member | Membership lapsed — renewal required | Yes |
GUEST | Guest | Non-member — public features only | Yes |
Note: SUPER_ADMIN is a platform role (not a tenant role). It lives in platformRoles, not roles, and bypasses all org permission checks. Platform/SuperAdmin actions belong in the separate admin portal (stg-admin.membervu.com), not the org portal.
6. Test Data (RCME seed)
| User | Email / Password | Role code(s) |
|---|---|---|
| Admin | admin@rcme.membervu.com / Admin123! | ADMIN |
| Juan Santos | officer@rcme.membervu.com / Officer123! | OFFICER |
| Ana Reyes | treasurer@demo.membervu.com / Treasurer123! | FINANCE_MANAGER |
| Carlos Garcia | events@demo.membervu.com / Events123! | EVENT_MANAGER |
| Elena Torres | membershipchair@demo.membervu.com / Membership123! | MEMBERSHIP_MANAGER |
| Seung Eun Lee | comms@demo.membervu.com / Comms123! | COMMUNICATIONS_MANAGER |
| Maria Cruz | testmember@rcme.membervu.com / Member123! | MEMBER |
| Antonio Dela Cruz | expired@demo.membervu.com / Expired123! | EXPIRED_MEMBER |
| Roberto Mendoza | guest@demo.membervu.com / Guest123! | GUEST |
| SuperAdmin | superadmin@zeniark.com / SuperAdmin123! | platformRoles: SUPER_ADMIN |
No seeded
PENDING_MEMBER user. pending@demo.membervu.com (Miguel Fernandez) is an applied prospect with role GUEST + status GUEST_ATTENDEE — not a PENDING_MEMBER. To test PENDING_MEMBER access you must drive a member into that state via the application flow.
7. Test Deliverables
- TC-RBAC-001 — Role Management (CRUD) · detailed
- TC-RBAC-002 — Permission Assignment & Boundaries · detailed
- TC-RBAC-003 — Access Control Enforcement · detailed
- TC-RBAC-004 — 🆕 PII Redaction & Permission Boundaries (guest-registration redaction #486, PLATFORM_SUPPORT gating #376/#377, role allowlist #389–#395, bank-accounts view #363/#372) · detailed
8. Dependencies
- Authentication module (JWT with
roles+platformRolesclaims). - Member module (role assignment to members).
- Events module (guest-registration roster — redaction surface for #486).
9. Risk Areas
- Permission cache staleness — 5-minute TTL; invalidated on
PUT .../permissionsbut role assignment changes need token refresh / re-login to take effect. - SUPER_ADMIN bypass / privilege escalation.
- Cross-tenant isolation.
- Admin-lockout guard when editing the ADMIN role's own permissions.
- PII redaction correctness on the guest-registration roster (#486) — a regression here leaks another attendee's email/check-in code to a plain MEMBER.
- No seeded platform user holds ONLY a non-
PLATFORM_SUPPORTplatform role — testing the #376/#377 denial path requires creating one via the admin portal's Users → New (see TC-RBAC-004).