rbac

package
v0.26.1 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompiledRule

type CompiledRule struct {
	Raw Rule
	// contains filtered or unexported fields
}

CompiledRule is an Rule prepared for matching. For prefix rules the trailing "*" is stripped once at compile-time so the hot path only does prefix comparisons.

Raw is the source rule as declared in configuration; the prefix internals stay unexported because they are matcher implementation detail callers should not depend on.

type Config

type Config struct {
	// Action applied when no rule matches the request path
	DefaultPolicy Policy `yaml:"defaultPolicy" mapstructure:"defaultPolicy" jsonschema:"required,enum=allow,enum=deny"`
	// Path-keyed allow/deny rules. Path matching prefers exact paths, then the longest matching prefix
	Rules []Rule `yaml:"rules" mapstructure:"rules"`
}

Config is the file-shape configuration for the middleware.

DefaultPolicy applies when no rule matches the request path. It must be PolicyAllow or PolicyDeny — the empty string is rejected by NewMatcher so callers make an explicit choice.

func LoadConfigFromFile

func LoadConfigFromFile(path string) (Config, error)

LoadConfigFromFile reads an Config from the YAML file at path.

Expected file shape:

defaultPolicy: allow      # or "deny"
rules:
  - path: "/api/foo/*"
    allow: [admin]
    deny:  [visitor]

LoadConfigFromFile only decodes the YAML; semantic errors (empty paths, duplicate paths, an unknown DefaultPolicy) surface from NewMatcher. Keeping load and validation separate lets callers inspect the raw decoded shape before committing to a matcher.

type Decision

type Decision struct {
	Outcome Outcome
	Rule    *CompiledRule
	Roles   []string
	Authed  bool
}

Decision is the result of evaluating a single request against an Matcher. Rule is nil when no rule matched (the default policy applied); Roles and Authed are the values returned by the extractor.

type Matcher

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

Matcher is a validated, compiled Config ready to drive the middleware. Build one via NewMatcher and pass it to RBAC.

Matcher is safe for concurrent use: it is read-only after construction.

func NewMatcher

func NewMatcher(cfg Config) (*Matcher, error)

NewMatcher validates cfg and compiles it into a matcher.

Returns an error when:

  • cfg.DefaultPolicy is neither PolicyAllow nor PolicyDeny (including the zero-value empty string).
  • any Rule has an empty Path.
  • two rules share the same exact path or the same prefix.

All validation errors surface here so callers can fail fast at service startup rather than at request time.

func (*Matcher) Evaluate

func (m *Matcher) Evaluate(extract RolesExtractor, r *http.Request) Decision

Evaluate classifies the request against the compiled rule set. The 401-vs-403 split is driven by the extractor's authenticated flag.

type Outcome

type Outcome string

Outcome is the terminal classification of a request. It drives both the HTTP response (allow → pass-through, deny → 403, unauthenticated → 401) and the log label.

const (
	OutcomeAllow           Outcome = "allow"
	OutcomeDeny            Outcome = "deny"
	OutcomeUnauthenticated Outcome = "unauthenticated"
	OutcomeNoRuleAllow     Outcome = "no_rule_allow"
	OutcomeNoRuleDeny      Outcome = "no_rule_deny"
)

type Policy

type Policy string

Policy is the fallback action applied when no Rule matches the request path. Encoded as a string so YAML / JSON configuration decodes directly without a custom unmarshaler.

const (
	// PolicyAllow lets unmatched requests through to the next handler.
	PolicyAllow Policy = "allow"
	// PolicyDeny rejects unmatched requests with 401 or 403 depending
	// on whether the caller is authenticated.
	PolicyDeny Policy = "deny"
)

type RolesExtractor

type RolesExtractor func(r *http.Request) (roles []string, authenticated bool)

RolesExtractor pulls the set of roles attached to a request, along with an authenticated flag that drives the 401-vs-403 distinction on denial.

"Roles" is a deliberately broad label: any string the caller wants to match against AllowRoles / DenyRoles fits — IAM roles, group memberships, OAuth scopes, custom claim values, and so on. The RBAC package name commits to role-shaped vocabulary; the extractor stays agnostic about where the values come from.

Contract:

  • roles may be nil or empty for an authenticated caller with no matching roles; the middleware treats len(roles) == 0 as "no positive matches" rather than as unauthenticated.
  • authenticated reflects whether the request carries a trusted identity at all. An extractor that cannot tell the difference should return true whenever it returns any roles, and false only on a confirmed absence of identity.
  • the extractor must not mutate the request.

type Rule

type Rule struct {
	// Exact request path (e.g. "/api/foo/GetBar") or a prefix ending in
	// '*' (e.g. "/api/foo/*"). The '*' suffix matches the empty string and any suffix.
	Path string `yaml:"path" mapstructure:"path" jsonschema:"required,pattern=^/,minLength=1"`
	// Roles granted access. Empty or omitted means "no positive constraint" — useful for deny-only rules.
	AllowRoles []string `yaml:"allowRoles" mapstructure:"allowRoles" jsonschema:"uniqueItems,minLength=1"`
	// Roles denied access. Evaluated before allowRoles; a match here rejects regardless of allowRoles.
	DenyRoles []string `yaml:"denyRoles"  mapstructure:"denyRoles" jsonschema:"uniqueItems,minLength=1"`
}

Rule declares allow/deny role lists for a single path pattern.

Path is either an exact request path (e.g. "/api/foo/GetBar") or a prefix ending in "*" (e.g. "/api/foo/*"). The "*" matches the empty string and any suffix: "/api/foo/*" matches both "/api/foo" and "/api/foo/anything".

Semantics applied to (AllowRoles, DenyRoles) against the request's roles:

pass iff (len(AllowRoles) == 0 || roles ∩ AllowRoles ≠ ∅)
         && roles ∩ DenyRoles == ∅

An empty AllowRoles list means "no positive constraint" — useful for deny-only rules. An empty DenyRoles list means "no explicit exclusions". Both empty means "this path is gated by having a matching rule at all", which is only meaningful when DefaultPolicy is PolicyDeny.

Jump to

Keyboard shortcuts

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