Skip to content
bitzorcas
中EN

Concept

Authorization platform

Understand the current platform from its unified decision chain through field security, sharing, delegated administration, simulation, and workflow-backed temporary grants.

Last updated

Authorization does not ask whether a user signed in. It asks whether this trusted caller may perform this action on this resource, in this tenant, now. Several policies contribute evidence, and the result must remain auditable, invalidatable, and reproducible.

1. One capability, two source areas

The runtime is split between Framework and Platform code:

OwnerRepresentative typesResponsibility
Framework ApplicationIAuthorizedRequest, AuthorizationDecisionService, evaluators, DataScopeResolverRuntime decision protocol and composition rules
Authorization ContractsRBAC, field, sharing, delegation, request contracts, catalogsPublic contracts and owner extension points
Authorization Applicationpolicy management, effective-permission explanation, temporary grantsChange and explain policy state, publish events, invalidate caches
Authorization Infrastructureowner-local records, stores, caches, and dual-ORM adaptersPersist relations, rules, classification, overrides, and temporary grants
Host compositionCoreRuntime and Persistence registrationInstall placeholders first, then close the graph with production adapters

Platform owns policy facts; Framework evaluates them. A business module must not query Authorization tables or reproduce policy with local role checks.

2. Request-to-decision path

AllowDeny

HTTP / Mediator request

IAuthorizedRequest
Resource + Action

AuthorizationPipelineBehavior

AuthorizationDecisionService

RBAC / AppScope

ABAC

ReBAC

Feature

Deny-first merge

Resolve DataScope

Authorization audit

Run handler

Forbidden Result
handler skipped

The authorization behavior executes before the transaction behavior. A denial returns Authorization.Denied, skips the handler, and must not start a transaction. AuthorizationPipelineBehaviorTests.Deny_Should_Prevent_Transaction_From_Starting fixes this ordering.

3. Evaluators are not independent gates

EvaluatorAllowDenyNeutral
RBACA non-Application caller has {module}.{resource}.{action}Never directlyPermission absent or caller not applicable
AppScopeAn Application caller has the same scopeNever directlyScope absent or caller not applicable
ABACFirst matching rule says AllowFirst match says Deny; store failure also deniesNo rules or no condition match
ReBAC LiteA user has an action-compatible relation to a Case, Client, or Billing instanceRelation store returns FailureNo subject, instance, relation, or supported resource
FeatureMapped feature for tickets, chat, workflow, or reporting is enabledMapped feature is disabled or provider returns falseModule has no mapping

Every evaluator runs before the final merge. Any Deny rejects; otherwise one or more Allows permit; all Neutral yields NoMatchingPolicy. Registration order affects the first denial reason and obligation order, but placing Feature last is not what gives Deny priority.

See decision engine for the algorithm, cache key, DataScope, and audit behavior.

4. Management plane versus decision plane

The management plane changes roles, bindings, permissions, ABAC, Feature, field policy, classification, sharing, and delegated grants through owner-local stores. A workflow-approved access request is a separate, absolutely expiring authorization source. The decision plane reads trusted subjects, resource facts, policy stores, and caches; it never calls a management handler.

Declare authorization facts on a business command
public sealed record ApproveInvoiceCommand(
string InvoiceId,
decimal Amount,
string Status,
string Sensitivity) : ICommand<Result>, IAuthorizedRequest
{
// The same descriptor feeds the RBAC code, ABAC facts, and instance-level ReBAC.
public ResourceDescriptor Resource => new(
Module: "billing",
ResourceType: "invoice",
ResourceId: InvoiceId,
TenantId: TrustedTenantId,
Status: Status,
Amount: Amount,
Sensitivity: Sensitivity);
// The action participates in permission derivation and policy filtering.
public AuthorizationAction Action => AuthorizationAction.Approve;
// Use the caller's trusted tenant accessor, never a tenant supplied in the body.
private string TrustedTenantId => CurrentTenantAccessor.EffectiveTenantId;
}

This complete contract example uses CurrentTenantAccessor as a stand-in for the caller’s trusted tenant port. In production, load owner, status, amount, and sensitivity from server-side data rather than accepting self-declared facts.

5. The current model is not a Role aggregate

Earlier prose described a Role.Grant() aggregate, but the implementation uses explicit store records:

ModelStable key and boundary
RoleRecord / RoleCatalogRecordDatabase ID for management; tenant-local role name for relation rows
UserRoleRecord / UserRoleRelationRecordIdentity subject key plus a role normalized to its name
RolePermissionRecord / relation recordRole name, Menu module code, permission code
Permission recordsGlobal generated or deterministically seeded catalog
ABAC recordsTenant rule with integer condition and verdict fields
Feature definition + overrideGlobal default separated from tenant override
ResourceRelationRecordTenant-local subject-to-resource relation without cross-context CLR navigation

See RBAC management and role lifecycle for stable-key and write-path details.

6. Management endpoints

CapabilityRepresentative routeResource / action
RolesGET/POST /api/authorization/roles, PUT/DELETE /roles/{roleId}authorization/role + View/Create/Update/Delete
User rolesGET/POST/DELETE /users/{userId}/roles/{roleId}authorization/user-role + View/Assign/Revoke
Role permissionsGET/POST/DELETE /roles/{roleId}/permissionsauthorization/role-permission + View/Grant/Revoke
Permission treeGET /api/authorization/permissions/treeauthorization/permission + View
ABACGET/POST/PUT/DELETE /api/authorization/abac-rulesauthorization/abac-rule + View/Create/Update/Delete
FeatureGET /api/authorization/features, PUT /features/{featureCode}authorization/feature + View/Update
Advanced governance/field-security, /sharing, /delegated-admin, /permission-audit, /permission-simulation, /permission-requestscapability-specific View/Manage/Approve/Reject/Revoke

Grant and revoke commands now use AuthorizationAction.Grant and AuthorizationAction.Revoke, matching the public permission catalog. User-role assignment and revocation invalidate the target subject’s permission and menu caches rather than the actor’s.

7. Cache and event boundaries

  • Role and role-permission changes publish events and invalidate the tenant Permission cache.
  • ABAC CRUD invalidates the effective tenant’s Permission decision cache.
  • Feature updates publish FeatureChangedIntegrationEvent; a global-default change invalidates all Feature entries, while an override targets one tenant.
  • The decision cache key covers caller, claims, tenant, client, all resource facts, and action; Delegated callers bypass it.
  • DataScope and ReBAC use independent caches and require independent invalidation.

Role changes use AuthorizationSubjectCacheInvalidation for the target subject and cross-instance menu synchronization. Group relationships, field policy, sharing rules, and temporary grants retain separate sources and invalidation paths; clearing the Permission cache does not refresh every derived catalog. See configuration, persistence, and caching.

8. Chapter map

GoalRead
Understand merge, caching, DataScope, and auditDecision engine
Design roles, grants, assignments, and cleanupRBAC lifecycle
Apply attributes, relations, and entitlementsABAC, ReBAC, and Feature
Compose stores and reason about invalidationConfiguration and persistence
Adopt field, sharing, delegation, and temporary grantsAdvanced governance, testing, and operations

9. Source review commands

Run from the BitzOrcasVNext root:

Terminal window
# Read both halves of the capability: runtime decisions and policy management.
rg -n "class AuthorizationDecisionService|class .*PolicyEvaluator|class .*Role" \
src/Framework/BitzOrcas.Application/Authorization src/Platform/Authorization -g '*.cs'
# Grant and Revoke actions must remain isomorphic with the public catalog.
rg -n "AuthorizationAction\.(Grant|Revoke)|role-permission\.(grant|revoke)|user-role\.revoke" \
src/Platform/Authorization -g '*.cs'
# Every invalidation must target the changed user, tenant, resource, or Feature.
rg -n "InvalidateBy(User|Tenant|Resource)Async" \
src/Platform/Authorization src/Framework/BitzOrcas.Application/Authorization -g '*.cs'

10. Minimum GA evidence

  1. Every request-derived permission exists in the catalog.
  2. Assignment and revocation invalidate the target subject’s decisions and menu projection immediately.
  3. ABAC, ReBAC, and Feature failures retain fail-closed behavior.
  4. Consumers convert DataScope into query filters and prove tenant isolation.
  5. Cache hits recompute DataScope and audit the call; delegated sessions never outlive their grant through cache.
  6. Field security, sharing, delegation, and temporary grants have no unregistered owner execution surfaces.
  7. Both ORM contracts, seed idempotency, and global permission/Feature uniqueness pass.

Next: decision engine · Back to module catalog

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%