authz

package
v0.5.8 Latest Latest
Warning

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

Go to latest
Published: May 20, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package authz provides a unified authorization model for the dioad platform.

Capability model

All permissions and feature entitlements are expressed as Capability values using the colon-separated convention inherited from Casbin:

  • Feature entitlements: "feature:<name>" e.g. "feature:custom-domain"
  • Resource:action permissions: "<resource>:<action>" e.g. "tunnel:write"

Use FeatureCapability and Permission to construct typed capabilities rather than bare string literals.

Backends

Multiple Authorizer implementations are provided:

CasbinAuthorizer is the recommended production backend for both connect and connect-control.

Index

Constants

This section is empty.

Variables

View Source
var ErrForbidden = errors.New("forbidden")

ErrForbidden is returned by Authorizer.Can when a policy decision denies the requested capability. Use errors.Is(err, ErrForbidden) for flow control. A non-nil *Decision accompanies ErrForbidden. On infrastructure failures (non-policy errors), err is non-nil but *Decision is nil.

View Source
var ErrUnauthorized = errors.New("unauthorized")

ErrUnauthorized is returned by Authorizer.Can when the request cannot be evaluated because there is no authenticated principal, or the caller is otherwise not eligible for authorization. Use errors.Is(err, ErrUnauthorized) for flow control. This is distinct from ErrForbidden, which means a principal was evaluated and denied by policy.

Functions

This section is empty.

Types

type AllowAllAuthorizer

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

AllowAllAuthorizer is an Authorizer that grants every capability. It is intended for dev/test environments where RBAC enforcement is explicitly disabled via configuration. It must never be used in production.

func NewAllowAllAuthorizer

func NewAllowAllAuthorizer(metadata PolicyMetadata) *AllowAllAuthorizer

NewAllowAllAuthorizer returns an AllowAllAuthorizer with the given policy metadata (used only for Metadata() introspection).

func (*AllowAllAuthorizer) Can

Can always returns an allowed Decision with ReasonAllowAll.

func (*AllowAllAuthorizer) Metadata

func (a *AllowAllAuthorizer) Metadata() PolicyMetadata

Metadata returns the policy metadata provided at construction.

func (*AllowAllAuthorizer) Privileges

Privileges returns a Privilege that grants every capability.

type Authorizer

type Authorizer interface {
	// Privileges returns the full capability set for the principal. It returns
	// nil when the principal has no recognised roles (not an error). Callers
	// should treat a nil Privilege as "no capabilities".
	Privileges(ctx context.Context, principalCtx *auth.PrincipalContext) (Privilege, error)

	// Can checks whether the principal holds cap. A non-nil *Decision is
	// returned for every policy outcome; nil only on infrastructure errors.
	// Use errors.Is(err, ErrUnauthorized) to detect a missing principal, and
	// errors.Is(err, ErrForbidden) to detect a policy denial.
	Can(ctx context.Context, principalCtx *auth.PrincipalContext, cap Capability) (*Decision, error)

	// Metadata returns the policy metadata for introspection.
	Metadata() PolicyMetadata
}

Authorizer determines whether a principal may exercise a capability.

Flow control

[Can] returns a non-nil *Decision for every policy outcome (allow or deny). It returns a nil *Decision only for infrastructure failures unrelated to policy.

Two sentinel errors signal distinct failure modes:

  • ErrUnauthorized — the request could not be evaluated because there is no authenticated principal (principalCtx is nil). This corresponds to an HTTP 401 condition.
  • ErrForbidden — the principal was authenticated but the policy denied the requested capability. This corresponds to an HTTP 403 condition; a non-nil *Decision with audit information always accompanies this error.

Example:

d, err := authorizer.Can(ctx, principalCtx, authz.Permission("tunnel", "write"))
if errors.Is(err, authz.ErrUnauthorized) {
    // no principal — redirect to login
}
if errors.Is(err, authz.ErrForbidden) {
    // denied by policy — d contains audit info
}

Connect pattern (fetch-once, check-many)

[Privileges] fetches the full capability set for a principal once; the caller then checks individual capabilities inline using Privilege.Has. This is efficient for handlers that perform multiple capability checks:

