Help us shape the future of Go! Take the 2026 Go Developer Survey

authz

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 2 Imported by: 1

README ΒΆ

go-authz πŸ”

A small, dependency-free, general-purpose authorization engine for Go. You configure the model; the library provides the decisions. Nothing is hard-coded to one scheme.

πŸ“¦ Install

go get github.com/Bugs5382/go-authz

✨ What you get

  • default-deny-all baseline β€” fail-safe by construction (Deny is the zero value).
  • allow / RBAC-style rules β€” subjects hold roles, roles are granted permissions.
  • responsibility matrix β€” a subject Γ— resource Γ— action table with wildcards.
  • Framework-agnostic: it returns Decisions. No HTTP/gRPC middleware baked in β€” you wire it.
  • Zero third-party dependencies. Optional decision logging via the standard library log/slog.

πŸš€ Usage

Every configuration runs on the same engine: build a Policy, add Rules, call Decide.

Default-deny
p := authz.NewPolicy() // no rules
d := p.Decide(ctx, authz.Request{Subject: "alice", Resource: "report", Action: "read"})
// d.Allowed() == false, d.Reason == authz.ReasonNoMatch
Allow / RBAC-style
rbac := authz.NewRBAC("rbac").
    Assign("alice", "editor").
    Grant("editor", authz.Wildcard, "write") // editors write any resource

p := authz.NewPolicy().Add(rbac) // deny-by-default + grants
p.Decide(ctx, authz.Request{Subject: "alice", Resource: "post", Action: "write"}).Allowed() // true
Responsibility matrix

Map your own actions (view / edit / delete, …) onto the action dimension:

m := authz.NewMatrix("access").
    Allow(authz.Wildcard, "document", "view").
    Allow("editor1", "document", "edit").
    Deny("editor1", "document", "delete").      // editor1 may not delete
    Allow("admin1", "document", "delete")

p := authz.NewPolicy().Add(m)
p.Decide(ctx, authz.Request{Subject: "editor1", Resource: "document", Action: "delete"}).Allowed() // false

🧭 Decisions

Decide returns a Decision{Effect, Reason, RuleID}. Reason is a stable machine-readable code (rule_matched or no_matching_rule) and RuleID names the deciding rule β€” ready for auth-decision logging downstream.

πŸ”€ Combining algorithms

FirstApplicable (default), DenyOverrides, and AllowOverrides are built in; supply your own via WithCombiner. Set the fallthrough with WithDefaultEffect (defaults to Deny).

πŸ›  Develop

task build    # go build ./...
task test     # go test ./...
task ci       # build + vet + test + lint
task lint     # gofmt check + golangci-lint + yamllint

βš–οΈ License

MIT Β© 2026 Shane

Documentation ΒΆ

Overview ΒΆ

Package authz is a small, dependency-free, general-purpose authorization engine. The consuming application configures the model; nothing is hard-coded to a particular scheme.

The building blocks are a Request (subject, resource, action, and optional attributes), a set of Rules, and a Policy that combines them into a Decision carrying an Effect (Allow or Deny) plus a machine-readable Reason for downstream logging. Deny is the zero value of Effect, so every unconfigured or unmatched path fails safe.

The same engine expresses several common models:

  • default-deny-all: NewPolicy() with no rules denies everything.
  • allow / RBAC-style: assign subjects to roles and grant roles (resource, action) permissions with RBAC, on a Deny default.
  • responsibility matrix: a subject x resource x action table, with per-dimension wildcards and most-specific-wins precedence, via Matrix.

The package is framework-agnostic: it returns decisions and ships no HTTP/gRPC middleware. Optional decision logging uses the standard library log/slog only.

Example (DefaultDeny) ΒΆ

Example_defaultDeny shows the baseline: a policy with no rules denies every request.

package main

import (
	"context"
	"fmt"

	authz "github.com/Bugs5382/go-authz"
)

func main() {
	p := authz.NewPolicy()

	d := p.Decide(context.Background(), authz.Request{
		Subject:  "alice",
		Resource: "report",
		Action:   "read",
	})

	fmt.Printf("allowed=%t effect=%s reason=%s\n", d.Allowed(), d.Effect, d.Reason)
}
Output:
allowed=false effect=deny reason=no_matching_rule
Example (Matrix) ΒΆ

