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
| Audience | Adapter responsibility | Explicit failure path |
|---|---|---|
| HTTP | Form Request authorization or a Policy/Gate after validation and actor/tenant establishment. | The generated Request returns false until an application rule is written. |
| Job | Reconstruct the actor from durable data, reject absence, then authorize before one UseCase. | No Form Request or ambient user proves worker permission. |
| Command | Validate 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. |
| Listener | Reconstruct 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.php — Generated default-deny Policy method.
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.
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.