URD: Permissions
| Module | CORE-02 | Version | v0.4 |
|---|---|---|---|
| Status | Built | Date | 2026-05-29 |
1. Purpose
Define how access to features and data is controlled. Permissions lets administrators assign roles to users, grant permissions to those roles, scope roles to a specific organization or merchant, and have every query automatically filtered to what the requesting user is allowed to see — enforced through a Casbin priority-based RBAC model with per-merchant domains.
2. Scope
| Included | Excluded |
|---|---|
| Eight fixed system roles + priority hierarchy | Wildcard permissions |
| System-role bypass (Super Admin / Admin / Operator) | Permission categories / UI grouping |
| Automatic role-based data filtering | Time- or shift-based permissions |
| Custom role creation with priority and scope | Permission audit logging |
| Permission catalog (create / update / delete) | Role templates / bundles |
| Grant / revoke permissions to roles | Per-merchant active-role switching |
| Grant / revoke roles to users | Technical API specifications (see developer docs) |
| Effective-permission query (direct + inherited) | |
| Per-organization & per-merchant scoping | |
| Privilege-escalation guard |
3. Definitions
| Term | Definition |
|---|---|
| Role | A named access level with a numeric priority. SYSTEM (fixed) or CUSTOM (user-created). |
| Permission | A named action on a resource, identified by a globally unique code. |
| Grant (policy) | A record granting a permission to a role or directly to a user. |
| Membership (group) | A record linking a user to a role, a user to an org/merchant, or a role to an org/merchant. |
| Effective permissions | The union of all permissions a user holds: direct grants + permissions inherited through roles. |
| Scoped role | A custom role bound to a specific organization or merchant, limiting where it applies. |
| Domain | The merchant a grant applies within, selected per request by the active-merchant header. |
| System-role bypass | Super Admin, Admin, and Operator skip all data filtering and hold every permission. |
| HQ-owner expansion | An Owner at a head-quarter merchant automatically reaches every sibling merchant of that organizer. |
| Privilege escalation | Managing a role at or above the actor's own priority — always blocked. |
4. Conceptual Model
Conceptual only — the full policy data model lives in the developer RBAC docs.
5. Functional Requirements
One table per functional area.
<AREA>codes match the test-case IDs. Priority = MoSCoW (Must / Should / Could / Won't).
5.1 Fixed Roles (ROLE)
| ID | P | Requirement |
|---|---|---|
| URD-ROLE-001 | M | Provide eight fixed roles seeded at startup: Super Admin, Admin, Operator, Owner, Cashier, Employee, Customer, Guest |
| URD-ROLE-002 | M | Fixed (SYSTEM) roles cannot be modified or deleted |
| URD-ROLE-003 | M | Each role has a numeric priority that sets its place in the hierarchy |
| URD-ROLE-004 | M | Super Admin, Admin, and Operator bypass all data filtering and hold every permission |
| URD-ROLE-005 | M | Owner sees only their own organization and its merchants |
| URD-ROLE-006 | M | Employee (and Cashier) see only merchants they are assigned to |
| URD-ROLE-007 | M | Every list and count operation is filtered by the requesting user's scope |
| URD-ROLE-008 | M | Automatic filtering cannot be overridden by a user-supplied filter or direct ID access |
| URD-ROLE-009 | S | An Owner at a head-quarter merchant reaches every sibling merchant of that organizer |
5.2 Custom Roles (CROLE)
| ID | P | Requirement |
|---|---|---|
| URD-CROLE-001 | M | Authorized users can create custom roles with an i18n name, priority, and optional scope |
| URD-CROLE-002 | M | Role identifier is auto-generated from priority + name and is unique within its scope |
| URD-CROLE-003 | M | A new role's priority must be strictly lower than the creator's own highest priority |
| URD-CROLE-004 | M | Custom roles can be scoped to a specific organization or merchant |
| URD-CROLE-005 | M | Custom roles can be updated (name, description, priority) |
| URD-CROLE-006 | M | A role cannot be deleted while users are still assigned to it |
| URD-CROLE-007 | M | Deleting a role cascade-removes its permission grants and scope links |
| URD-CROLE-008 | S | An Owner can create roles scoped only to their own organization or merchants |
5.3 Permission Catalog (PERM)
| ID | P | Requirement |
|---|---|---|
| URD-PERM-001 | M | Admin can create permissions with a unique code, action, scope, and subject |
| URD-PERM-002 | M | Permission code must be globally unique |
| URD-PERM-003 | M | Permission name and description support i18n |
| URD-PERM-004 | M | Admin can update and delete permissions |
| URD-PERM-005 | M | A permission with active grants cannot be deleted |
5.4 Grant / Revoke (GRANT)
| ID | P | Requirement |
|---|---|---|
| URD-GRANT-001 | M | Admin can grant one or more permissions to a role |
| URD-GRANT-002 | M | Admin can revoke one or more permissions from a role |
| URD-GRANT-003 | M | Granting an already-granted permission is idempotent (skipped, not duplicated) |
| URD-GRANT-004 | M | Admin can grant and revoke roles to/from users |
| URD-GRANT-005 | M | Privilege-escalation guard is enforced on every grant/revoke operation |
| URD-GRANT-006 | M | Admin can view all permissions granted to a role |
| URD-GRANT-007 | M | Admin can view all users assigned to a role and all roles assigned to a user |
5.5 Effective Permissions & Scope (EFF)
| ID | P | Requirement |
|---|---|---|
| URD-EFF-001 | M | Compute effective permissions as the union of direct + role-inherited grants (deduplicated) |
| URD-EFF-002 | S | The query supports a mode filter: direct, inherit, or both |
| URD-EFF-003 | M | Retrieve all organizations and all merchants a user belongs to |
| URD-EFF-004 | M | A grant resolves only within the active merchant domain chosen per request |
6. Acceptance Criteria
AC-ROLE-01: Data filtering by role
| Given | When | Then |
|---|---|---|
| Users with different roles | They hit the same endpoint | Super Admin/Admin/Operator see all; Owner sees own org; Employee/Cashier see assigned merchants |
| Any list/count query | Run by a scoped user | Filtering is automatic and cannot be overridden |
| A scoped user supplies a wider filter or a foreign ID | Request is made | The filter is ignored / access denied |
AC-ROLE-02: Fixed-role protection
| Given | When | Then |
|---|---|---|
| Any of the eight system roles | User tries to modify or delete it | Operation rejected |
| A fixed role's priority | User tries to change it | Rejected — priorities are immutable |
AC-CROLE-01: Custom role creation
| Given | When | Then |
|---|---|---|
| Admin or Owner | Creates a role with priority + name | Role created with type CUSTOM, identifier auto-generated |
| Priority ≥ creator's own | Creation attempted | Rejected (privilege escalation) |
| Org/merchant scope provided | Role created | Scope link created |
| Same priority + name in same scope | Creation attempted | Rejected (identifier collision) |
AC-GRANT-01: Permission grant
| Given | When | Then |
|---|---|---|
| Role + permission IDs | Admin grants | Grant records created, count returned |
| Permission already granted | Admin grants again | Skipped (idempotent), skip count returned |
| Actor priority ≤ role priority | Admin grants | Rejected (privilege escalation) |
AC-EFF-01: Effective permissions
| Given | When | Then |
|---|---|---|
| User with roles + direct grants | Effective query | Union of direct + inherited, deduplicated |
| Mode = direct | Query | Only direct grants returned |
| Mode = inherit | Query | Only role-inherited grants returned |
AC-CROLE-02: Role deletion
| Given | When | Then |
|---|---|---|
| Custom role with no users | Admin deletes | Role + grants + scope links soft-deleted |
| Custom role with assigned users | Admin deletes | Blocked — must unassign users first |
| System role | Admin deletes | Rejected — fixed roles are immutable |
7. Constraints & Non-Goals
Constraints
| ID | Constraint |
|---|---|
| C-01 | Eight fixed roles are seeded at startup and are immutable |
| C-02 | No one can create or manage a role at or above their own priority |
| C-03 | Permission codes are globally unique |
| C-04 | Grant operations are idempotent |
| C-05 | All records are soft-deleted, never physically removed |
| C-06 | The session token is stateless — role/permission changes take effect on next sign-in |
| C-07 | All operations require authentication |
| C-08 | A grant resolves within one active merchant domain per request |
Non-Goals
- Wildcard permissions (e.g.
sales.*) - Permission categories and UI grouping
- Time- or shift-based permissions
- Permission audit log
- Role templates / pre-configured bundles
- Per-merchant active-role switching
8. Version History
| Date | Author | Description | Ver |
|---|---|---|---|
| 2026-02-26 | P. Do - Product Owner | Initial user stories | v0.1 |
| 2026-04-16 | Product | Restructured to URD format | v0.3 |
| 2026-05-29 | Product | Migrated to module-docs convention; reconciled with Casbin per-merchant RBAC (8 roles, priorities, domains); re-keyed areas (ROLE/CROLE/PERM/GRANT/EFF) | v0.4 |