Skip to content

Expose UpstreamFilter through the pkg/authserver.New facade (Config passthrough) #5732

Description

@tgrunnagle

Summary

WithUpstreamFilter (added in #5725) lets a caller narrow the upstream
authorization chain, but it is only reachable through the low-level
pkg/authserver/server/handlers.NewHandler. The high-level
pkg/authserver.New(ctx, cfg, storage.Storage) facade and its authserver.Config
provide no way to inject an UpstreamFilter. A caller who embeds the
authorization server through the public authserver.New facade therefore cannot
install a filter at all.

Current behavior

  • pkg/authserver/server/handlers/handler.go defines the UpstreamFilter
    interface and the WithUpstreamFilter(UpstreamFilter) Option constructor.

  • pkg/authserver/server_impl.go builds the handler internally and passes only
    the refresher option (server_impl.go:209):

    handlerInstance, err := handlers.NewHandler(fositeProvider, authServerConfig, stor, upstreams,
        handlers.WithUpstreamRefresher(refresher))
  • authserver.Config (pkg/authserver/config.go) has no field for a filter, and
    authserver.New (pkg/authserver/server.go) takes no functional options.

  • The builders server_impl.go uses to assemble the handler — createProvider,
    the CIMD storage decorator, newUpstreamTokenRefresher — are unexported, so a
    caller cannot reconstruct the handler stack via handlers.NewHandler without
    re-implementing the server wiring.

Net effect: the filter hook shipped in #5725 is unreachable for anyone using the
public authserver.New facade. Only the internally-constructed refresher is
wired.

Proposed change

Thread an optional filter through the facade, mirroring the internal refresher
wiring:

  1. Add a field to authserver.Config:

    // UpstreamFilter, when set, narrows the upstream authorization chain after the
    // first leg resolves (see handlers.WithUpstreamFilter). When nil, all
    // configured upstreams are walked — the current behavior.
    UpstreamFilter handlers.UpstreamFilter
  2. In server_impl.go, pass it to NewHandler only when non-nil:

    opts := []handlers.Option{handlers.WithUpstreamRefresher(refresher)}
    if cfg.UpstreamFilter != nil {
        opts = append(opts, handlers.WithUpstreamFilter(cfg.UpstreamFilter))
    }
    handlerInstance, err := handlers.NewHandler(fositeProvider, authServerConfig, stor, upstreams, opts...)

A nil filter preserves today's behavior (walk all upstreams), so the change is
backward-compatible.

Import direction is clean: pkg/authserver already imports
pkg/authserver/server/handlers (server_impl.go:18), and handlers does not
import pkg/authserver, so referencing handlers.UpstreamFilter from
config.go introduces no cycle. If a direct reference is undesirable, a package
alias (type UpstreamFilter = handlers.UpstreamFilter) at the authserver
level would work equally well.

Test

Add coverage next to the existing filter tests
(pkg/authserver/server/handlers/handler_chain_test.go) proving that:

  • a Config.UpstreamFilter set through authserver.New reaches the handler and
    narrows the chain, and
  • a nil filter walks all configured upstreams (no behavior change).

References

  • Filter upstream auth chain via callback hook #5725 — added WithUpstreamFilter and the UpstreamFilter interface at the
    handler level.
  • pkg/authserver/server/handlers/handler.goUpstreamFilter,
    WithUpstreamFilter.
  • pkg/authserver/server_impl.go:209 — the handlers.NewHandler(...) call site.
  • pkg/authserver/config.goConfig.
  • pkg/authserver/server.goNew.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Fields

No fields configured for Chore 🧹.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions