Skip to content

Authorize every Pulsar audience

Every audience authorizes at its Adapter boundary; Gates and Policies decide permission only. Authorization does not establish tenant scope, validate Domain state, make an effect idempotent, or replace a one-UseCase entrypoint.

Authorize every audience

Authorize Job and Command

AudienceAdapter responsibilityExplicit failure path
HTTPForm Request authorization or a Policy/Gate after validation and actor/tenant establishment.The generated Request returns false until an application rule is written.
JobReconstruct the actor from durable data, reject absence, then authorize before one UseCase.No Form Request or ambient user proves worker permission.
CommandValidate input, establish explicit application actor and tenant or reject unattended work, then authorize.A CLI invocation must not bypass permission because it has no HTTP session.
ListenerReconstruct actor/tenant when actor-sensitive, then authorize the reaction explicitly.A retry repeats the authorization and does not inherit a prior decision.

Choose a Policy or Gate

Use a Policy for an ability tied to a Model and a Gate for an ability without a Resource. The generated Policy for a Model returns false from view, create, update, and delete; its before() admin hook is an audited exception, not default permission.

app/Pulsar/Domain/{Domain}/Policies/{Model}Policy.phpGenerated default-deny Policy method.

php
final class OrderPolicy
{
    public function update(AuthUser $user, Order $model): bool
    {
        return false; 
    }
}

Name abilities without magic strings

Define stable ability values in a backed Ability Enum owned by the Domain that needs them, then pass ->value to Laravel's Gate API. Do not introduce a global Ability Enum or role abstraction here.

Context: per-Domain ability value — A Gate receives the stable backed value and an explicit subject.

php
Gate::forUser($actor)->authorize(OrderAbility::Update->value, $order); 

Fail closed

The Request stub starts with return false;; a Policy starts with false methods. Replace those placeholders with an application rule, not an allow-by-default convenience. Gate::authorize() fails closed when no matching rule allows the ability. An approved Policy decision only answers “may this actor do this?”

Keep permission separate

After authorization, scope every subject to the tenant, let the Action or UseCase validate business state for every caller, and protect a retryable effect with its stable idempotency key. A correct Gate call does not prove that a record belongs to the tenant, that a state transition is valid, or that an effect has not already occurred. Tenant isolation owns the combined enforcement approach.