SECURITY FEATURE
RBAC
Semitexa separates coarse-grained capabilities from fine-grained permission slugs so modules can extend authorization without coupling themselves to one storage model.
Feature Guide
A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.
What this does
Semitexa RBAC is intentionally hybrid: coarse-grained capabilities can be represented as internal bitmask grants for very fast broad checks, while business-facing permission slugs such as products.write or settings.smtp.update handle exact, human-readable authorization decisions.
How it works
The payload access policy can declare both #[RequiresCapability] and #[RequiresPermission]. Authorizer evaluates them in order: authentication first, then capability checks, then slug checks. CapabilityRegistry maps Capability enum cases to bit positions inside integer segments, while SubjectGrantResolver builds the current subject grant set and asks a module-level PermissionProviderInterface for the user's slug permissions. That means the RBAC core owns the evaluation pipeline, but storage and permission catalogs stay owned by the modules that actually know the business domain.
Why it matters
This split avoids two common failures. A pure slug model becomes noisy for broad platform-level rights, while a pure bitmask model becomes opaque for audits and product-specific rules. The hybrid model keeps the hot path compact and machine-friendly, but still gives reviewers and module authors explicit permission names and extension points.
Key concepts
- #[RequiresCapability]
- Declares a coarse-grained code-level capability check that is evaluated before slug permissions.
- CapabilityRegistry
- Maps Capability enum cases to bitmask segment and bit positions so capability checks can stay fast and internal.
- #[RequiresPermission]
- Declares an exact slug-based permission such as users.manage or settings.smtp.update.
- PermissionProviderInterface
- Contract implemented by domain modules to supply the current user's permission slugs without coupling RBAC to a specific storage backend.
- SubjectGrantResolver
- Builds the authenticated subject's combined grant set and caches it per request before Authorizer evaluates policy requirements.
- module-owned permission catalog
- Each module can define and expose its own permission slugs, roles, and assignment rules while the shared authorization pipeline keeps one evaluation model.
RBAC
Hybrid grant model
Capabilities cover broad platform rights, permission slugs cover exact business actions, and any module can add its own permission list by implementing the RBAC provider contract.
#[RequiresCapability(AdminCapability::BackofficeAccess)]
#[RequiresPermission('products.write')]
#[AsProtectedPayload(path: '/admin/products/{id}', methods: ['PUT'])]
class UpdateProductPayload { ... }
// A domain module supplies slug permissions through PermissionProviderInterface.
| Permission | Admin | Editor | Viewer |
|---|---|---|---|
products.read
|
Granted | Granted | Granted |
products.write
|
Granted | Granted | Denied |
users.manage
|
Granted | Denied | Denied |
orders.manage
|
Granted | Denied | Denied |
settings.manage
|
Granted | Denied | Denied |
Verified against Semitexa Ultimate 2026.09.19.1020
RBAC
Hybrid RBAC: bitmask-backed capabilities for broad checks, slug permissions for exact business rules, and module-owned catalogs behind one authorizer.
How it works
Semitexa separates two authorization layers. Capabilities are coarse-grained platform rights stored as bitmasks, checked with #[RequiresCapability]. Permission slugs are fine-grained business rules registered by any module implementing PermissionProviderInterface and checked with #[RequiresPermission]. The Authorizer resolves both through the SubjectGrantResolver so handlers never touch the grant resolution logic directly.
Why this matters
A single permission model that forces every module to share one storage schema becomes a coupling problem as the application grows. Separating capabilities from slug permissions lets each module own its permission catalog, extend the authorization surface without modifying core tables, and still share one authorizer that keeps the check consistent across the whole application.
How it works
The payload access policy can declare both #[RequiresCapability] and #[RequiresPermission]. Authorizer evaluates them in order: authentication first, then capability checks, then slug checks. CapabilityRegistry maps Capability enum cases to bit positions inside integer segments, while SubjectGrantResolver builds the current subject grant set and asks a module-level PermissionProviderInterface for the user's slug permissions. That means the RBAC core owns the evaluation pipeline, but storage and permission catalogs stay owned by the modules that actually know the business domain.
Why it matters
This split avoids two common failures. A pure slug model becomes noisy for broad platform-level rights, while a pure bitmask model becomes opaque for audits and product-specific rules. The hybrid model keeps the hot path compact and machine-friendly, but still gives reviewers and module authors explicit permission names and extension points.
Key concepts
- #[RequiresCapability]
- Declares a coarse-grained code-level capability check that is evaluated before slug permissions.
- CapabilityRegistry
- Maps Capability enum cases to bitmask segment and bit positions so capability checks can stay fast and internal.
- #[RequiresPermission]
- Declares an exact slug-based permission such as users.manage or settings.smtp.update.
- PermissionProviderInterface
- Contract implemented by domain modules to supply the current user's permission slugs without coupling RBAC to a specific storage backend.
- SubjectGrantResolver
- Builds the authenticated subject's combined grant set and caches it per request before Authorizer evaluates policy requirements.
- module-owned permission catalog
- Each module can define and expose its own permission slugs, roles, and assignment rules while the shared authorization pipeline keeps one evaluation model.