Authorization has no single options section that explains production behavior. Composition, persistence provider, cache infrastructure, audit backend, seeds, and policy data are the real control plane.
1. Host composition order
CoreRuntime installs a resolvable, restricted graph: real RBAC and AppScope; Neutral ABAC and ReBAC placeholders; a false-returning Feature provider; simplified DataScope; and a possible Null audit sink.
The persistence path chooses generated SqlSugar or EF Core stores, registers the audit backend, and then adds real policy evaluators and cache adapters.
// 1. Install the API-shell graph and fail-closed defaults.services.AddBitzOrcasCoreRuntime(configuration);
// 2. The public persistence extension selects ORM, audit store, generated adapters,// and Authorization infrastructure in its reviewed internal order.services.AddBitzOrcasPersistenceAdapters(configuration);
// These calls occur inside PersistenceRegistration after provider selection.services.AddBitzOrcasAuditStore(configuration);services.AddBitzOrcasGeneratedPersistenceAdapters(persistenceProvider);services.AddBitzOrcasAuthorizationPlatform();Business Hosts should call the public composition extensions, not repeat scoped evaluator registration.
2. Service inventory
| Service | CoreRuntime | Production registration | Lifetime |
|---|---|---|---|
IAuthorizationDecisionService | AuthorizationDecisionService | Same implementation with closed dependencies | Scoped |
| RBAC / AppScope | Real | Retained | Singleton |
| ABAC / ReBAC | Neutral placeholders | Real evaluators appended | Scoped |
| Feature provider | Unavailable=false | FeatureStore | Generated-adapter lifetime |
IDataScopeResolver | Simplified | Full resolver | Scoped |
| Four cache ports | Base ports | CacheStore*Cache | Singleton |
IAuthorizationAuditSink | Null default | AuditLoggerDispatcher | Singleton |
Identity Infrastructure implements the narrow IOrganizationUnitMemberRepository consumed by the full DataScope resolver. This is port collaboration, not a project reference from Authorization to Identity Infrastructure.
3. Persistence ownership
| Table | Tenant | Soft delete | Key contract |
|---|---|---|---|
SysRoleType | Global | Yes | Unique RoleType |
SysRole | Yes | Yes | Unique TenantId + Name |
SysUserRole | Yes | Yes | Unique TenantId + UserId + RoleId |
SysPermission | Global | No | Unique permission Code |
SysRoleModulePermission | Yes | Yes | Filtered unique role/user + module + permission relations |
SysAbacRule | Yes | Yes | Read by tenant + module + resource type |
SysFeatureDefinition | Global | Yes | Unique FeatureCode and global DefaultState |
SysFeatureOverride | Yes | Yes | Unique TenantId + FeatureCode, stores only OverrideState |
SysResourceRelation | Yes | Yes | Unique tenant + resource type/ID + user |
These records are owner-local persistence models, not one-for-one legacy entity/mapper copies. Compile-time Fluent Configuration consumes their table, index, and column metadata.
3.1 Stable keys
SysRole.Idis a management ID; Name is the tenant-local relation key.SysUserRole.UserIdreferences an Identity-owned subject key.SysPermission.Codeis the global permission code; Mid identifies its Menu module.- Resource relations keep external type and ID only, with no cross-context CLR navigation.
- Removing a tenant Feature override falls back to the global default without copying the definition.
4. Store roles
| Port | Responsibility | Failure contract |
|---|---|---|
IRoleStore | Manage and read role, user-role, role-permission data | FailClosed + BusinessClosedDefault |
IAbacRuleManagementStore | ABAC CRUD | Fail closed |
IAbacRuleStore | Read enabled rules for evaluation | Exception becomes evaluator Deny |
IFeatureManagementStore | Definitions and tenant override upsert | Fail closed |
IFeatureStore / provider | Read merged tenant state | Missing or failure becomes disabled / empty |
IRelationStore | Instance relation and member reads | Failure becomes ReBAC Deny |
IAuthorizationAssignmentReader | Subject-projection role and permission reads | Owner-local read port |
IAuthorizationSubjectReader | Confirm the target Identity subject | Prevent orphan and cross-tenant UserRole writes |
Management and evaluation ports are intentionally separate: management returns typed results, while security evaluation converts unavailable state into a stable denial.
5. Deterministic seeds
| Prefix | Asset | Content |
|---|---|---|
| 200 | 200-sys_role_type.csv | Global role types |
| 210 | 210-sys_role.csv | Initial tenant roles |
| 230 | 230-sys_permission.csv | Business permission catalog |
| 240 | 240-sys_role_module_permission.csv | Initial role-to-module-permission relations |
| 410 | 410-sys_feature_definition.csv | Global Feature definitions |
| 520 | 520-sys_user_role.csv | Initial user-role bindings |
Governance Generator also collects module contributions from permission catalog attributes. The generated and seeded catalogs need a union-consistency check so a code constant cannot exist without a database catalog row.
# All permission codes must be globally unique and follow the naming convention.rg -n "PermissionDefinition|public const string" \ src/Platform/Authorization/BitzOrcas.Platform.Authorization.Contracts -g '*.cs'
# A new permission or Feature needs a migration and idempotent seed coverage.rg -n "authorization\.|platform\." \ src/Platform/Authorization/BitzOrcas.Platform.Authorization.Infrastructure/Seeders/Assets -g '*.csv'6. Four independent caches
| Cache | Policy | Core key facts | Invalidation |
|---|---|---|---|
| Permission decision | Medium, 15 minutes | Full identity + resource + action hash | User, tenant |
| ReBAC relation | Short, 60 seconds | Tenant + resource + user + action | Resource |
| DataScope | Medium, 15 minutes | Tenant + user + office | User |
| Feature | Medium, 15 minutes | Tenant + FeatureCode | Tenant |
Permission decision get/set is best-effort at both adapter and service layers. Cache invalidation calls directly remove by tag and can fail a management request.
await relations.UpsertAsync(relation, cancellationToken);
// Positive and negative ReBAC results are cached, so every relation change invalidates the resource.await reBacCache.InvalidateByResourceAsync( relation.ResourceType, relation.ResourceId, cancellationToken);
// If the relation also changes organization scope, invalidate that independent cache.await dataScopeCache.InvalidateByUserAsync( relation.TenantId, relation.UserId, cancellationToken);This is the write-side protocol to implement. The module currently has no public relation write store.
7. Global Feature invalidation
UpdateFeatureState upserts a tenant override for tenant-scoped definitions and invalidates that tenant. For a global definition it updates DefaultState and calls InvalidateAllAsync. The invalidation scope now matches the write scope. Keep a two-tenant, multi-instance convergence test so a future optimization does not reintroduce stale global values or over-invalidate tenant overrides.
8. Integration events
| Event | Changes | Key data |
|---|---|---|
RoleChangedIntegrationEvent | Created, Updated, Deleted | Event, tenant, role, type, actor, time |
PermissionChangedIntegrationEvent | RoleAssigned, RoleRevoked, RolePermissionChanged | User, role, permission, change type |
FeatureChangedIntegrationEvent | Feature state update | Feature code, enabled state, tenant, actor, time |
These event types have no [IntegrationTopic]; runtime topic names use the full event type name. Consumers must not invent a short topic, and namespace/type changes require versioned integration handling.
9. Dual-ORM contract
For SqlSugar and EF Core, verify trusted tenant filters, equivalent filtered unique indexes, exclusion of soft-deleted rows, distinct empty-versus-failure semantics, idempotent stable-key seeding, and equivalent write-plus-event transaction behavior.
10. Production verification
# Confirm real stores and evaluators enter the persistence branch.rg -n "AddBitzOrcasGeneratedPersistenceAdapters|AddBitzOrcasAuthorizationPlatform" \ src/Hosts/BitzOrcas.Api/Composition -g '*.cs'
# A real audit backend removes the Null authorization sink.rg -n "RemoveAll<IAuthorizationAuditSink>|NullAuthorizationAuditSink" \ src/Framework -g '*.cs'
# Every owner relation needs explicit tenant, soft-delete, and unique-index metadata.rg -n "BitzTable|BitzIndex" \ src/Platform/Authorization/BitzOrcas.Platform.Authorization.Infrastructure/Persistence -g '*.cs'Previous: ABAC, ReBAC, and Feature · Next: testing and operations