Example_matrix shows a responsibility-matrix configuration. The application maps its own actions onto the action dimension: here view, edit, and delete. Deny cells and wildcards compose, most-specific wins.

package main

import (
	"context"
	"fmt"

	authz "github.com/Bugs5382/go-authz"
)

func main() {
	m := authz.NewMatrix("access").
		Allow(authz.Wildcard, "document", "view"). // anyone may view a document
		Allow("editor1", "document", "edit").      // editor1 may edit
		Deny("editor1", "document", "delete").     // but may not delete
		Allow("admin1", "document", "delete")      // admin1 may delete

	p := authz.NewPolicy().Add(m)
	ctx := context.Background()

	fmt.Printf("anyone view:    %t\n", p.Decide(ctx, authz.Request{Subject: "x", Resource: "document", Action: "view"}).Allowed())
	fmt.Printf("editor1 edit:   %t\n", p.Decide(ctx, authz.Request{Subject: "editor1", Resource: "document", Action: "edit"}).Allowed())
	fmt.Printf("editor1 delete: %t\n", p.Decide(ctx, authz.Request{Subject: "editor1", Resource: "document", Action: "delete"}).Allowed())
	fmt.Printf("admin1 delete:  %t\n", p.Decide(ctx, authz.Request{Subject: "admin1", Resource: "document", Action: "delete"}).Allowed())
}
Output:
anyone view:    true
editor1 edit:   true
editor1 delete: false
admin1 delete:  true
Example (Rbac) ΒΆ

Example_rbac shows an allow/RBAC-style configuration: subjects hold roles and roles are granted permissions, on top of a deny-by-default policy.

package main

import (
	"context"
	"fmt"

	authz "github.com/Bugs5382/go-authz"
)

func main() {
	rbac := authz.NewRBAC("rbac").
		Assign("alice", "editor").
		Assign("bob", "viewer").
		Grant("editor", authz.Wildcard, "write"). // editors write any resource
		Grant("viewer", "report", "read")         // viewers read the report

	p := authz.NewPolicy().Add(rbac)
	ctx := context.Background()

	alice := p.Decide(ctx, authz.Request{Subject: "alice", Resource: "post", Action: "write"})
	bob := p.Decide(ctx, authz.Request{Subject: "bob", Resource: "post", Action: "write"})

	fmt.Printf("alice write post: %t\n", alice.Allowed())
	fmt.Printf("bob write post:   %t\n", bob.Allowed())
}
Output:
alice write post: true
bob write post:   false

Index ΒΆ

Examples ΒΆ

Constants ΒΆ

View Source
const (
	// ReasonRuleMatched means an applicable rule produced the effect; the
	// Decision's RuleID names that rule.
	ReasonRuleMatched = "rule_matched"
	// ReasonNoMatch means no rule was applicable and the policy's default
	// effect was used; RuleID is empty.
	ReasonNoMatch = "no_matching_rule"
)

Reason codes describe, in machine-readable form, why a Decision has its Effect. They are intended for downstream auth-decision logging.

View Source
const Wildcard = "*"

Wildcard is the token that matches any value in an RBAC grant or a Matrix entry. An application that needs "*" to be a literal subject, resource, or action should not use RBAC/Matrix wildcards for that dimension.

Variables ΒΆ

This section is empty.

Functions ΒΆ

This section is empty.

Types ΒΆ

type CombiningAlgorithm ΒΆ

type CombiningAlgorithm func(ctx context.Context, req Request, rules []Rule, def Effect) Decision

CombiningAlgorithm folds the applicable rules of a policy into a single Decision. def is the policy's default effect, used when no rule decides. Applications may supply their own algorithm to WithCombiner.

type Decision ΒΆ

type Decision struct {
	Effect Effect
	Reason string
	RuleID string
}

Decision is the result of evaluating a Request.

Reason is a stable, machine-readable code (see the Reason constants) and RuleID names the rule responsible, or is empty when the default effect applied. Together they give downstream logging a complete, greppable record.

func AllowOverrides ΒΆ

func AllowOverrides(ctx context.Context, req Request, rules []Rule, def Effect) Decision

AllowOverrides returns Allow if any applicable rule allows, otherwise Deny if any applicable rule denies, otherwise the default effect.

func DenyOverrides ΒΆ

func DenyOverrides(ctx context.Context, req Request, rules []Rule, def Effect) Decision

