authz

package
v1.0.442 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package authz provides HTTP and gRPC authorization where URI paths (and their children) are allowed access by a set of roles. The caller supplies a way to map a request (or gRPC context) to an identity.Identity; the identity's Role() is checked against the configured path tree.

Access control points are on entire URI segments only: Allow("/foo/bar", "bob") gives access to /foo/bar and /foo/bar/baz, but not /foo/barry.

Access is based on the deepest matching path, not the accumulated paths:

Allow("/foo", "bob")
Allow("/foo/bar", "barry")

allows barry access to /foo/bar but not to /foo, and bob to /foo but not to /foo/bar.

AllowAny("/foo") allows any request (including guests) access to /foo. AllowAnyRole("/bar") allows any request with a non-empty, non-guest role access to /bar. AllowAny always overrides Allow/AllowAnyRole on the same node regardless of call order. Multiple calls to Allow for the same resource are cumulative.

A Provider is usually built from a Config loaded from YAML/JSON:

allow:
  - /v1/admin:admin
  - /v1/items:admin,user
allow_any:
  - /v1/status
allow_any_role:
  - /v1/me
log_allowed: true
log_denied: true
log_level: DEBUG
logger_skip_paths:
  - path: /v1/status
    agent: kube-probe

p, err := authz.New(&cfg)
if err != nil {
	return err
}
h, err := p.NewHandler(next)        // http.Handler; snapshots the tree
grpcSrv := grpc.NewServer(grpc.UnaryInterceptor(p.NewUnaryInterceptor()))

Denied requests from unauthenticated callers (guest or empty role) receive a 401 httperror.Unauthorized response (codes.Unauthenticated for gRPC); denied requests from any other role receive a 403 httperror.Forbidden response (codes.PermissionDenied). OPTIONS requests, including CORS preflights, are authorized like any other method: the preflight headers are caller-controlled, so a CORS middleware that answers preflights must run before the authz handler (restserver.NewMux and gserver place it there).

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoRoleMapperSpecified can't call NewHandler before you've set the RoleMapper function
	ErrNoRoleMapperSpecified = errors.New("you must have a RoleMapper set to be able to create a http.Handler")
	// ErrNoPathsConfigured is returned by NewHandler if you call NewHandler, but haven't configured any paths to be accessible
	ErrNoPathsConfigured = errors.New("you must have at least one path before being able to create a http.Handler")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	// Allow will allow the specified roles access to this path and its children, in format: ${path}:${role},${role}
	Allow []string `json:"allow" yaml:"allow"`

	// AllowAny will allow any request access to this path and its children
	AllowAny []string `json:"allow_any" yaml:"allow_any"`

	// AllowAnyRole will allow any authenticated request that include a non empty role
	AllowAnyRole []string `json:"allow_any_role" yaml:"allow_any_role"`

	// LogAllowedAny specifies to log allowed access to nodes in AllowAny list
	LogAllowedAny bool `json:"log_allowed_any" yaml:"log_allowed_any"`

	// LogAllowed specifies to log allowed access
	LogAllowed bool `json:"log_allowed" yaml:"log_allowed"`

	// LogDenied specifies to log denied access
	LogDenied bool `json:"log_denied" yaml:"log_denied"`

	// LogLevel specifies the log level to use for logging.
	// If not specified, the DEBUG log level will be set.
	LogLevel string `json:"log_level" yaml:"log_level"`

	// SkipLogPaths if set, specifies a list of paths to not log.
	// this can be used for /v1/status/node or /metrics
	SkipLogPaths []telemetry.LoggerSkipPath `json:"logger_skip_paths,omitempty" yaml:"logger_skip_paths,omitempty"`
}

Config is the declarative authorization configuration consumed by New. It is typically loaded from YAML/JSON; see the package documentation for the field names. Paths must start with "/".

type GRPCAuthz

type GRPCAuthz interface {
	// SetGRPCRoleMapper configures the function that maps a gRPC request
	// context to the identity whose Role() is authorized. The default reads
	// identity.FromContext.
	SetGRPCRoleMapper(m func(ctx context.Context) identity.Identity)
	// NewUnaryInterceptor returns grpc.UnaryServerInterceptor that enforces the current
	// authorization configuration.
	// The returned interceptor will extract the role and verify that the role has access to the
	// URI being request, and either return an error, or pass the request on to the supplied
	// delegate handler
	NewUnaryInterceptor() grpc.UnaryServerInterceptor
}

GRPCAuthz is the gRPC-facing authorization contract. *Provider implements it; the full method name (e.g. "/pkg.Service/Method") is matched against the path tree exactly like an HTTP path.