privs, _ := authorizer.Privileges(ctx, principalCtx)
if privs.Has(authz.FeatureCapability("custom-domain")) { ... }
if privs.Has(authz.Permission("tunnel", "write")) { ... }

func NewDefaultCasbinAuthorizer

func NewDefaultCasbinAuthorizer(metadata PolicyMetadata, logger zerolog.Logger) (Authorizer, error)

NewDefaultCasbinAuthorizer attempts to create a CasbinAuthorizer from the given metadata. If Casbin initialisation fails, it falls back to a RoleAuthorizer and logs a warning so operators can investigate.

type AuthorizerConfig

type AuthorizerConfig struct {
	// RoleCapabilities is the list of role→capability assignments.
	RoleCapabilities []RoleCapabilityConfig `mapstructure:"role-capabilities"`

	// RoleAliases maps external token role names to internal role names.
	// Only roles listed here (as keys) are accepted; others are dropped.
	RoleAliases map[string]string `mapstructure:"role-aliases"`

	// AllowAll disables all policy checks; every capability is granted.
	// Must only be set in dev/test environments.
	AllowAll bool `mapstructure:"allow-all"`
}

AuthorizerConfig is the top-level config for constructing an Authorizer from a YAML/mapstructure config source.

func (AuthorizerConfig) ToMetadata

func (c AuthorizerConfig) ToMetadata() PolicyMetadata

ToMetadata converts the config into a PolicyMetadata value suitable for constructing a RoleAuthorizer or CasbinAuthorizer.

type Capability

type Capability string

Capability is a named thing a principal is allowed to do or use. All capabilities use ':' as the separator (Casbin convention):

"feature:<name>"      — product entitlement (feature:custom-domain)
"<resource>:<action>" — resource:action permission (tunnel:write)

Use FeatureCapability or Permission to construct values; avoid bare string literals in application code.

func FeatureCapability

func FeatureCapability(name string) Capability

FeatureCapability constructs a feature-namespace capability. For example, FeatureCapability("custom-domain") returns "feature:custom-domain".

func Permission

func Permission(resource, action string) Capability

Permission constructs a resource:action capability. For example, Permission("tunnel", "write") returns "tunnel:write".

type CasbinAuthorizer

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

CasbinAuthorizer is an Authorizer backed by Casbin v2. It is the recommended production backend for both connect and connect-control.

Policy rules are loaded from PolicyMetadata.RoleCapabilities at construction. [Can] uses Casbin enforce for efficient per-action checks and records the granting role in the returned Decision.

func NewCasbinAuthorizer

func NewCasbinAuthorizer(metadata PolicyMetadata) (*CasbinAuthorizer, error)

NewCasbinAuthorizer creates a CasbinAuthorizer from the given PolicyMetadata. Each capability in RoleCapabilities is split on ":" to derive the Casbin (obj, act) pair and loaded as a policy rule. Returns an error if the Casbin model or any policy rule cannot be created.

func (*CasbinAuthorizer) Can

func (a *CasbinAuthorizer) Can(_ context.Context, principalCtx *auth.PrincipalContext, cap Capability) (*Decision, error)

Can checks whether the principal's roles grant cap using Casbin enforcement. The returned Decision includes GrantedBy — the first role that grants the capability — enabling fine-grained audit logging.

func (*CasbinAuthorizer) Metadata

func (a *CasbinAuthorizer) Metadata() PolicyMetadata

Metadata returns the policy metadata.

func (*CasbinAuthorizer) Privileges

func (a *CasbinAuthorizer) Privileges(_ context.Context, principalCtx *auth.PrincipalContext) (Privilege, error)

Privileges resolves the principal's roles from canonical role names or RoleAliases and returns the union of all matching role capabilities as a PrivilegeSet.

type Decision

type Decision struct {
	// Allowed is true when the capability was granted.
	Allowed bool

	// Reason is a stable token describing the outcome.
	Reason DecisionReason

	// GrantedBy is the Role that granted the capability.
	// Empty when Allowed is false or when AllowAllAuthorizer is used.
	GrantedBy Role

	// Required is the Capability that was evaluated.
	Required Capability
}

Decision records the outcome of an authorization check for audit logging. It is non-nil whenever the authorizer reaches a policy conclusion (allow or deny). It is nil only when the error is unrelated to policy — for example, an infrastructure failure in the underlying enforcement engine.

type DecisionReason

type DecisionReason string

DecisionReason is a stable string token that describes why an authorization decision was made. It is intended for structured audit logging; callers should still use errors.Is with ErrUnauthorized or ErrForbidden for flow control, depending on the failure mode.

const (
	// ReasonAllowAll is returned by [AllowAllAuthorizer] — every capability
	// is granted regardless of principal or policy.
	ReasonAllowAll DecisionReason = "allow_all"

	// ReasonGranted means a policy rule explicitly grants the capability.
	ReasonGranted DecisionReason = "granted"

	// ReasonDeniedNilPrincipal means no principal was supplied.
	ReasonDeniedNilPrincipal DecisionReason = "denied_nil_principal"

	// ReasonDeniedNoRoles means the principal carries no roles that appear in
	// the policy's RoleAliases map. Unmapped external roles are rejected.
	ReasonDeniedNoRoles DecisionReason = "denied_no_roles"

	// ReasonDeniedNoPermission means the principal's roles are recognised but
	// none of them grant the required capability.
	ReasonDeniedNoPermission DecisionReason = "denied_no_permission"
)

type MapAuthorizer

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

MapAuthorizer is an Authorizer backed by a principal-ID → PrivilegeSet map. It is useful for inline test configs and simple static deployments where each principal's capabilities are enumerated directly.

If a principal ID is not found in the map, Privileges returns nil (no capabilities). In a MultiAuthorizer chain this causes the next backend to be tried.

func NewMapAuthorizer

func NewMapAuthorizer(privileges map[string]*PrivilegeSet, metadata PolicyMetadata) *MapAuthorizer

NewMapAuthorizer creates a MapAuthorizer with the given principal→privileges map and policy metadata (used for Metadata() introspection).

func (*MapAuthorizer) Can

func (a *MapAuthorizer) Can(ctx context.Context, principalCtx *auth.PrincipalContext, cap Capability) (*Decision, error)

Can checks whether the principal's mapped PrivilegeSet contains cap.

func (*MapAuthorizer) Metadata

func (a *MapAuthorizer) Metadata() PolicyMetadata

Metadata returns the policy metadata.

func (*MapAuthorizer) Privileges

func (a *MapAuthorizer) Privileges(_ context.Context, principalCtx *auth.PrincipalContext) (Privilege, error)

Privileges looks up the principal's ID in the map. Returns nil when not found.

type MultiAuthorizer

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

MultiAuthorizer chains multiple Authorizer backends. [Privileges] returns the result from the first backend that returns a non-nil Privilege; [Can] similarly short-circuits on the first non-nil Privilege result. If all backends return nil Privileges the principal is denied.

This matches the connect MultiAuthoriser semantics and is useful for composing inline + dynamic backends.

func NewMultiAuthorizer

func NewMultiAuthorizer(authorizers ...Authorizer) *MultiAuthorizer

NewMultiAuthorizer creates a MultiAuthorizer from the provided backends. The metadata from the first backend is used for Metadata() introspection.

func (*MultiAuthorizer) Can

func (m *MultiAuthorizer) Can(ctx context.Context, principalCtx *auth.PrincipalContext, cap Capability) (*Decision, error)

Can checks whether the first backend that returns a non-nil Privilege grants cap. If no backend recognises the principal, the request is denied.

func (*MultiAuthorizer) Metadata

func (m *MultiAuthorizer) Metadata() PolicyMetadata

Metadata returns the metadata from the first backend, or an empty PolicyMetadata when no backends are configured.

func (*MultiAuthorizer) Privileges

func (m *MultiAuthorizer) Privileges(ctx context.Context, principalCtx *auth.PrincipalContext) (Privilege, error)

Privileges iterates the backends in order and returns the first non-nil Privilege. Returns nil when no backend recognises the principal.

type PolicyMetadata

type PolicyMetadata struct {
	// RoleCapabilities maps each internal Role to the set of Capabilities it grants.
	RoleCapabilities map[Role][]Capability

	// RoleAliases maps external role/group names (from IdP tokens) to internal
	// Roles. Canonical internal role names are accepted when they appear in
	// RoleCapabilities; otherwise only aliases listed here are accepted, and
	// unrecognised role strings are ignored.
	RoleAliases map[string]Role
}

PolicyMetadata describes a static role→capability policy used to configure RoleAuthorizer and CasbinAuthorizer.

func CloneMetadata

func CloneMetadata(m PolicyMetadata) PolicyMetadata

CloneMetadata returns a deep copy of m.

func MergeRoleAliases

func MergeRoleAliases(meta PolicyMetadata, aliases map[string]Role) PolicyMetadata

MergeRoleAliases returns a copy of meta with the provided aliases merged in. Existing aliases are preserved; entries in aliases override duplicates.

type Privilege

type Privilege interface {
	Has(Capability) bool
}

Privilege represents the set of capabilities granted to a principal. Callers use [Has] to check whether a specific capability is present. The concrete implementation is PrivilegeSet.

func NewWildcardPrivilege

func NewWildcardPrivilege(ps *PrivilegeSet) Privilege

NewWildcardPrivilege returns a Privilege backed by ps that additionally grants any "resource:action" capability when "resource:any" is present.

type PrivilegeSet

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

PrivilegeSet is a capability set backed by a map. It implements Privilege and can be built incrementally via [Grant]. Use NewPrivilegeSet to create a set from a slice of known capabilities.

func NewPrivilegeSet

func NewPrivilegeSet(caps ...Capability) *PrivilegeSet

NewPrivilegeSet creates a PrivilegeSet containing the given capabilities.

func (*PrivilegeSet) Capabilities

func (p *PrivilegeSet) Capabilities() []Capability

Capabilities returns a snapshot of all capabilities in the set.

func (*PrivilegeSet) Grant

func (p *PrivilegeSet) Grant(cap Capability)

Grant adds cap to the set.

func (*PrivilegeSet) Has

func (p *PrivilegeSet) Has(cap Capability) bool

Has reports whether the set contains cap.

func (*PrivilegeSet) Union

func (p *PrivilegeSet) Union(other *PrivilegeSet) *PrivilegeSet

Union returns a new PrivilegeSet containing all capabilities from both sets (OR-merge). Neither input is modified.

type Role

type Role string

Role is an internal policy role name.

type RoleAuthorizer

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

RoleAuthorizer is an Authorizer backed by an in-memory role→capability map. Matching roles are unioned to form the principal's privilege set. It is suitable for static config-driven deployments and unit tests. For production use with complex policies, prefer CasbinAuthorizer.

func NewRoleAuthorizer

func NewRoleAuthorizer(metadata PolicyMetadata) *RoleAuthorizer

NewRoleAuthorizer creates a RoleAuthorizer from the given PolicyMetadata.

func (*RoleAuthorizer) Can

func (a *RoleAuthorizer) Can(ctx context.Context, principalCtx *auth.PrincipalContext, cap Capability) (*Decision, error)

Can checks whether the principal's union of role capabilities includes cap.

func (*RoleAuthorizer) Metadata

func (a *RoleAuthorizer) Metadata() PolicyMetadata

Metadata returns the policy metadata.

func (*RoleAuthorizer) Privileges

func (a *RoleAuthorizer) Privileges(_ context.Context, principalCtx *auth.PrincipalContext) (Privilege, error)

Privileges resolves the principal's roles from canonical role names or RoleAliases and returns the union of all matching role capabilities. Returns nil when the principal has no recognised roles.

type RoleCapabilityConfig

type RoleCapabilityConfig struct {
	// Role is the internal role name.
	Role string `mapstructure:"role"`

	// Capabilities is the list of capability strings granted to this role.
	// Each must use ':' as the separator, e.g. "tunnel:write", "feature:custom-domain".
	Capabilities []string `mapstructure:"capabilities"`
}

RoleCapabilityConfig maps a role name to the capabilities it grants. Used in YAML/mapstructure-based configuration.

Jump to

Keyboard shortcuts

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