← Back to Services

6.2 Authorization Helpers

KMP builds on CakePHP Authorization with tenant-local roles, permissions, active windows, warrants, and branch scopes. Use the public authorization APIs; policy internals such as _hasPolicy() and _getBranchIdsForPolicy() are for policy implementations, not controllers.

Common controller patterns

// One entity
$this->Authorization->authorize($entity, 'edit');

// Collection/controller action
$this->Authorization->authorizeModel('index');

// Scope before pagination, grid data, counts, or export
$query = $this->Authorization->applyScope($query, 'index');

Follow the exact call shape already used by the relevant app controller base; API and web controllers have different surrounding response behavior.

Identity helpers

KmpIdentityInterface is implemented by member and service-principal identities. It exposes identifiers, permissions, policy data, and scope application. Member adds these useful helpers:

Helper Contract
getPermissions() / getPermissionIDs() Cached effective tenant permissions
getPolicies(branchIds?) Policy configuration, optionally narrowed by branch
isSuperUser() Whether an effective permission carries the super-user flag
getBranchIdsForAction(action, resource) null for global, an ID array for scoped access, [] for none
canManageMember(member) Current self/parent-minor management rule

getBranchIdsForAction() accepts an entity/table resource or a Cake alias such as Gatherings or Waivers.GatheringWaivers. It is useful when a service needs branch IDs to build an already-authorized domain query. Do not treat null as no access—it means global access.

Policy behavior

BasePolicy maps standard canAdd, canEdit, canDelete, canView, canIndex, and canGridData to configured policy grants. scopeIndex() uses the table’s addBranchScopeQuery() when branch-limited; canGridData() defaults to index authorization. Subclasses add domain actions and custom scopes.

Super users short-circuit in BasePolicy::before(). URL/controller resources are handled through configured URL policy methods. Do not call the protected policy helpers from application code or copy the deny sentinel used internally by policy scopes.

AuthorizationService::checkCan() is a boolean helper for nested/non-terminal checks and preserves Cake’s “authorization checked” state. Normal controller actions should still call authorize() so a missing check is detectable.

Scope is mandatory

Authorizing index proves the identity may use the endpoint; it does not necessarily constrain every row. Apply scope to the actual query used by pagination, Dataverse, CSV/PDF/JSON, autocomplete, counts, and related-record pickers. Never authorize one query and serialize another.

Tenancy and caching

Permission/policy loaders resolve data from the active tenant and cache it. Do not reuse an identity after changing tenant context. Clear existing identity/permission caches through established lifecycle paths when grants change or impersonation starts/stops.

Tests

Cover allow, deny, global scope, one/multiple branch scopes, no policy, super user, active-window/warrant edges, self/parent-minor rules where applicable, collection row exclusion, exports/API variants, service-principal behavior, impersonation, and two tenants with colliding IDs.