Documentation
¶
Overview ¶
Package authz provides the authorization interface and types for the OpenTDF platform. It defines the contract between the authentication middleware and authorization engines.
Index ¶
- Constants
- Variables
- func AdapterConfigFromExternal(cfg Config) any
- func ContextWithResolverContext(ctx context.Context, rc *ResolverContext) context.Context
- func GetResolvedDataFromContext(ctx context.Context, key string) any
- func NewJWTClaimsRoleProvider(groupsClaim string, logger *logger.Logger) platformauthz.RoleProvider
- func RegisterFactory(name string, factory Factory)
- type Authorizer
- type CasbinV1Config
- type CasbinV2Config
- type CedarConfig
- type Config
- type Decision
- type DecisionMetadata
- type EnforcementResult
- type EngineType
- type Factory
- type JWTClaimsRoleProvider
- type Mode
- type OPAConfig
- type Option
- type PolicyConfig
- type Request
- type ResolverContext
- type ResolverFunc
- type ResolverRegistry
- type ResolverResource
- type RolesProviderConfig
- type ScopedResolverRegistry
- type SubjectExtractor
- func (e SubjectExtractor) BuildV1SubjectsFromToken(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) ([]string, []string, error)
- func (e SubjectExtractor) BuildV2SubjectsFromToken(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) ([]string, []string, error)
- func (e SubjectExtractor) ClaimsForRequest(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) (platformauthz.RequestClaims, error)
- func (e SubjectExtractor) ClientIDFromToken(ctx context.Context, token jwt.Token) (string, error)
- func (e SubjectExtractor) ContextWithClaims(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) (context.Context, error)
Constants ¶
const ( SubjectRolePrefix = "role:" SubjectClientPrefix = "client:" )
const DefaultEngine = "casbin"
DefaultEngine is the default authorization engine when none is specified.
Variables ¶
var ( ErrClientIDClaimNotConfigured = errors.New("no client ID claim configured") ErrClientIDClaimNotFound = errors.New("client ID claim not found") ErrClientIDClaimNotString = errors.New("client ID claim is not a string") ErrTokenRequired = errors.New("token is required") ErrSubjectExtractorLogger = errors.New("subject extractor logger is required") ErrSubjectExtractorRoles = errors.New("subject extractor role provider is required") )
Functions ¶
func AdapterConfigFromExternal ¶
AdapterConfigFromExternal maps external configuration to the appropriate internal adapter configuration. This provides a clean boundary between customer-facing config (stable) and internal adapter config (can evolve).
The external PolicyConfig is what customers configure in YAML/JSON. The internal adapter configs are what the authorization engines consume.
Engine selection:
- "casbin" (default): Returns CasbinV1Config or CasbinV2Config based on Version
- "cedar": Returns CedarConfig (future)
- "opa": Returns OPAConfig (future)
func ContextWithResolverContext ¶
func ContextWithResolverContext(ctx context.Context, rc *ResolverContext) context.Context
ContextWithResolverContext returns a new context with the ResolverContext attached. This is called by the auth interceptor after resolution to make cached data available to handlers.
func GetResolvedDataFromContext ¶
GetResolvedDataFromContext is a convenience function to retrieve cached data from the ResolverContext in the given context. Returns nil if no ResolverContext or key not found.
func NewJWTClaimsRoleProvider ¶
func NewJWTClaimsRoleProvider(groupsClaim string, logger *logger.Logger) platformauthz.RoleProvider
NewJWTClaimsRoleProvider constructs the default JWT claims role provider.
func RegisterFactory ¶
RegisterFactory registers an authorization engine factory. This is called during init() by each authorizer implementation.
Types ¶
type Authorizer ¶
type Authorizer interface {
// Authorize performs an authorization check.
//
// The implementation should:
// 1. Extract subjects (roles/username) from the token
// 2. Apply the appropriate authorization model based on configuration
// 3. For v2: Use ResourceContext dimensions if available
// 4. Return an error only for system failures, not for denied access
//
// Thread-safety: This method may be called concurrently from multiple goroutines.
Authorize(ctx context.Context, req *Request) (*Decision, error)
// Version returns the authorization model version this authorizer implements.
// Returns "v1" for legacy path-based, "v2" for RPC+dimensions, etc.
Version() string
// SupportsResourceAuthorization returns true if this authorizer
// supports resource-level authorization with dimensions.
// If false, ResourceContext will always be ignored.
SupportsResourceAuthorization() bool
}
Authorizer is the interface for pluggable authorization engines. Implementations must be thread-safe.
The OpenTDF platform supports multiple authorization versions:
- v1: Legacy path-based authorization using (subject, resource, action) tuple
- v2: RPC + dimensions authorization using (subject, rpc, dimensions) tuple
When implementing a new authorization engine (e.g., OPA, Cedar), implement this interface and register it via the Factory.
func New ¶
func New(cfg Config) (Authorizer, error)
New creates an Authorizer based on configuration. The engine is selected based on cfg.PolicyConfig.Engine:
- "casbin" (default): Casbin policy engine
- "cedar": AWS Cedar policy engine (future)
- "opa": Open Policy Agent engine (future)
For Casbin, the version determines the authorization model:
- "v1" (default): Legacy path-based model (subject, resource, action)
- "v2": RPC+dimensions model (subject, rpc, dimensions)
type CasbinV1Config ¶
type CasbinV1Config struct {
PolicyConfig
// RoleProvider extracts role/group subjects.
RoleProvider platformauthz.RoleProvider
// Adapter is a custom policy adapter (e.g., SQL).
// If nil, uses string adapter with Csv content.
Adapter persist.Adapter
}
CasbinV1Config configures the legacy path-based Casbin authorizer. This model uses (subject, resource, action) tuples for authorization.
Example policy:
p, role:admin, *, *, allow p, role:standard, /attributes*, read, allow
type CasbinV2Config ¶
type CasbinV2Config struct {
PolicyConfig
// RoleProvider extracts role/group subjects.
RoleProvider platformauthz.RoleProvider
// Adapter is a custom policy adapter (e.g., SQL).
// If nil, uses string adapter with Csv content.
Adapter persist.Adapter
}
CasbinV2Config configures the RPC + dimensions Casbin authorizer. This model uses (subject, rpc, dimensions) tuples for authorization.
Example policy:
p, role:admin, *, *, allow p, role:standard, /policy.attributes.AttributesService/*, read, allow p, role:ns-admin, /policy.attributes.AttributesService/*, *, ns:my-namespace, allow
type CedarConfig ¶
type CedarConfig struct {
PolicyConfig
// RoleProvider extracts role/group subjects.
RoleProvider platformauthz.RoleProvider
// SchemaPath is the path to the Cedar schema file.
SchemaPath string
// PoliciesPath is the path to Cedar policy files.
PoliciesPath string
// EntitiesPath is the path to Cedar entities file.
EntitiesPath string
}
CedarConfig configures the AWS Cedar authorization engine (future). Cedar provides a policy language with strong typing and formal verification.
type Config ¶
type Config struct {
// Policy configuration (claims, CSV, adapter, etc.)
PolicyConfig
// Logger for authorization decisions
Logger any
// Options for engine-specific configuration
Options []Option
}
Config provides configuration for authorization engine initialization.
type Decision ¶
type Decision struct {
// Allowed indicates whether the request is permitted.
Allowed bool
// Reason provides a human-readable explanation for audit logging.
Reason string
// Mode indicates which authorization model was used.
Mode Mode
// MatchedPolicy optionally contains the policy rule that matched (for debugging).
MatchedPolicy string
// Metadata contains supplemental information for audit logging.
Metadata DecisionMetadata
}
Decision represents the result of an authorization check.
type DecisionMetadata ¶
type DecisionMetadata struct {
// GroupsClaim is the configured JWT claim used to extract authorization groups.
GroupsClaim string
}
DecisionMetadata contains supplemental authorization decision metadata.
type EnforcementResult ¶
type EnforcementResult struct {
// Allowed indicates whether the request is permitted.
Allowed bool
// GroupsClaim is the configured JWT claim used to extract authorization groups.
GroupsClaim string
}
EnforcementResult represents the v1 authorization enforcement result.
type EngineType ¶
type EngineType string
EngineType identifies the authorization engine implementation.
const ( // EngineCasbin uses Casbin for policy enforcement. EngineCasbin EngineType = "casbin" )
type Factory ¶
type Factory func(cfg Config) (Authorizer, error)
Factory creates Authorizer instances based on configuration. This allows the platform to instantiate different authorization engines (Casbin, OPA, Cedar) based on configuration.
func GetFactory ¶
GetFactory returns the factory for the given name, if registered.
type JWTClaimsRoleProvider ¶
type JWTClaimsRoleProvider struct {
// contains filtered or unexported fields
}
JWTClaimsRoleProvider extracts role/group values from a configured JWT claim.
func (*JWTClaimsRoleProvider) Roles ¶
func (p *JWTClaimsRoleProvider) Roles(_ context.Context, token jwt.Token, _ platformauthz.RoleRequest) ([]string, error)
type OPAConfig ¶
type OPAConfig struct {
PolicyConfig
// RoleProvider extracts role/group subjects.
RoleProvider platformauthz.RoleProvider
// BundlePath is the path to the OPA bundle.
BundlePath string
// Query is the Rego query for authorization decisions.
Query string
}
OPAConfig configures the Open Policy Agent authorization engine (future). OPA provides a general-purpose policy engine with Rego query language.
type Option ¶
type Option func(*optionConfig)
Option is a functional option for authorizer configuration.
func WithRoleProvider ¶
func WithRoleProvider(provider platformauthz.RoleProvider) Option
WithRoleProvider sets the role provider used for subject extraction.
type PolicyConfig ¶
type PolicyConfig struct {
Builtin string `mapstructure:"-" json:"-"`
// Issuer is the configured token issuer for role provider requests.
Issuer string `mapstructure:"-" json:"-"`
// Engine specifies the authorization engine to use.
// - "casbin" (default): Casbin policy engine
// - "cedar": AWS Cedar policy engine (future)
// - "opa": Open Policy Agent engine (future)
Engine string `mapstructure:"engine" json:"engine" default:"casbin"`
// Version specifies the engine-specific authorization model version.
// For Casbin:
// - "v1" (default): Legacy path-based authorization (subject, resource, action)
// - "v2": RPC + dimensions authorization (subject, rpc, dimensions)
// v2 enables fine-grained resource-level authorization using AuthzResolvers.
Version string `mapstructure:"version" json:"version" default:"v1"`
// Username claim to use for user information
UserNameClaim string `mapstructure:"username_claim" json:"username_claim" default:"preferred_username"`
// Claim to use for group/role information
GroupsClaim string `mapstructure:"groups_claim" json:"groups_claim" default:"realm_access.roles"`
// Role provider configuration (resolved via StartOptions)
RolesProvider RolesProviderConfig `mapstructure:"roles_provider" json:"roles_provider"`
// Claim to use to reference idP clientID
ClientIDClaim string `mapstructure:"client_id_claim" json:"client_id_claim" default:"azp"`
// Deprecated: Use GroupsClaim instead
RoleClaim string `mapstructure:"claim" json:"claim" default:"realm_access.roles"`
// Deprecated: Use Casbin grouping statements g, <user/group>, <role>
RoleMap map[string]string `mapstructure:"map" json:"map"`
// Override the builtin policy with a custom policy
Csv string `mapstructure:"csv" json:"csv"`
// Extend the builtin policy with a custom policy
Extension string `mapstructure:"extension" json:"extension"`
Model string `mapstructure:"model" json:"model"`
// Adapter is intentionally any to allow future adapter config shapes beyond Casbin persist.Adapter.
// Conversion and validation happen downstream.
Adapter any `mapstructure:"-" json:"-"`
}
PolicyConfig contains the policy configuration for authorization.
type Request ¶
type Request struct {
// Subject information extracted from JWT
Token jwt.Token
// RPC method path (e.g., "/policy.attributes.AttributesService/UpdateAttribute")
// Used as the primary resource identifier in v2 model.
RPC string
// Action derived from RPC method (read, write, delete, unsafe).
// Used in v1 model; informational in v2 model.
Action string
// ResourceContext contains resolved authorization dimensions (namespace, attribute, etc.).
// If non-nil, indicates resource-level authorization should be attempted.
// Populated by ResolverRegistry when a resolver is registered for the RPC.
ResourceContext *ResolverContext
}
Request encapsulates all information needed for an authorization decision. This is the contract between the interceptor and any authorization engine.
type ResolverContext ¶
type ResolverContext struct {
Resources []*ResolverResource
// ResolvedData stores data fetched during resolution (e.g., attributes, namespaces)
// to avoid duplicate DB queries in handlers. Keys are service-defined strings.
// Handlers can retrieve this data via GetResolvedDataFromContext().
ResolvedData map[string]any
}
ResolverContext holds the resolved authorization context for a request. Multiple resources are supported for operations like "move from A to B" where authorization is required for both source and destination.
func NewResolverContext ¶
func NewResolverContext() ResolverContext
NewResolverContext creates a new empty resolver context.
func ResolverContextFromContext ¶
func ResolverContextFromContext(ctx context.Context) *ResolverContext
ResolverContextFromContext retrieves the ResolverContext from the context. Returns nil if not present (e.g., no resolver registered for the method).
func (*ResolverContext) GetResolvedData ¶
func (a *ResolverContext) GetResolvedData(key string) any
GetResolvedData retrieves cached data by key. Returns nil if key not found. Caller should type-assert the result.
func (*ResolverContext) NewResource ¶
func (a *ResolverContext) NewResource() *ResolverResource
NewResource creates and adds a new resource to the context.
func (*ResolverContext) SetResolvedData ¶
func (a *ResolverContext) SetResolvedData(key string, value any)
SetResolvedData stores data in the resolver context cache. Use this to cache fetched resources (e.g., attributes) for handler reuse. The key should be a descriptive string (e.g., "attribute", "namespace").
type ResolverFunc ¶
type ResolverFunc func(ctx context.Context, req connect.AnyRequest) (ResolverContext, error)
ResolverFunc is the function signature for service-provided resolvers. Services implement this to extract authorization dimensions from requests.
Parameters:
- ctx: Request context (includes auth info, can be used for DB calls)
- req: The connect request (use Deserialize helper to get typed proto)
Returns:
- ResolverContext with populated dimensions
- Error if resolution fails (results in 403)
Service maintainers are responsible for:
- Deserializing the request using the provided helper
- Extracting relevant fields
- Performing any required DB lookups
- Populating dimensions in ResolverContext
type ResolverRegistry ¶
type ResolverRegistry struct {
// contains filtered or unexported fields
}
ResolverRegistry holds resolver functions keyed by service method. This is the global registry used by the interceptor. It is thread-safe for concurrent read/write access.
func NewResolverRegistry ¶
func NewResolverRegistry() *ResolverRegistry
NewResolverRegistry creates a new resolver registry.
func (*ResolverRegistry) Get ¶
func (r *ResolverRegistry) Get(method string) (ResolverFunc, bool)
Get returns the resolver for a method, if registered.
func (*ResolverRegistry) ScopedForService ¶
func (r *ResolverRegistry) ScopedForService(serviceDesc *grpc.ServiceDesc) *ScopedResolverRegistry
ScopedForService creates a namespace-scoped registry that only allows registering resolvers for the given service's methods. This prevents services from registering resolvers for other services. Panics if serviceDesc is nil.
type ResolverResource ¶
ResolverResource represents a single resource's authorization dimensions. Each key-value pair is a dimension (e.g., "namespace" -> "hr").
func (*ResolverResource) AddDimension ¶
func (a *ResolverResource) AddDimension(dimension, value string)
AddDimension adds a dimension to the resource.
type RolesProviderConfig ¶
type RolesProviderConfig struct {
Name string `mapstructure:"name" json:"name"`
Config map[string]any `mapstructure:"config" json:"config"`
}
RolesProviderConfig contains role-provider selection and provider-specific settings.
type ScopedResolverRegistry ¶
type ScopedResolverRegistry struct {
// contains filtered or unexported fields
}
ScopedResolverRegistry is a namespace-scoped view of the registry. It only allows registering resolvers for the service it was created for.
func (*ScopedResolverRegistry) MustRegister ¶
func (s *ScopedResolverRegistry) MustRegister(methodName string, resolver ResolverFunc)
MustRegister is like Register but panics on error. Use during service initialization where errors should be fatal.
func (*ScopedResolverRegistry) Register ¶
func (s *ScopedResolverRegistry) Register(methodName string, resolver ResolverFunc) error
Register adds a resolver for a method in this service. Only the method name is required (e.g., "UpdateAttribute"), not the full path. The full path is derived from the ServiceDesc.
Returns an error if the method doesn't exist in the ServiceDesc.
func (*ScopedResolverRegistry) ServiceName ¶
func (s *ScopedResolverRegistry) ServiceName() string
ServiceName returns the service name this registry is scoped to.
type SubjectExtractor ¶
type SubjectExtractor struct {
// contains filtered or unexported fields
}
func NewSubjectExtractor ¶
func NewSubjectExtractor(userNameClaim, clientIDClaim string, roleProvider platformauthz.RoleProvider, log *logger.Logger) (SubjectExtractor, error)
NewSubjectExtractor constructs a subject extractor with required dependencies.
func (SubjectExtractor) BuildV1SubjectsFromToken ¶
func (e SubjectExtractor) BuildV1SubjectsFromToken(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) ([]string, []string, error)
BuildV1SubjectsFromToken preserves legacy subjects: claims.Groups plus claims.Subject as-is, including empty values. It does not emit an independent client ID subject or filter reserved role:/client: username prefixes.
func (SubjectExtractor) BuildV2SubjectsFromToken ¶
func (e SubjectExtractor) BuildV2SubjectsFromToken(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) ([]string, []string, error)
BuildV2SubjectsFromToken emits typed subjects: client IDs and roles use reserved prefixes, empty roles are filtered, and usernames with role:/client: prefixes are skipped to avoid collisions.
func (SubjectExtractor) ClaimsForRequest ¶
func (e SubjectExtractor) ClaimsForRequest(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) (platformauthz.RequestClaims, error)
func (SubjectExtractor) ClientIDFromToken ¶
func (SubjectExtractor) ContextWithClaims ¶
func (e SubjectExtractor) ContextWithClaims(ctx context.Context, token jwt.Token, req platformauthz.RoleRequest) (context.Context, error)
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package casbin registers the Casbin authorization engine and dispatches to the configured versioned implementation.
|
Package casbin registers the Casbin authorization engine and dispatches to the configured versioned implementation. |
|
v1
Package v1 provides the legacy path-based Casbin authorization implementation.
|
Package v1 provides the legacy path-based Casbin authorization implementation. |
|
v2
Package v2 provides the resource/dimension-based Casbin authorization implementation.
|
Package v2 provides the resource/dimension-based Casbin authorization implementation. |