DenyOverrides returns Deny if any applicable rule denies, otherwise Allow if any applicable rule allows, otherwise the default effect. Use it when a single denial must veto every grant.

func FirstApplicable ΒΆ

func FirstApplicable(ctx context.Context, req Request, rules []Rule, def Effect) Decision

FirstApplicable returns the verdict of the first applicable rule, in order, and falls back to the default effect when none applies. It is the default combining algorithm.

func (Decision) Allowed ΒΆ

func (d Decision) Allowed() bool

Allowed reports whether the decision permits the request.

type Effect ΒΆ

type Effect uint8

Effect is the outcome an authorization decision can carry.

Deny is the zero value on purpose: an uninitialised Effect, an unmatched request, or a mis-wired policy all fail safe by denying access.

const (
	// Deny refuses access. It is the zero value of Effect.
	Deny Effect = iota
	// Allow permits access.
	Allow
)

func (Effect) String ΒΆ

func (e Effect) String() string

String returns the lower-case name of the effect ("deny" or "allow").

type Evaluator ΒΆ

type Evaluator interface {
	Decide(ctx context.Context, req Request) Decision
}

Evaluator decides authorization Requests. Policy is the built-in implementation; the interface lets applications depend on the behaviour rather than the concrete type.

type MatchFunc ΒΆ

type MatchFunc func(ctx context.Context, req Request) bool

MatchFunc reports whether a rule applies to req. It backs the functional rule constructors and the composable matchers.

func ActionIs ΒΆ

func ActionIs(a string) MatchFunc

ActionIs matches requests whose Action equals a.

func All ΒΆ

func All(ms ...MatchFunc) MatchFunc

All matches when every provided matcher matches (logical AND). With no matchers it matches everything.

func Any ΒΆ

func Any(ms ...MatchFunc) MatchFunc

Any matches when at least one provided matcher matches (logical OR). With no matchers it matches nothing.

func ResourceIs ΒΆ

func ResourceIs(r string) MatchFunc

ResourceIs matches requests whose Resource equals r.

func SubjectIs ΒΆ

func SubjectIs(s string) MatchFunc

SubjectIs matches requests whose Subject equals s.

type Matrix ΒΆ

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

Matrix is a responsibility matrix: a subject x resource x action table that resolves to an Effect. An application maps its own relations (for example view, edit, delete) onto the action dimension.

Any dimension of an entry may be Wildcard ("*"). Lookup is most-specific wins: an entry with more exact (non-wildcard) dimensions takes precedence, and ties are broken in subject, then resource, then action order. The Matrix is applicable only when some entry matches; otherwise the request falls through to the policy default. The Matrix is a Rule.

func NewMatrix ΒΆ

func NewMatrix(id string) *Matrix

NewMatrix returns an empty Matrix with the given id.

func (*Matrix) Allow ΒΆ

func (m *Matrix) Allow(subject, resource, action string) *Matrix

Allow records an Allow cell. It is shorthand for Set(..., Allow).

func (*Matrix) Deny ΒΆ

func (m *Matrix) Deny(subject, resource, action string) *Matrix

Deny records a Deny cell. It is shorthand for Set(..., Deny).

func (*Matrix) Eval ΒΆ

func (m *Matrix) Eval(_ context.Context, req Request) (Effect, bool)

Eval returns the effect of the most specific matching cell, if any.

func (*Matrix) ID ΒΆ

func (m *Matrix) ID() string

ID returns the rule identifier.

func (*Matrix) Set ΒΆ

func (m *Matrix) Set(subject, resource, action string, effect Effect) *Matrix

Set records effect for the subject/resource/action cell (any may be Wildcard) and returns the Matrix for chaining.

type Option ΒΆ

type Option func(*Policy)

Option configures a Policy at construction.

func WithCombiner ΒΆ

func WithCombiner(c CombiningAlgorithm) Option

WithCombiner sets the combining algorithm. The default is FirstApplicable.

func WithDefaultEffect ΒΆ

func WithDefaultEffect(e Effect) Option

WithDefaultEffect sets the effect used when no rule applies. The default is Deny.

func WithLogger ΒΆ

func WithLogger(l *slog.Logger) Option

WithLogger enables decision logging via the standard library log/slog. Each decision is emitted at Debug level. A nil logger (the default) is silent.

func WithRules ΒΆ

func WithRules(rules ...Rule) Option

WithRules appends rules to the policy. It may be combined with Add.

