07 — Authorization Model
Status: ✅ Draft
Prerequisite: 06 — Authentication Flow
Next: 08 — Security Architecture
Detailed design of Core RBAC — role hierarchy, permission model, role assignment rules,
/v1/rbac/checkevaluation logic, multi-scope and temporal scoping, and the Gathri exception.
1. Permission String Format and Wildcard Behaviour
Confirmed in architecture session — do not revisit.
All permissions follow resource:action notation:
volunteer:approve
volunteer:view
allocation:view
report:export
This is the same convention as AWS IAM (s3:GetObject, ec2:DescribeInstances). The RBAC Engine stores permissions as strings in this format within role definitions.
Wildcard permissions
Wildcard permissions are supported at the action level only.
| Pattern | Supported | Meaning |
|---|---|---|
volunteer:* | ✅ | All actions on the volunteer resource |
*:approve | ❌ | Not supported — resource must be explicit |
*:* | ❌ | Not supported |
Auto-grant implication: a role holding volunteer:* automatically passes any future action added to the volunteer resource without a role update. Wildcard roles are reserved for high-trust, module-admin-level assignments — not general user roles.
Module-side check: modules evaluating permissions locally must match the same wildcard logic as the engine.
function hasPermission(permissions, resource, action) {
return permissions.includes(`${resource}:${action}`)
|| permissions.includes(`${resource}:*`);
}
2. Role Levels
Every role in Core has a level that determines its scope.
| Level | Scope | Example roles |
|---|---|---|
platform | No Miqaat scope — applies across the entire platform | PLATFORM_ADMIN |
miqaat | Scoped to a specific miqaatId | VMS_Coordinator, AMS_Reviewer, Istefadah_Ops |
A role at miqaat level is always tied to one Miqaat. The same person can hold the same role in two different Miqaats — these are two separate assignments, each with its own miqaatId.
PLATFORM_ADMIN is the only platform-level role. It is documented separately in 06 — Authentication Flow, Section 7.
3. Role Hierarchy
⏳ Pending technical discussion — the design below is a starting position, not a confirmed decision.
The
parentRolefield exists in the C-010 role schema, so hierarchy is intended. But the exact inheritance direction, evaluation logic, and depth limit must be confirmed in a dedicated RBAC technical session before the engine is built. Do not treat anything in this section as final.
Roles can have a parentRole. The proposed model is that a parent role inherits all permissions of its children — it does not pass permissions down.
Miqaat_Operations_Lead ← parent
└── VMS_Coordinator ← child
└── AMS_Reviewer ← child
Under this model, Miqaat_Operations_Lead would hold all permissions of VMS_Coordinator and AMS_Reviewer combined, plus any defined directly on it. A user assigned Miqaat_Operations_Lead would not need separate child role assignments.
This direction needs to be confirmed. The alternative — children inheriting from parents — is equally valid and changes the assignment model significantly. The technical session must decide:
- Which direction does inheritance flow?
- Is inheritance resolved at check time (dynamic) or at assignment time (materialised)?
- What is the maximum supported hierarchy depth?
- Can a role have multiple parents?
- How are circular references prevented?
4. Role Assignment Rules
⏳ Pending technical discussion — the questions below must be resolved before the RBAC Engine write path is built.
Confirmed
Every role assignment and revocation fires a Core event — this is confirmed in the Event Catalog:
role.assigned(C-025) — modules that gate features on role presence subscribe to thisrole.revoked(C-026) — all modules consume this to invalidate their local permission cache
Pending confirmation
Who can assign roles?
The current assumption is only PLATFORM_ADMIN holders can assign or revoke roles. But the question is whether any delegation is needed — for example, can a Miqaat-level operations lead assign sub-roles within their own scope? This needs to be decided: fully centralised (Platform Admin only) vs limited delegation model.
Multiple roles per Miqaat — is this allowed?
Can one itsId hold VMS_Coordinator and AMS_Reviewer simultaneously in the same Miqaat? If yes, the RBAC check merges permissions across all held roles. If no, only one active role per itsId per Miqaat is permitted. This changes the engine's evaluation logic and the assignment validation.
Primary khidmat role enforcement — which engine owns it?
HR Bank has a quota check (GET /v1/hr/quota) that checks whether a primary khidmat slot is free. But it is not confirmed whether the RBAC Engine enforces this at assignment time, or whether it is purely an HR Bank and module concern. The boundary needs to be agreed.
Are role assignments always explicit? The current position is yes — no implicit grants from ITS group membership, Gathri, or jamaat. But this must be confirmed: are there any cases where ITS data should automatically trigger a role assignment in Core?
5. /v1/rbac/check Evaluation Logic
Full step-by-step evaluation for a single permission check.
Input:
X-Its-Id: ITS-1234567 (from Identity Bridge)
body.resource: "volunteer"
body.action: "approve"
body.miqaatId: "MQ-1447-KHI"
Step 1 — Load role assignments
Find all roles assigned to ITS-1234567 in MQ-1447-KHI
e.g. ["VMS_Coordinator"]
Step 2 — Check temporal validity
Is each assignment within its valid date range?
(see Section 6 — Temporal Scoping)
Drop any assignments outside their valid window.
Step 3 — Expand permissions (with hierarchy)
For each valid role:
Load the role's own permissions
Walk up parentRole chain, collect ancestor permissions
Merge into one permission set.
e.g. ["volunteer:approve", "volunteer:view", "allocation:view", ...]
Step 4 — Evaluate
Does the merged set contain "volunteer:approve"? → exact match → allowed
Does the merged set contain "volunteer:*"? → wildcard match → allowed
Neither? → denied
Step 5 — Return result
allowed: true → reasonCode: ROLE_GRANTS_ACTION, roleMatched: "VMS_Coordinator"
allowed: false → reasonCode: NO_MATCHING_ROLE
Special reason codes
reasonCode | Meaning |
|---|---|
ROLE_GRANTS_ACTION | A role held by this itsId grants this action on this resource |
NO_MATCHING_ROLE | No role held grants this action |
ROLE_SCOPE_MISMATCH | A matching role exists but is scoped to a different miqaatId |
MIQAAT_CLOSED | The Miqaat is archived — no permission checks are valid |
ROLE_EXPIRED | A matching role exists but its temporal window has passed |
6. Multi-Scope — Holding Roles Across Multiple Miqaats
A Mumin can hold roles in multiple Miqaats simultaneously. Each assignment is independent.
ITS-1234567
├── VMS_Coordinator in MQ-1447-KHI (Karachi 2026)
└── AMS_Reviewer in MQ-1446-MUM (Mumbai 2025)
Every RBAC check includes miqaatId in the request — the engine only evaluates roles assigned for that specific Miqaat. Roles in other Miqaats are invisible to the check.
GET /v1/rbac/permissions also requires miqaatId — it returns permissions for one Miqaat scope only. If a module serves users across multiple active Miqaats simultaneously, it must fetch and cache permissions per Miqaat.
7. Temporal Scoping
Role assignments can carry optional validity dates:
| Field | Description |
|---|---|
validFrom | The assignment is not active before this date. Absent = immediately active. |
validUntil | The assignment expires after this date. Absent = no expiry. |
The RBAC Engine checks validity at evaluation time (Step 2 in Section 5). An assignment outside its valid window is treated as if it does not exist — the check proceeds with the remaining valid roles.
Use cases:
- A volunteer coordinator role that is only valid during the operational phase of a Miqaat
- A temporary escalated role granted for a specific task window
- A role pre-assigned before the Miqaat begins (
validFromin the future)
Cache implication: modules with local permission caches must be aware that a cached permission set can become invalid when a role's validUntil passes — not only on a role.revoked event. Modules should factor the earliest validUntil across all roles into their cache TTL. If any role in the cached set expires before the TTL would naturally clear the cache, the module must re-fetch before that expiry.
⏳ Whether Core pushes a
role.expiredevent atvalidUntiltime (so modules can invalidate proactively) or whether modules are expected to manage this themselves — pending architecture decision.
8. The Gathri Exception
⏳ Business session required — details below are the current understanding, not a confirmed decision.
In Dawoodi Bohra Miqaat context, a Gathri is a family unit. For certain Miqaat types (particularly Ashara Mubaraka residential registrations), access and quota are managed at the Gathri level — not purely at the individual itsId level.
What this means for RBAC:
Standard RBAC checks are per-itsId. The Gathri exception arises when a permission or quota applies to the family unit and the individual's eligibility depends on the Gathri's status, not only their own.
Example: a Gathri head registers the family for residential accommodation. The Gathri holds the allocation slot. Individual family members' access to venue entry, meal passes, or scanning is derived from the Gathri's confirmed status — not from individual allocation records.
Current position: the RBAC Engine operates on itsId only. Gathri-level logic is expected to live in the module (AMS, Istefadah Registration) that understands the Gathri structure — not in Core RBAC. Core's role is to answer "can this itsId perform this action?" — not "what Gathri does this itsId belong to and what does that grant?"
However, if eligibility or allocation rules reference Gathri status, the Eligibility Engine's rule families may need a gathri rule type that calls ITS data for family membership. This is a rule configuration concern, not a Core RBAC concern.
The exact boundary — what is Core's responsibility vs the module's — must be confirmed in a dedicated business session covering the Gathri model for the specific Miqaat types where it applies.
9. Open Questions
| Question | Blocked On |
|---|---|
| Role hierarchy — which direction does inheritance flow? Parent inherits child, or child inherits parent? | RBAC technical session |
| Role hierarchy — resolved at check time (dynamic) or at assignment time (materialised)? | RBAC technical session |
| Role hierarchy — maximum depth, multiple parents, circular reference prevention | RBAC technical session |
| Role assignment — fully centralised (Platform Admin only) or limited delegation allowed? | RBAC technical session |
| Role assignment — can one itsId hold multiple roles in the same Miqaat simultaneously? | RBAC technical session |
| Role assignment — which engine enforces the one-primary-khidmat rule: RBAC or HR Bank? | RBAC technical session |
| Role assignment — are there any cases where ITS group membership implicitly grants a Core role? | RBAC technical session + ITS Technical session |
Whether Core fires a role.expired event at validUntil time | RBAC technical session |
| Gathri exception — exact boundary between Core RBAC and module responsibility | Business session (Gathri model) |
Whether Gathri is a rule family in the Eligibility Engine or a separate concept | Business session |
| Role naming conventions — is there a registry or do module teams propose names freely? | Platform Admin design session |