Decision Cache
Within one request, each distinct check runs the policy once. Between requests, the package keeps only the decisions that a resource declares. A resource that declares nothing keeps nothing.
Declaring the cached abilities
List in authorizationCache() the abilities to keep, with the number of seconds to keep each one:
public function authorizationCache(): array
{
return ['viewAny' => 300, 'view' => 60];
}
- The other abilities run the policy in each request.
- A duration that is not an integer of 1 or more throws
Lomkit\Rest\Exceptions\InvalidResourceException. - The resource of the check declares the ability: the resource of the endpoint or of the reached relation for
viewAnyandcreate, the resource of the row for its gates and its row checks, and the parent resource for the link abilities of a relation. - A declared ability is read from the cache wherever it is checked: the gates of a response, the checks before validation, the row checks of
mutate,destroy,restoreandforceDestroy, and the row checks of actions. - Denials are kept like the other decisions, with their message and status.
delete on a row lets destroy delete that row without calling the policy while the row keeps its updated_at. Cache the write abilities only when their policy reads nothing that changes outside the row.What is never kept
- The check of
createon each row thatmutatecreates, and theupdatechecks ofmutate, which carry the changes of their entry. Thecreatecheck on the model class can be kept. - A decision on a row without
updated_at. Add timestamps to a model to keep the decisions on its rows. - A decision on a row whose
updated_atis not before the second in which the request started. - A check on a row that the request wrote.
- A check on a model that is not saved yet.
The key
The key holds the guard, the user class and key, the resource class, the ability, the class, key and updated_at of each model, the values of authorizationCacheContext(), and the stamps that Rest::forgetAuthorizationsFor() and Rest::forgetAuthorizations() renew.
A decision on a row follows the row's updated_at, so a change made through Eloquent is checked again in the next request. A change made without Eloquent that leaves updated_at alone, such as DB::table()->update(), shows once the entry expires or the user is forgotten.
Adding values to the key
authorizationCacheContext() adds values of your own to the key of every decision that the resource keeps, for example the team of the user:
public function authorizationCacheContext(): array
{
return ['team' => $this->request()->user()?->current_team_id];
}
A value is an integer, a string, a boolean or null. Another value throws InvalidResourceException.
Tenants
The key holds the user class and key, not the tenant. With several tenants whose users share identifiers, add the tenant in the base resource of your application:
public function authorizationCacheContext(): array
{
return ['tenant' => tenant()?->getTenantKey()];
}
Or name in authorizations.cache.store a store that is already per tenant. The context also keys the metric results.
Forgetting decisions
use Lomkit\Rest\Facades\Rest;
Rest::forgetAuthorizationsFor($user);
Rest::forgetAuthorizations();
forgetAuthorizationsFor($user)drops the kept decisions and metric results of a user, on every guard. Call it where your application changes what a policy or aGate::before()callback reads about the user, such as a role or a team.forgetAuthorizations()drops the kept decisions and metric results of every user and of guests, for example after a deploy that changes a policy.- Both work on every cache store, and write even when the cache is off.
Otherwise a kept decision stays for the seconds of its ability: a change to a policy shows once the entry expires.
Configuration
'authorizations' => [
'cache' => [
'enabled' => env('REST_AUTHORIZATIONS_CACHE_ENABLED', true),
'store' => env('REST_AUTHORIZATIONS_CACHE_STORE'),
],
],
enabledset tofalseturns the cache off for every resource. SetREST_AUTHORIZATIONS_CACHE_ENABLED=falsewhile you debug policies.storenames the cache store, the default store whennull. A store that is not defined throwsCache store [name] is not defined.on the first check of a declared ability.- A decision is stored as an array of scalars, so any store works,
fileanddatabaseincluded. - When no resource declares an ability, the package calls no cache store.
Grouping your own checks
The package reads the kept decisions of a group of checks in one call, and writes the decisions it evaluated in one call per duration. authorizer()->batch() groups checks of your own the same way:
use Lomkit\Rest\Authorization\Check;
$this->request()->context()->authorizer()->batch(
[Check::onClass($resource, 'viewAny')],
fn () => $resource->authorizedTo('viewAny', Post::class),
);