type Policy ΒΆ

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

Policy is the built-in Evaluator. It holds an ordered set of rules, a default effect used when no rule decides, and a combining algorithm. The zero interior (no rules, Deny default, FirstApplicable) is a complete default-deny-all policy.

func NewPolicy ΒΆ

func NewPolicy(opts ...Option) *Policy

NewPolicy builds a Policy. With no options it denies everything.

func (*Policy) Add ΒΆ

func (p *Policy) Add(rules ...Rule) *Policy

Add appends rules and returns the policy for chaining.

func (*Policy) Decide ΒΆ

func (p *Policy) Decide(ctx context.Context, req Request) Decision

Decide evaluates req against the policy and returns the Decision.

type RBAC ΒΆ

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

RBAC is a role-based rule: subjects are assigned roles, and roles are granted (resource, action) permissions. It is applicable and yields Allow when one of the request subject's roles grants the requested resource and action; otherwise it is inapplicable (it never denies). Pair it with a Deny default so ungranted requests fall through to denial.

Grants may use Wildcard ("*") for the resource and/or action to grant all values of that dimension. RBAC is a Rule.

func NewRBAC ΒΆ

func NewRBAC(id string) *RBAC

NewRBAC returns an empty RBAC rule with the given id.

func (*RBAC) Assign ΒΆ

func (r *RBAC) Assign(subject string, roles ...string) *RBAC

Assign gives a subject one or more roles and returns the RBAC for chaining.

func (*RBAC) Eval ΒΆ

func (r *RBAC) Eval(_ context.Context, req Request) (Effect, bool)

Eval reports Allow (applicable) when a role of req.Subject grants the request.

func (*RBAC) Grant ΒΆ

func (r *RBAC) Grant(role, resource, action string) *RBAC

Grant permits a role to perform action on resource (either may be Wildcard) and returns the RBAC for chaining.

func (*RBAC) ID ΒΆ

func (r *RBAC) ID() string

ID returns the rule identifier.

type Request ΒΆ

type Request struct {
	Subject    string
	Resource   string
	Action     string
	Attributes map[string]any
}

Request describes an access attempt: a Subject trying to perform an Action on a Resource. Subject, Resource, and Action are opaque strings whose meaning is defined entirely by the consuming application.

Attributes carries optional context for attribute-based rules (for example ownership, tenant, or a relation such as owner/reviewer). It may be nil.

func (Request) Attr ΒΆ

func (r Request) Attr(key string) (any, bool)

Attr returns the attribute stored under key and whether it was present.

type Rule ΒΆ

type Rule interface {
	// ID is a stable identifier used as the Decision's RuleID.
	ID() string
	// Eval decides whether the rule applies and, if so, with what effect.
	Eval(ctx context.Context, req Request) (effect Effect, matched bool)
}

Rule is a single element of a Policy.

Eval reports whether the rule is applicable to req; when it is (matched true), the returned Effect is the rule's verdict. When matched is false the Effect is ignored and the rule takes no part in the decision.

func AllowWhen ΒΆ

func AllowWhen(id string, match MatchFunc) Rule

AllowWhen returns a rule that allows when match reports true.

func DenyWhen ΒΆ

func DenyWhen(id string, match MatchFunc) Rule

DenyWhen returns a rule that denies when match reports true.

func NewRule ΒΆ

func NewRule(id string, effect Effect, match MatchFunc) Rule

NewRule returns a rule with the given id that yields effect when match reports true, and is inapplicable otherwise.

Directories ΒΆ

Path Synopsis
examples
defaultdeny command
Command defaultdeny demonstrates the baseline behaviour of go-authz: a Policy with no rules configured denies every request.
Command defaultdeny demonstrates the baseline behaviour of go-authz: a Policy with no rules configured denies every request.
matrix command
Command matrix demonstrates go-authz's responsibility matrix: a subject x resource x action table with per-dimension wildcards and most-specific-wins precedence.
Command matrix demonstrates go-authz's responsibility matrix: a subject x resource x action table with per-dimension wildcards and most-specific-wins precedence.
rbac command
Command rbac demonstrates go-authz's RBAC rule: subjects are assigned roles, and roles are granted (resource, action) permissions.
Command rbac demonstrates go-authz's RBAC rule: subjects are assigned roles, and roles are granted (resource, action) permissions.

Jump to

Keyboard shortcuts

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