feat(19.7-01): in-process dispatch marker and compiled handler lookup
- bouncer.Dispatch, WithDispatch, Dispatched: context-only pre-resolved
principal marker honoured by Registry.Middleware for listed guard names
- cabana.ResolveBackend honours a backend-kind marker; additive
ResolveBackendToken, ScopeLimitFor and BackendPrincipal resolvers
- surf.Router.Handler and compileHandlers: one compiled chain per route,
shared by BuildRouter validation and compile
- smoke tests TestDispatchSmoke, TestDispatchedSmoke, TestDispatchMarkerSmoke
- READMEs and docs/services/{authentication,routing}.md updated
This commit is contained in:
@@ -137,6 +137,8 @@ To protect routes, return that middleware from `pact.HasMiddleware` under a name
|
||||
|
||||
A guard that resolves more than a user, such as an API token record, implements `bouncer.CredentialGuard`; the middleware stores that record too, and handlers read it with `bouncer.Credential`.
|
||||
|
||||
A framework dispatcher that calls a route in-process on behalf of a caller it has already authenticated (the MCP module running a tool through the plugin's own route) does not mint a token for the call. It attaches a `bouncer.Dispatch` marker to the request context with `bouncer.WithDispatch`: the pre-resolved principal, its optional credential and the exact guard names that may honour it. The marker is context-only. Nothing reads a header, cookie or query string for it, so an HTTP request can never carry one, and only a dispatcher inside the framework sets it; plugins never do. `bouncer.Registry.Middleware` honours a marker only for a guard name the marker lists (`bouncer.Dispatched`) and then skips the guard, which covers every guard registered through the registry, such as a plugin's API token guard. `cabana.ResolveBackend` honours a marker for the `backend` guard when its principal is a backend one; a frontend marker is refused as unauthenticated, and a scoped principal is still checked against the cover codes. A marker for one guard has no effect on a route behind another.
|
||||
|
||||
### Accepting admin tokens on plugin routes
|
||||
|
||||
Plugin routes that should accept an admin JWT or a personal API token call `cabana.ResolveBackend`. It reads only the Authorization Bearer header (never the admin cookie), applies the token's method filter, and for a scoped token requires a method-allowed scope that is full or matches every cover code you pass. On success it returns the request with `bouncer.WithUser` (and `bouncer.WithCredential` for a token). Failures are `cabana.ErrUnauthenticated` or `cabana.ErrTokenScope`. Routes registered before `cabana.Activate` must call it per request, because the resolver is published at activation.
|
||||
|
||||
@@ -281,6 +281,8 @@ for _, rt := range r.Routes() {
|
||||
// POST /api/blog/posts/{id}/comments acme.blog [throttle:60,1 acme.auth throttle:blog.comments body.limit:65536]
|
||||
```
|
||||
|
||||
`surf.Router.Handler` hands out the compiled chain of one route by method and pattern, for example `r.Handler("GET", "/api/blog/posts/{id}")`. The chain is the route exactly as the mux serves it: constraints, the named middleware (guards, throttles), the body limit, locale, recovery and the CORS profile. A framework component that calls a route in-process, such as the MCP module, serves that handler with a request it builds itself, setting the path values and `Pattern` as the mux would; the caller's identity reaches the guard through the [dispatch marker](authentication.md#guards-and-middleware), never through a header. The lookup answers false for an unknown route and before `surf.BuildRouter` compiled the routes.
|
||||
|
||||
## CORS
|
||||
|
||||
CORS is configured with the keys of Laravel's `config/cors.php`, under `http.cors`, and applies only to paths that match `http.cors.paths`. With no `http.cors` section, no CORS headers are sent:
|
||||
|
||||
Reference in New Issue
Block a user