1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace LaravelUi5\OData\Service\Contracts;
6:
7: use Illuminate\Http\Request;
8: use LaravelUi5\OData\Service\ReadContext;
9:
10: /**
11: * The read-authorization forward-exit.
12: *
13: * OData is security-agnostic: it ships this seam and a no-op default
14: * ({@see \LaravelUi5\OData\Service\AllowAllReadAuthorizer}). A host that knows about actors
15: * and permissions binds its own implementation — via the `odata.read_authorizer` config key
16: * or by rebinding this interface — and records verdicts into the {@see ReadContext}:
17: *
18: * - `denyHard()` on a primary / root target → the controller answers a 403;
19: * - `denyDrop()` on an `$expand` target → the engine prunes it and emits a `sap-messages`
20: * warning (the honest-partial model);
21: * - no verdict / `allow()` → the read proceeds.
22: *
23: * The `$plan` is typed as the marker {@see QueryPlanInterface} to respect the
24: * Service → Protocol ring boundary; an enforcer downcasts to the concrete plan
25: * (`EntitySetQueryPlan`, `EntityQueryPlan`, …) to read its target set. It must not throw for
26: * an authorization decision — it records into the collector, and the caller enforces.
27: */
28: interface ReadAuthorizerInterface
29: {
30: public function authorize(QueryPlanInterface $plan, Request $request, ReadContext $read): void;
31: }
32: