Files
summercms/modules/bouncer/dispatch.go
Jakub Zych 1bd91a4522 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
2026-10-22 16:00:19 +02:00

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
}