- 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
55 lines
2.0 KiB
Go
55 lines
2.0 KiB
Go
package bouncer
|
|
|
|
import "context"
|
|
|
|
type dispatchKey struct{}
|
|
|
|
// Dispatch is the pre-resolved principal marker an in-process dispatcher
|
|
// attaches to a request it builds itself (the MCP module calling one of a
|
|
// plugin's routes on behalf of an already authenticated caller). The marker
|
|
// is context-only: it lives under an unexported key, nothing reads a header,
|
|
// cookie or query string for it, and an HTTP request can therefore never
|
|
// carry one. Only an in-process dispatcher inside the framework may call
|
|
// WithDispatch; plugins never set it.
|
|
//
|
|
// Guards lists the exact registry guard names (for example "api_token" or
|
|
// "backend") whose middleware honours the marker; Registry.Middleware for a
|
|
// name the marker does not list ignores it and authenticates the request as
|
|
// usual. Principal is the identity the dispatcher resolved and Credential the
|
|
// optional credential record stored next to it (WithCredential), nil for a
|
|
// principal without one.
|
|
type Dispatch struct {
|
|
Guards []string
|
|
Principal *Principal
|
|
Credential any
|
|
}
|
|
|
|
// WithDispatch stores the marker on ctx. A nil ctx becomes a background
|
|
// context, as WithUser does. Only an in-process dispatcher may call it.
|
|
func WithDispatch(ctx context.Context, d *Dispatch) context.Context {
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
return context.WithValue(ctx, dispatchKey{}, d)
|
|
}
|
|
|
|
// Dispatched returns the marker on ctx when it carries a principal and lists
|
|
// guard among its Guards (exact name comparison, no wildcard). Any other
|
|
// context, a marker without a principal or a marker for other guards answers
|
|
// false, so the caller falls back to authenticating the request.
|
|
func Dispatched(ctx context.Context, guard string) (*Dispatch, bool) {
|
|
if ctx == nil || guard == "" {
|
|
return nil, false
|
|
}
|
|
d, ok := ctx.Value(dispatchKey{}).(*Dispatch)
|
|
if !ok || d == nil || d.Principal == nil {
|
|
return nil, false
|
|
}
|
|
for _, name := range d.Guards {
|
|
if name == guard {
|
|
return d, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|