Authorization and Entitlements
This guide explains the architectural patterns governing how users are authorized to perform actions and how feature sets are enabled across SaaS environments.
Overview
Framework M separates identity from access control through a Port/Adapter (Hexagonal) architecture. This ensures that the core business logic remains agnostic to whether you are using a simple local database check, a complex Open Policy Agent (ABAC) implementation, or a graph-based SpiceDB (ReBAC) system.
1. The Three Layers of Access
Access control is resolved through three distinct layers, each governed by its own Protocol.
| Layer | Responsibility | Protocol | Default Implementation |
|---|---|---|---|
| Authentication | "Who is this user?" | IdentityProtocol | Citadel IAM / Local JWT |
| Entitlement | "What SaaS features does this tenant pay for?" | FeatureEntitlementProtocol | Subscription Check |
| Authorization | "What can this user do with this resource?" | PermissionProtocol | DocType RBAC |
2. Stateless Policy Evaluation
The PermissionProtocol is built around a stateless request/response cycle. Instead of the permission engine fetching data from databases (which creates tight coupling), the framework provides all necessary context upfront.
The PolicyEvaluateRequest
This request contains four critical context blocks:
- Principal: The identity performing the action (User ID).
- Action: The intent (e.g.,
read,write,submit). - Resource: The entity being accessed (e.g.,
Invoice). - Attributes: Dynamic metadata (e.g., User's Department, Document Status, Tenant ID).
This stateless design allows the "Adapter" (like OPA) to make a sub-millisecond decision without secondary I/O lookups.
3. SaaS Entitlements (The Guard Rail)
Entitlements sit "above" permissions. They govern the visibility of entire feature sets before user-level roles are even checked.
Architectural Flow:
- Navigation Resolution: The
NavigationPolicyEngineasks the entitlement system: "Iswms_ai_packingenabled for this tenant?" - Pruning: If the tenant hasn't paid for the AI Pack, the entire navigation branch is pruned at the source.
- Zero-Visibility: This prevents users from seeing "Access Denied" screens for features their company hasn't purchased, providing a cleaner UI experience.
4. Port/Adapter Extensibility
Because these are defined as Protocols in framework-m-core, you can swap the engine as your application scales:
Level 1: Standard RBAC (Local)
For small to medium apps, permissions are defined in DocType.Meta. The RbacPermissionAdapter reads these rules directly from the code.
Level 2: OPA/ABAC (Decoupled)
As enterprise rules become complex (e.g., "Allow write if user is in HQ and time is 9-5"), you can swap the adapter to OpaPermissionAdapter. The rules are now managed in Rego files outside the Python codebase.
Level 3: SpiceDB/ReBAC (Relational)
For complex organizational hierarchies, you can swap to SpiceDbPermissionAdapter. It evaluates permissions by traversing relationship graphs (e.g., "User is a manager of the owner of this invoice").
5. Usage in Navigation
The Navigation Architecture uses these protocols to resolve what the user sees in the Rail & Drawer:
This multi-stage resolution ensures that the navigation manifest delivered to the browser (RFC-0010) is already pre-filtered for both SaaS plan and User roles.