authz

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 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.

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.
	// errors.Is(err, ErrForbidden) is the idiomatic deny check.
	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. Use errors.Is(err, ErrForbidden) for flow control:

d, err := authorizer.Can(ctx, principalCtx, authz.Permission("tunnel", "write"))
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 via 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(err, ErrForbidden) for flow control.

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. Only aliases listed here are accepted; unmapped external roles are
	// rejected. This prevents a token from accidentally carrying an internal role
	// name (e.g. "admin") and gaining elevated privileges.
	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 via 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