Documentation
¶
Overview ¶
Package warden provides composable permissions and authorization for Go.
Warden supports RBAC (role-based), ABAC (attribute-based), and ReBAC (relationship-based) authorization models individually or combined. It is tenant-scoped by default via forge.Scope and integrates with the Forge ecosystem for audit logging, feature flags, and async jobs.
eng, err := warden.NewEngine(
warden.WithStore(memStore),
)
result, err := eng.Check(ctx, &warden.CheckRequest{
Subject: warden.Subject{Kind: warden.SubjectUser, ID: "user_123"},
Action: warden.Action{Name: "read"},
Resource: warden.Resource{Type: "document", ID: "doc_456"},
})
Index ¶
- Constants
- Variables
- func AncestorNamespaces(path string) []string
- func IsAncestorOrSelf(ancestor, path string) bool
- func JoinNamespace(parent, child string) string
- func NamespaceFromContext(ctx context.Context) string
- func ScopeFromContext(ctx context.Context) (appID, tenantID string)
- func ValidateNamespacePath(path string, maxDepth int) error
- func WithNamespace(ctx context.Context, namespacePath string) context.Context
- func WithTenant(ctx context.Context, appID, tenantID string) context.Context
- type Action
- type Cache
- type CallOption
- type CheckRequest
- type CheckResult
- type Config
- type Decision
- type Engine
- func (e *Engine) CanI(ctx context.Context, subjectKind SubjectKind, ...) (bool, error)
- func (e *Engine) Check(ctx context.Context, req *CheckRequest, opts ...CallOption) (*CheckResult, error)
- func (e *Engine) Config() Config
- func (e *Engine) Enforce(ctx context.Context, req *CheckRequest, opts ...CallOption) error
- func (e *Engine) Health(ctx context.Context) error
- func (e *Engine) Plugins() *plugin.Registry
- func (e *Engine) SetExpressionEvaluator(ev ExpressionEvaluator)
- func (e *Engine) Start(_ context.Context) error
- func (e *Engine) Stop(_ context.Context) error
- func (e *Engine) Store() store.Store
- type Evaluator
- type ExpressionEvaluator
- type GraphWalker
- type ID
- type MatchInfo
- type Option
- func WithCache(c Cache) Option
- func WithConfig(c Config) Option
- func WithEvaluator(ev Evaluator) Option
- func WithExpressionEvaluator(ev ExpressionEvaluator) Option
- func WithGraphWalker(gw GraphWalker) Option
- func WithLogger(l log.Logger) Option
- func WithPlugin(x plugin.Plugin) Option
- func WithStore(s store.Store) Option
- type Prefix
- type Resource
- type Subject
- type SubjectKind
Constants ¶
const MaxNamespaceDepth = 8
MaxNamespaceDepth is the default cap on namespace nesting. Practical organizations don't go deeper than this; the cap keeps walking-cost predictable. Configurable via Config.MaxNamespaceDepth.
Variables ¶
var ( // ErrAccessDenied is returned when an authorization check fails. ErrAccessDenied = errors.New("warden: access denied") // ErrRoleNotFound is returned when a role cannot be found. ErrRoleNotFound = errors.New("warden: role not found") // ErrPermissionNotFound is returned when a permission cannot be found. ErrPermissionNotFound = errors.New("warden: permission not found") // ErrAssignmentNotFound is returned when an assignment cannot be found. ErrAssignmentNotFound = errors.New("warden: assignment not found") // ErrPolicyNotFound is returned when a policy cannot be found. ErrPolicyNotFound = errors.New("warden: policy not found") // ErrRelationNotFound is returned when a relation tuple cannot be found. ErrRelationNotFound = errors.New("warden: relation not found") // ErrResourceTypeNotFound is returned when a resource type cannot be found. ErrResourceTypeNotFound = errors.New("warden: resource type not found") // ErrSystemRoleImmutable is returned when trying to modify a system role. ErrSystemRoleImmutable = errors.New("warden: system role cannot be modified") // ErrSystemPermissionImmutable is returned when trying to modify a system permission. ErrSystemPermissionImmutable = errors.New("warden: system permission cannot be modified") // ErrAlreadyExists is the common base error for entity-uniqueness // violations. Use errors.Is(err, ErrAlreadyExists) to match any of the // specialized ErrDuplicate* errors below. // // Defined in wardenerr so that low-level subpackages (e.g. store/memory) // can return typed duplicate errors without an import cycle. ErrAlreadyExists = wardenerr.ErrAlreadyExists // ErrDuplicateRole is returned when a role would violate the // (tenant_id, namespace_path, slug) uniqueness constraint. ErrDuplicateRole = wardenerr.ErrDuplicateRole // ErrDuplicatePermission is returned when a permission would violate the // (tenant_id, namespace_path, name) uniqueness constraint. ErrDuplicatePermission = wardenerr.ErrDuplicatePermission // ErrDuplicatePolicy is returned when a policy would violate the // (tenant_id, namespace_path, name) uniqueness constraint. ErrDuplicatePolicy = wardenerr.ErrDuplicatePolicy // ErrDuplicateResourceType is returned when a resource type would violate // the (tenant_id, namespace_path, name) uniqueness constraint. ErrDuplicateResourceType = wardenerr.ErrDuplicateResourceType // ErrDuplicateAssignment is returned when a role is already assigned to a // subject within the same scope. Wraps ErrAlreadyExists. ErrDuplicateAssignment = wardenerr.ErrDuplicateAssignment // ErrDuplicateRelation is returned when a relation tuple already exists. // Wraps ErrAlreadyExists. ErrDuplicateRelation = wardenerr.ErrDuplicateRelation // ErrCyclicRoleInheritance is returned when role inheritance would create a cycle. ErrCyclicRoleInheritance = errors.New("warden: cyclic role inheritance detected") // ErrMaxMembersExceeded is returned when a role's member limit is reached. ErrMaxMembersExceeded = errors.New("warden: role max members exceeded") // ErrInvalidCondition is returned when a policy condition is malformed. ErrInvalidCondition = errors.New("warden: invalid policy condition") // ErrGraphDepthExceeded is returned when the relation graph walk exceeds max depth. ErrGraphDepthExceeded = errors.New("warden: relation graph depth exceeded") )
Functions ¶
func AncestorNamespaces ¶
AncestorNamespaces returns the path itself and every ancestor up to the tenant root, ordered from most-specific to most-general. The tenant root (empty string) is always the last element.
Examples:
AncestorNamespaces("") → [""]
AncestorNamespaces("eng") → ["eng", ""]
AncestorNamespaces("eng/platform") → ["eng/platform", "eng", ""]
AncestorNamespaces("eng/platform/sre") → ["eng/platform/sre", "eng/platform", "eng", ""]
Used by the engine to resolve roles, permissions, policies, and resource types across the namespace ancestor chain (cascading scope inheritance).
func IsAncestorOrSelf ¶
IsAncestorOrSelf reports whether ancestor is path itself or a strict ancestor of path. Both must be valid namespace paths (or empty string).
func JoinNamespace ¶
JoinNamespace concatenates two namespace paths with a "/" separator, dropping empties on either side. Used by the DSL parser to build absolute paths from nested namespace blocks.
func NamespaceFromContext ¶
NamespaceFromContext extracts the namespace path from the given context. Returns the empty string (tenant root) if not set.
func ScopeFromContext ¶
ScopeFromContext extracts the tenant scope from the given context. Returns appID and tenantID using the standard resolution chain: explicit WithTenant > forge.Scope > empty.
func ValidateNamespacePath ¶
ValidateNamespacePath checks that a path is well-formed. Empty string (the tenant root) is always valid. Otherwise the path is "/"-separated, each segment matches namespaceSegmentRegex, no reserved segment appears, and depth ≤ maxDepth (or MaxNamespaceDepth if 0).
func WithNamespace ¶
WithNamespace returns a context with the given namespace path overlaid on top of the existing tenant scope. Pass empty string for the tenant root.
Combine with WithTenant when needed:
ctx := warden.WithTenant(context.Background(), "app1", "t1") ctx = warden.WithNamespace(ctx, "engineering/platform")
Types ¶
type Action ¶
type Action struct {
Name string `json:"name"`
}
Action represents what the subject wants to do.
type Cache ¶
type Cache interface {
// Get returns a cached check result, if available.
Get(ctx context.Context, tenantID string, req *CheckRequest) (*CheckResult, bool)
// Set stores a check result in the cache.
Set(ctx context.Context, tenantID string, req *CheckRequest, result *CheckResult)
// InvalidateTenant removes all cached results for a tenant.
InvalidateTenant(ctx context.Context, tenantID string)
// InvalidateSubject removes all cached results for a specific subject.
InvalidateSubject(ctx context.Context, tenantID string, subjectKind SubjectKind, subjectID string)
}
Cache provides caching for authorization check results.
type CallOption ¶
type CallOption func(*callOptions)
CallOption is a functional option applied per-call on Check, Enforce, and CanI. It is distinct from Option, which configures the Engine at construction time.
func WithCallAppID ¶
func WithCallAppID(appID string) CallOption
WithCallAppID overrides the app ID for this single call.
func WithCallNamespacePath ¶
func WithCallNamespacePath(namespacePath string) CallOption
WithCallNamespacePath overrides the namespace path for this single call. Pass empty string to scope the call to the tenant root.
func WithCallTenantID ¶
func WithCallTenantID(tenantID string) CallOption
WithCallTenantID overrides the tenant ID for this single call. It takes precedence over context-derived scope and CheckRequest.TenantID.
type CheckRequest ¶
type CheckRequest struct {
Subject Subject `json:"subject"`
Action Action `json:"action"`
Resource Resource `json:"resource"`
Context map[string]any `json:"context,omitempty"`
TenantID string `json:"tenant_id,omitempty"` // Optional: overrides context-derived tenant.
NamespacePath string `json:"namespace_path,omitempty"` // Optional: overrides context-derived namespace.
}
CheckRequest is the input to an authorization check.
type CheckResult ¶
type CheckResult struct {
Allowed bool `json:"allowed"`
Decision Decision `json:"decision"`
Reason string `json:"reason,omitempty"`
MatchedBy []MatchInfo `json:"matched_by,omitempty"`
Obligations []string `json:"obligations,omitempty"`
EvalTimeNs int64 `json:"eval_time_ns"`
}
CheckResult is the outcome of an authorization check.
Obligations is the list of obligation names from PBAC policies that matched this check (allow OR deny). Obligations are side-effect signals — names of actions the calling system should perform (audit-log, require-mfa, notify-security, etc.). They don't change the Allowed/Decision outcome. The engine also fires the PolicyObligationFired plugin hook per obligation, so existing plugin pipelines (Chronicle audit, dispatchers) get them automatically.
type Config ¶
type Config struct {
// MaxGraphDepth is the maximum depth for ReBAC graph traversal.
// Defaults to 10.
MaxGraphDepth int `json:"max_graph_depth,omitempty"`
// CacheTTL is the time-to-live for cached check results.
// Zero means no caching.
CacheTTL time.Duration `json:"cache_ttl,omitempty"`
// EnableRBAC enables role-based access control evaluation.
// Defaults to true.
EnableRBAC *bool `json:"enable_rbac,omitempty"`
// EnableABAC enables attribute-based access control evaluation.
// Defaults to true.
EnableABAC *bool `json:"enable_abac,omitempty"`
// EnableReBAC enables relationship-based access control evaluation.
// Defaults to true.
EnableReBAC *bool `json:"enable_rebac,omitempty"`
// EnableCheckLog enables writing authorization check results to the
// check log store. Defaults to true.
EnableCheckLog *bool `json:"enable_check_log,omitempty"`
}
Config holds configuration for the Warden engine.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
type Decision ¶
type Decision string
Decision is the authorization outcome.
const ( // DecisionAllow means the request is permitted. DecisionAllow Decision = "allow" // DecisionDeny means the request is denied (generic). DecisionDeny Decision = "deny" // DecisionDenyExplicit means an explicit deny policy matched. DecisionDenyExplicit Decision = "deny_explicit" // DecisionDenyDefault means no matching allow rule was found. DecisionDenyDefault Decision = "deny_default" // DecisionDenyNoRoles means the subject has no roles assigned. DecisionDenyNoRoles Decision = "deny_no_roles" // DecisionDenyNoPerms means no role grants the required permission. DecisionDenyNoPerms Decision = "deny_no_perms" // DecisionDenyCondition means an ABAC condition blocked the request. DecisionDenyCondition Decision = "deny_condition" // DecisionDenyRelation means no matching relation was found. DecisionDenyRelation Decision = "deny_relation" )
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the central authorization engine. It coordinates RBAC, ReBAC, and ABAC evaluation, manages the store, and fires extension hooks.
func (*Engine) CanI ¶
func (e *Engine) CanI(ctx context.Context, subjectKind SubjectKind, subjectID, action, resourceType, resourceID string, opts ...CallOption) (bool, error)
CanI is a shorthand for a simple authorization check. Optional CallOption values override scope for this single call.
func (*Engine) Check ¶
func (e *Engine) Check(ctx context.Context, req *CheckRequest, opts ...CallOption) (*CheckResult, error)
Check performs an authorization check. This is the hot path. Optional CallOption values override scope for this single call.
func (*Engine) Enforce ¶
func (e *Engine) Enforce(ctx context.Context, req *CheckRequest, opts ...CallOption) error
Enforce returns an error if the authorization check is denied. Optional CallOption values override scope for this single call.
func (*Engine) SetExpressionEvaluator ¶
func (e *Engine) SetExpressionEvaluator(ev ExpressionEvaluator)
SetExpressionEvaluator overrides the resource-type expression evaluator at runtime. Used by the extension to wire dsl.NewEngineEvaluator after engine construction, since the evaluator depends on the engine's store.
type Evaluator ¶
type Evaluator interface {
Evaluate(ctx context.Context, policies []*policy.Policy, req *CheckRequest) (*CheckResult, error)
}
Evaluator evaluates ABAC/PBAC policies against a check request.
func DefaultEvaluator ¶
func DefaultEvaluator() Evaluator
DefaultEvaluator returns the built-in condition evaluator backed by the system wall clock.
func NewConditionEvaluator ¶
NewConditionEvaluator returns an Evaluator that uses the supplied clock for PBAC time-window evaluation (NotBefore / NotAfter). Pass time.Now for production; tests can pass a fixed-time function.
type ExpressionEvaluator ¶
type ExpressionEvaluator interface {
EvalPermission(ctx context.Context, tenantID, namespacePath, resourceType, permName, subjectKind, subjectID, resourceID string) (matched bool, err error)
}
ExpressionEvaluator is an optional engine hook that evaluates resource-type permission expressions (the SpiceDB-style `viewer or editor or parent->read` expressions stored on ResourceType.Permissions[].Expression).
The DSL package implements this; wire it via WithExpressionEvaluator. When nil, resource-type expressions are inert (the relation graph walker still handles direct + transitive relations).
type GraphWalker ¶
type GraphWalker interface {
Walk(ctx context.Context, relStore relation.Store, tenantID, namespacePath string, req *CheckRequest) (allowed bool, path string, err error)
}
GraphWalker traverses the relation graph for ReBAC evaluation.
Walk is invoked with the namespace path of the request. Relations cascade like roles and policies: the walker follows tuples in the request namespace and every ancestor namespace (see AncestorNamespaces), so a relationship granted at a parent namespace is honored for descendant-scoped checks.
func DefaultGraphWalker ¶
func DefaultGraphWalker(maxDepth int) GraphWalker
DefaultGraphWalker returns a BFS graph walker with the given max depth.
type MatchInfo ¶
type MatchInfo struct {
Source string `json:"source"` // "rbac", "abac", "rebac"
RuleID string `json:"rule_id,omitempty"`
Detail string `json:"detail,omitempty"`
}
MatchInfo describes what rule matched during evaluation.
type Option ¶
type Option func(*Engine)
Option is a functional option for the Engine.
func WithEvaluator ¶
WithEvaluator sets the ABAC policy evaluator.
func WithExpressionEvaluator ¶
func WithExpressionEvaluator(ev ExpressionEvaluator) Option
WithExpressionEvaluator sets the resource-type permission expression evaluator. The DSL package's NewEngineEvaluator is the canonical implementation. When unset, resource-type expressions are inert.
func WithGraphWalker ¶
func WithGraphWalker(gw GraphWalker) Option
WithGraphWalker sets the ReBAC graph walker.
func WithPlugin ¶
WithPlugin registers a plugin with the engine.
type Resource ¶
type Resource struct {
Type string `json:"type"`
ID string `json:"id"`
Attributes map[string]any `json:"attributes,omitempty"`
}
Resource represents the target of an authorization check.
type Subject ¶
type Subject struct {
Kind SubjectKind `json:"kind"`
ID string `json:"id"`
Attributes map[string]any `json:"attributes,omitempty"`
}
Subject represents an actor in an authorization check.
type SubjectKind ¶
type SubjectKind string
SubjectKind identifies the type of actor making an authorization request.
const ( // SubjectUser represents a human user. SubjectUser SubjectKind = "user" // SubjectAPIKey represents an API key (e.g., from Keysmith). SubjectAPIKey SubjectKind = "api_key" // SubjectService represents a service-to-service caller. SubjectService SubjectKind = "service" // SubjectServiceAcct represents a service account. SubjectServiceAcct SubjectKind = "service_acct" )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
_examples
|
|
|
abac
command
Example: attribute-based access control with conditions.
|
Example: attribute-based access control with conditions. |
|
forge
command
Example: Warden as a Forge extension with API routes.
|
Example: Warden as a Forge extension with API routes. |
|
rbac
command
Example: pure RBAC with role inheritance.
|
Example: pure RBAC with role inheritance. |
|
rebac
command
Example: Zanzibar-style ReBAC with transitive relations.
|
Example: Zanzibar-style ReBAC with transitive relations. |
|
standalone
command
Example: standalone Warden usage without Forge.
|
Example: standalone Warden usage without Forge. |
|
standalone-embed
command
Standalone-embed example — boots a Warden engine, applies an embedded .warden config tree, then runs a sample Check.
|
Standalone-embed example — boots a Warden engine, applies an embedded .warden config tree, then runs a sample Check. |
|
Package api provides HTTP handlers for the Warden authorization engine.
|
Package api provides HTTP handlers for the Warden authorization engine. |
|
Package assignment defines the Assignment entity (role→subject binding).
|
Package assignment defines the Assignment entity (role→subject binding). |
|
Package cache provides caching implementations for Warden check results.
|
Package cache provides caching implementations for Warden check results. |
|
Package checklog defines the check audit log Entry entity.
|
Package checklog defines the check audit log Entry entity. |
|
cmd
|
|
|
internal/cli
Package cli holds shared helpers for the warden CLI binaries.
|
Package cli holds shared helpers for the warden CLI binaries. |
|
warden
command
Command warden is the CLI for the .warden DSL.
|
Command warden is the CLI for the .warden DSL. |
|
warden-lsp
command
Command warden-lsp is a Language Server Protocol (LSP) server for the `.warden` DSL.
|
Command warden-lsp is a Language Server Protocol (LSP) server for the `.warden` DSL. |
|
components
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
pages
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
settings
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
widgets
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
Package dsl implements the .warden declarative configuration language.
|
Package dsl implements the .warden declarative configuration language. |
|
Package extension provides a Forge extension entry point for Warden.
|
Package extension provides a Forge extension entry point for Warden. |
|
Package id defines TypeID-based identity types for all Warden entities.
|
Package id defines TypeID-based identity types for all Warden entities. |
|
Package lsp implements the Warden Language Server Protocol server for the .warden DSL.
|
Package lsp implements the Warden Language Server Protocol server for the .warden DSL. |
|
Package middleware provides HTTP authorization middleware for Warden.
|
Package middleware provides HTTP authorization middleware for Warden. |
|
Package permission defines the Permission entity and its store interface.
|
Package permission defines the Permission entity and its store interface. |
|
Package plugin defines the plugin system for Warden.
|
Package plugin defines the plugin system for Warden. |
|
Package policy defines the Policy entity used for ABAC and PBAC evaluation.
|
Package policy defines the Policy entity used for ABAC and PBAC evaluation. |
|
Package relation defines the Tuple entity for ReBAC (Zanzibar-style relations).
|
Package relation defines the Tuple entity for ReBAC (Zanzibar-style relations). |
|
Package resourcetype defines the ResourceType entity for ReBAC schema definitions.
|
Package resourcetype defines the ResourceType entity for ReBAC schema definitions. |
|
Package role defines the Role entity and its store interface for RBAC.
|
Package role defines the Role entity and its store interface for RBAC. |
|
Package store defines the aggregate persistence interface.
|
Package store defines the aggregate persistence interface. |
|
contract
Package contract provides shared test contracts that every Warden store backend must satisfy.
|
Package contract provides shared test contracts that every Warden store backend must satisfy. |
|
memory
Package memory provides an in-memory implementation of the Warden composite store.
|
Package memory provides an in-memory implementation of the Warden composite store. |
|
mongo
Package mongo provides a MongoDB Store implementation for the Warden store.
|
Package mongo provides a MongoDB Store implementation for the Warden store. |
|
postgres
Package postgres provides a PostgreSQL implementation of the Warden composite store using grove ORM with Go-based migrations.
|
Package postgres provides a PostgreSQL implementation of the Warden composite store using grove ORM with Go-based migrations. |
|
sqlite
Package sqlite provides a SQLite Store implementation for embedded/edge use.
|
Package sqlite provides a SQLite Store implementation for embedded/edge use. |
|
Package wardenerr holds shared sentinel errors used across warden subpackages.
|
Package wardenerr holds shared sentinel errors used across warden subpackages. |