type HTTPAuthz

type HTTPAuthz interface {
	// SetRoleMapper configures the function that maps an HTTP request to the
	// identity whose Role() is authorized. The default reads identity.FromRequest.
	SetRoleMapper(func(*http.Request) identity.Identity)
	// NewHandler returns a http.Handler that enforces the current authorization configuration
	// The handler has its own copy of the configuration changes to the Provider after calling
	// NewHandler won't affect previously created Handlers.
	// The returned handler will extract the role and verify that the role has access to the
	// URI being request, and either return an error, or pass the request on to the supplied
	// delegate handler
	NewHandler(delegate http.Handler) (http.Handler, error)
}

HTTPAuthz is the HTTP-facing authorization contract consumed by restserver.HTTPServer.WithAuthz. *Provider implements it.

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

Provider holds the path/role tree and the role mappers, and implements HTTPAuthz and GRPCAuthz. Build it with New (or configure a zero value via Allow/AllowAny/AllowAnyRole, but note that isAllowed dereferences cfg, so a Provider created without New must not be used for checks). Mutating calls (Allow*, Set*Mapper) are not synchronised and must complete before the handlers/interceptors serve traffic.

func New

func New(cfg *Config) (*Provider, error)

New builds a Provider from cfg with the default role mappers. Each Config.Allow entry must be "${path}:${role}[,${role}...]", otherwise an error is returned. cfg must not be nil. Paths that do not start with "/" panic.

func (*Provider) Allow

func (c *Provider) Allow(path string, roles ...string)

Allow allows the specified roles access to this path and its children [unless a specific Allow/AllowAny is called for a child path]. Multiple calls to Allow for the same path are cumulative; empty role names are ignored. Panics if path does not start with "/".

func (*Provider) AllowAny

func (c *Provider) AllowAny(path string)

AllowAny allows any request, including unauthenticated guests, access to this path and its children [unless a specific Allow/AllowAny is called for a child path]. It replaces any AllowAnyRole flag on the node; roles added with Allow are kept but ignored while AllowAny is set. Panics if path does not start with "/".

func (*Provider) AllowAnyRole

func (c *Provider) AllowAnyRole(path string)

AllowAnyRole allows any request whose role is non-empty and not the guest role access to this path and its children [unless a specific Allow/AllowAny is called for a child path]. Panics if path does not start with "/".

func (*Provider) Clone

func (c *Provider) Clone() *Provider

Clone returns a deep copy of this Provider (tree, mappers and config), so later mutations of the original do not affect the copy.

func (*Provider) NewHandler

func (c *Provider) NewHandler(delegate http.Handler) (http.Handler, error)

NewHandler returns a http.Handler that enforces the current authorization configuration. The handler works on a Clone of the Provider, so changes to the Provider after calling NewHandler do not affect previously created handlers. The handler maps the request to an identity via the role mapper, checks r.URL.Path against the tree and either writes a JSON denial (401 unauthorized for a guest or empty role, 403 forbidden for any other role) or passes the request to delegate. OPTIONS requests, including CORS preflights, are authorized like any other method; a CORS middleware that answers preflights must run before this handler. It returns ErrNoRoleMapperSpecified or ErrNoPathsConfigured when the Provider is not usable.

func (*Provider) NewStreamServerInterceptor added in v0.33.319

func (c *Provider) NewStreamServerInterceptor() grpc.StreamServerInterceptor

NewStreamServerInterceptor returns the streaming counterpart of NewUnaryInterceptor, checking access once when the stream is opened.

func (*Provider) NewUnaryInterceptor

func (c *Provider) NewUnaryInterceptor() grpc.UnaryServerInterceptor

NewUnaryInterceptor returns a grpc.UnaryServerInterceptor that checks the identity from the gRPC role mapper against info.FullMethod and fails with httperror.Unauthorized (codes.Unauthenticated) for a guest or empty role and httperror.Forbidden (codes.PermissionDenied) for any other role when denied. Unlike NewHandler it uses the live Provider, not a clone, and does not require a configured tree (an empty tree denies everything).

func (*Provider) SetGRPCRoleMapper

func (c *Provider) SetGRPCRoleMapper(m func(ctx context.Context) identity.Identity)

SetGRPCRoleMapper configures the function that maps a gRPC context to the identity whose Role() is authorized by the interceptors.

func (*Provider) SetRoleMapper

func (c *Provider) SetRoleMapper(m func(r *http.Request) identity.Identity)

SetRoleMapper configures the function that maps an HTTP request to the identity whose Role() is authorized. Setting it to nil makes NewHandler fail with ErrNoRoleMapperSpecified.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL