goentitlement

package module
v0.0.0-...-ec65718 Latest Latest
Warning

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

Go to latest
Published: May 25, 2025 License: MIT Imports: 9 Imported by: 0

README

GoEntitlement - General Purpose Entitlement Library

GoEntitlement is a flexible, high-performance authorization library for Go that supports multiple entitlement patterns including RBAC (Role-Based Access Control), ABAC (Attribute-Based Access Control), feature flags, and subscription management.

💩 This library was largely generated by Claude 4 to see what all the fuss is about. To be honest, it's as bad you would expect only worse. V4 makes less syntax mistakes, but it's just is rambling in incoherent as earlier models. For some reason this library has multilayered APIs, all inconsistent and all confusing. What I wanted was a quick abstraction over cedar that I could plug in elsewhere and "change my mind" about later without too much fuss. I needed principals, resources, actions. I got this pile of whatever it is. Don't use it, I'm going to write it myself when I get time and throw away almost all of this dogshit.

Features

  • Multiple Authorization Patterns: RBAC, ABAC, feature flags, subscription management
  • Flexible Storage: In-memory store with pluggable backend support
  • High Performance: Efficient caching and batch operations
  • Security: Audit logging, encryption support, policy validation
  • Extensible: Plugin architecture for custom stores and policies

Installation

go get github.com/benbenbenbenbenben/goentitlement

Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    
    "github.com/benbenbenbenbenben/goentitlement"
)

func main() {
    // Create manager with default in-memory store
    ctx := context.Background()
    
    // Create entities
    user := goentitlement.NewPrincipal("user123", goentitlement.PrincipalTypeUser)
    document := goentitlement.NewResource("doc456", goentitlement.ResourceTypeDocument)
    
    // Save entities to store (required for some operations)
    store := goentitlement.NewInMemoryEntitlementStore()
    managerWithStore := goentitlement.NewEntitlementManagerWithStore(store)
    
    store.SavePrincipal(ctx, user)
    store.SaveResource(ctx, document)
    
    // Grant permission
    entitlement := goentitlement.NewEntitlement(user, goentitlement.EntitlementTypePermission, "read")
    entitlement.Resource = &document
    
    err := managerWithStore.GrantEntitlement(ctx, entitlement)
    if err != nil {
        log.Fatal(err)
    }
    
    // Check permission
    allowed, err := managerWithStore.CheckPermission(ctx, user, "read", document)
    if err != nil {
        log.Fatal(err)
    }
    
    if allowed {
        fmt.Println("User can read the document")
    } else {
        fmt.Println("Access denied")
    }
}

Core Concepts

Principal

Represents an entity that can be granted entitlements (users, services, roles).

user := goentitlement.NewPrincipal("user123", goentitlement.PrincipalTypeUser)
user.Attributes["department"] = "engineering"
user.Groups = []string{"developers", "staff"}
Resource

Represents something that can be accessed or acted upon.

document := goentitlement.NewResource("doc456", goentitlement.ResourceTypeDocument)
document.Attributes["classification"] = "internal"
Entitlement

Represents a specific permission or capability.

entitlement := goentitlement.NewEntitlement(user, goentitlement.EntitlementTypePermission, "read")
entitlement.Resource = &document
entitlement.Conditions = map[string]interface{}{
    "time_of_day": "business_hours",
}

API Reference

EntitlementManager Interface

The main entry point for the library providing high-level operations:

Simple Authorization
  • CheckPermission(ctx, principal, action, resource) (bool, error)
  • CanAccess(ctx, principalID, resourceID, action) (bool, error)
Feature Flags
  • HasFeature(ctx, principal, feature) (bool, error)
  • IsFeatureEnabled(ctx, principalID, feature) (bool, error)
  • EnableFeature(ctx, principalID, feature, conditions) error
  • DisableFeature(ctx, principalID, feature) error
Subscription Management
  • HasSubscription(ctx, principal, tier) (bool, error)
  • GetSubscriptionTier(ctx, principalID) (string, error)
  • SetSubscription(ctx, principalID, tier, expiresAt) error
RBAC Helpers
  • HasRole(ctx, principal, role) (bool, error)
  • AssignRole(ctx, principalID, role) error
  • RemoveRole(ctx, principalID, role) error
  • GetRoles(ctx, principalID) ([]string, error)
Entitlement Management
  • GrantEntitlement(ctx, entitlement) error
  • RevokeEntitlement(ctx, entitlementID) error
  • ListEntitlements(ctx, principal) ([]Entitlement, error)
Batch Operations
  • CheckMultiplePermissions(ctx, requests) ([]AuthorizationResult, error)
  • GrantMultipleEntitlements(ctx, entitlements) error
Storage Interface

The library includes a pluggable storage system:

type EntitlementStore interface {
    // Policy storage
    SavePolicy(ctx, policy) error
    GetPolicy(ctx, id) (Policy, error)
    ListPolicies(ctx) ([]Policy, error)
    DeletePolicy(ctx, id) error
    
    // Entity storage
    SavePrincipal(ctx, principal) error
    GetPrincipal(ctx, id) (Principal, error)
    SaveResource(ctx, resource) error
    GetResource(ctx, id) (Resource, error)
    
    // Entitlement storage
    SaveEntitlement(ctx, entitlement) error
    GetEntitlement(ctx, id) (Entitlement, error)
    GetEntitlements(ctx, principalID) ([]Entitlement, error)
    DeleteEntitlement(ctx, id) error
    
    // Batch operations
    SaveEntitlements(ctx, entitlements) error
    
    // Health and maintenance
    Health(ctx) error
    Close() error
}

Usage Examples

Feature Flag Management
// Enable a feature for a user
err := manager.EnableFeature(ctx, "user123", "premium_analytics", map[string]interface{}{
    "trial_expires": time.Now().Add(30 * 24 * time.Hour),
})

// Check if user has feature
hasFeature, err := manager.HasFeature(ctx, user, "premium_analytics")
Subscription Management
// Set user subscription
expiryDate := time.Now().Add(365 * 24 * time.Hour)
err := manager.SetSubscription(ctx, "user123", "premium", &expiryDate)

// Check subscription tier
tier, err := manager.GetSubscriptionTier(ctx, "user123")

// Check if user has specific subscription
hasPremium, err := manager.HasSubscription(ctx, user, "premium")
RBAC Operations
// Assign role to user
err := manager.AssignRole(ctx, "user123", "admin")

// Check if user has role
hasRole, err := manager.HasRole(ctx, user, "admin")

// Get all user roles
roles, err := manager.GetRoles(ctx, "user123")

// Remove role
err = manager.RemoveRole(ctx, "user123", "admin")
Batch Operations
// Batch authorization checks
requests := []goentitlement.AuthorizationRequest{
    {Principal: user1, Action: "read", Resource: doc1},
    {Principal: user1, Action: "write", Resource: doc2},
    {Principal: user2, Action: "delete", Resource: doc3},
}

results, err := manager.CheckMultiplePermissions(ctx, requests)

// Batch entitlement granting
entitlements := []goentitlement.Entitlement{
    goentitlement.NewEntitlement(user1, goentitlement.EntitlementTypePermission, "read"),
    goentitlement.NewEntitlement(user2, goentitlement.EntitlementTypePermission, "write"),
}

err = manager.GrantMultipleEntitlements(ctx, entitlements)

Configuration

Manager Options
// Create manager with custom configuration
manager := goentitlement.NewEntitlementManagerWithStore(store,
    goentitlement.WithCache(5*time.Minute),
    goentitlement.WithAuditLogger(auditLogger),
    goentitlement.WithPolicyDir("./policies"),
    goentitlement.WithMetrics(metricsCollector),
)
Available Options
  • WithPolicyDir(dir string) - Set directory for policy files
  • WithCache(ttl time.Duration) - Enable caching with specified TTL
  • WithAuditLogger(logger AuditLogger) - Enable audit logging
  • WithMetrics(collector MetricsCollector) - Enable metrics collection

Error Handling

The library uses custom error types with specific error codes:

type EntitlementError struct {
    Code    ErrorCode
    Message string
    Cause   error
}

// Error codes
const (
    ErrorCodeNotFound     = "NOT_FOUND"
    ErrorCodeUnauthorized = "UNAUTHORIZED"
    ErrorCodeInvalidInput = "INVALID_INPUT"
    ErrorCodeStorageError = "STORAGE_ERROR"
    ErrorCodePolicyError  = "POLICY_ERROR"
)

Testing

Run the test suite:

go test -v

The library includes comprehensive tests covering:

  • Basic permission checks
  • Feature flag management
  • RBAC operations
  • Subscription management
  • Batch operations

Architecture

The library is built with a layered architecture:

  1. High-Level API - Simple methods for common operations
  2. Mid-Level API - Entity management and policy building
  3. Low-Level API - Direct Cedar policy access
  4. Storage Layer - Pluggable storage backends

Performance Considerations

  • Caching: Built-in caching layer for frequently accessed policies and entities
  • Batch Operations: Support for bulk authorization checks and entitlement operations
  • Concurrent Processing: Safe for concurrent use with proper locking mechanisms
  • Lazy Loading: Entities and policies loaded on-demand

Security Features

  • Audit Logging: Comprehensive audit trail for all authorization decisions
  • Policy Validation: Built-in validation for policies
  • Input Sanitization: Protection against injection attacks
  • Encryption Support: Optional encryption for sensitive data in storage

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

MIT License

Documentation

Overview

Package goentitlement provides a comprehensive entitlement management library for Go applications.

This library offers authorization, feature flag management, subscription handling, and role-based access control (RBAC) functionality. It supports both in-memory and file-based storage backends and integrates with the Cedar policy engine for advanced authorization scenarios.

Key features:

  • Simple permission checking and authorization
  • Feature flag management and evaluation
  • Subscription tier management with expiration support
  • Role-based access control (RBAC)
  • Batch operations for performance
  • Pluggable storage backends (in-memory, file-based)
  • Audit logging and metrics collection
  • Cedar policy integration for complex authorization rules

Basic usage:

manager := goentitlement.NewEntitlementManager()
principal := goentitlement.NewPrincipal("user123", goentitlement.PrincipalTypeUser)
resource := goentitlement.NewResource("doc456", goentitlement.ResourceTypeDocument)

allowed, err := manager.CheckPermission(ctx, principal, "read", resource)
if err != nil {
	log.Fatal(err)
}
if allowed {
	// Grant access
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AuditLogger

type AuditLogger interface {
	// LogAuthorization records authorization decisions for audit purposes
	LogAuthorization(ctx context.Context, req AuthorizationRequest, result AuthorizationResult)
	// LogEntitlementChange records changes to entitlements for audit purposes
	LogEntitlementChange(ctx context.Context, change EntitlementChange)
}

AuditLogger defines the interface for audit logging implementations.

Audit loggers capture important security events in the entitlement system, including authorization decisions and entitlement changes. This enables compliance reporting, security monitoring, and forensic analysis.

Example implementation:

type MyAuditLogger struct{}

func (l *MyAuditLogger) LogAuthorization(ctx context.Context, req AuthorizationRequest, result AuthorizationResult) {
	log.Printf("Authorization: %s %s %s -> %v", req.Principal.ID, req.Action, req.Resource.ID, result.Allowed)
}

func (l *MyAuditLogger) LogEntitlementChange(ctx context.Context, change EntitlementChange) {
	log.Printf("Entitlement %s: %s by %s", change.Operation, change.Entitlement.ID, change.Actor.ID)
}

type AuthorizationRequest

type AuthorizationRequest struct {
	// Principal specifies who is making the request
	Principal Principal `json:"principal"`
	// Action specifies what operation is being requested
	Action string `json:"action"`
	// Resource specifies what is being accessed
	Resource Resource `json:"resource"`
	// Context provides additional information for authorization decisions
	Context map[string]interface{} `json:"context,omitempty"`
}

AuthorizationRequest represents a request for authorization evaluation.

This structure encapsulates all the information needed to make an authorization decision: who is requesting access (Principal), what they want to do (Action), what they want to access (Resource), and any additional context.

Example:

request := AuthorizationRequest{
	Principal: user,
	Action:    "read",
	Resource:  document,
	Context: map[string]interface{}{
		"ip_address": "192.168.1.1",
		"user_agent": "MyApp/1.0",
	},
}

type AuthorizationResult

type AuthorizationResult struct {
	// Allowed indicates whether the request is authorized
	Allowed bool `json:"allowed"`
	// Reasons provides human-readable explanations for the decision
	Reasons []string `json:"reasons,omitempty"`
	// Policies lists the policies that influenced the decision
	Policies []string `json:"policies,omitempty"`
	// Duration records how long the authorization evaluation took
	Duration time.Duration `json:"duration"`
}

AuthorizationResult represents the outcome of an authorization evaluation.

This structure contains the authorization decision along with supporting information such as the reasoning behind the decision, applicable policies, and performance metrics.

Example:

result := AuthorizationResult{
	Allowed:  true,
	Reasons:  []string{"user has read permission on document"},
	Policies: []string{"document-access-policy"},
	Duration: 2 * time.Millisecond,
}

type CedarEngine

type CedarEngine interface {
	// Direct Cedar policy operations
	AddCedarPolicy(ctx context.Context, policyID, cedar string) error
	RemoveCedarPolicy(ctx context.Context, policyID string) error

	// Direct entity operations
	AddCedarEntity(ctx context.Context, entity cedar.Entity) error
	RemoveCedarEntity(ctx context.Context, entityID cedar.EntityUID) error

	// Raw authorization
	Authorize(ctx context.Context, req cedar.Request) (cedar.Decision, error)

	// Schema management
	SetSchema(ctx context.Context, schema string) error
	ValidatePolicy(ctx context.Context, policy string) error
}

CedarEngine interface for direct Cedar access

type Entitlement

type Entitlement struct {
	// ID is the unique identifier for this entitlement
	ID string `json:"id"`
	// Type categorizes the kind of entitlement
	Type EntitlementType `json:"type"`
	// Principal specifies who is granted this entitlement
	Principal Principal `json:"principal"`
	// Resource optionally specifies what resource this entitlement applies to
	Resource *Resource `json:"resource,omitempty"`
	// Action specifies what action is being authorized
	Action string `json:"action"`
	// Conditions optionally specifies additional constraints for this entitlement
	Conditions map[string]interface{} `json:"conditions,omitempty"`
	// ExpiresAt optionally specifies when this entitlement expires
	ExpiresAt *time.Time `json:"expires_at,omitempty"`
	// CreatedAt records when this entitlement was first created
	CreatedAt time.Time `json:"created_at"`
	// UpdatedAt records when this entitlement was last modified
	UpdatedAt time.Time `json:"updated_at"`
}

Entitlement represents a specific permission or capability granted to a principal.

Entitlements are the core authorization grants in the system. They specify what a principal is allowed to do, optionally on specific resources, with optional conditions and expiration times.

Example:

entitlement := Entitlement{
	ID:        "ent123",
	Type:      EntitlementTypePermission,
	Principal: user,
	Resource:  &document,
	Action:    "read",
	Conditions: map[string]interface{}{
		"time_of_day": "business_hours",
	},
	ExpiresAt: &expirationTime,
}

func NewEntitlement

func NewEntitlement(principal Principal, entitlementType EntitlementType, action string) Entitlement

NewEntitlement creates a new Entitlement with current timestamps.

An Entitlement grants a specific capability or permission to a principal. This is a low-level function for creating entitlements directly. In most cases, you should use the higher-level manager methods like GrantEntitlement, EnableFeature, AssignRole, or SetSubscription.

The principal parameter specifies who is granted the entitlement. The entitlementType parameter categorizes the type of entitlement being granted. The action parameter specifies what action is being authorized.

Example:

principal := goentitlement.NewPrincipal("user123", goentitlement.PrincipalTypeUser)
entitlement := goentitlement.NewEntitlement(
	principal,
	goentitlement.EntitlementTypePermission,
	"read",
)

type EntitlementChange

type EntitlementChange struct {
	// Operation describes what kind of change was made (grant, revoke, update, etc.)
	Operation string `json:"operation"`
	// Entitlement is the entitlement that was modified
	Entitlement Entitlement `json:"entitlement"`
	// Actor is the principal who made the change
	Actor Principal `json:"actor"`
	// Timestamp records when the change occurred
	Timestamp time.Time `json:"timestamp"`
}

EntitlementChange represents a modification to an entitlement for audit logging.

This structure captures all the information needed to audit changes to entitlements in the system, including what changed, who made the change, and when it occurred.

Example:

change := EntitlementChange{
	Operation:   "grant",
	Entitlement: newEntitlement,
	Actor:       adminUser,
	Timestamp:   time.Now(),
}

type EntitlementError

type EntitlementError struct {
	// Code categorizes the type of error
	Code ErrorCode `json:"code"`
	// Message provides a human-readable description of the error
	Message string `json:"message"`
	// Cause optionally wraps the underlying error that caused this failure
	Cause error `json:"cause,omitempty"`
}

EntitlementError represents a structured error with categorization and context.

This error type provides detailed information about failures in the entitlement system, including error codes for programmatic handling and optional cause chaining for debugging.

Example:

err := &EntitlementError{
	Code:    ErrorCodeNotFound,
	Message: "principal not found",
	Cause:   originalError,
}

func (*EntitlementError) Error

func (e *EntitlementError) Error() string

Error implements the error interface for EntitlementError.

It returns a formatted error message that includes the main message and any underlying cause information.

type EntitlementManager

type EntitlementManager interface {
	// CheckPermission verifies if a principal can perform an action on a resource
	CheckPermission(ctx context.Context, principal Principal, action string, resource Resource) (bool, error)
	// CanAccess is a convenience method for checking access using entity IDs
	CanAccess(ctx context.Context, principalID, resourceID, action string) (bool, error)

	// HasFeature checks if a principal has access to a specific feature
	HasFeature(ctx context.Context, principal Principal, feature string) (bool, error)
	// IsFeatureEnabled is a convenience method for checking features using principal ID
	IsFeatureEnabled(ctx context.Context, principalID, feature string) (bool, error)
	// EnableFeature grants access to a feature for a principal with optional conditions
	EnableFeature(ctx context.Context, principalID, feature string, conditions map[string]interface{}) error
	// DisableFeature revokes access to a feature for a principal
	DisableFeature(ctx context.Context, principalID, feature string) error

	// HasSubscription checks if a principal has a specific subscription tier
	HasSubscription(ctx context.Context, principal Principal, tier string) (bool, error)
	// GetSubscriptionTier returns the current subscription tier for a principal
	GetSubscriptionTier(ctx context.Context, principalID string) (string, error)
	// SetSubscription assigns a subscription tier to a principal with optional expiration
	SetSubscription(ctx context.Context, principalID, tier string, expiresAt *time.Time) error

	// HasRole checks if a principal has a specific role
	HasRole(ctx context.Context, principal Principal, role string) (bool, error)
	// AssignRole grants a role to a principal
	AssignRole(ctx context.Context, principalID, role string) error
	// RemoveRole revokes a role from a principal
	RemoveRole(ctx context.Context, principalID, role string) error
	// GetRoles returns all roles assigned to a principal
	GetRoles(ctx context.Context, principalID string) ([]string, error)

	// GrantEntitlement creates a new entitlement for a principal
	GrantEntitlement(ctx context.Context, entitlement Entitlement) error
	// RevokeEntitlement removes an existing entitlement
	RevokeEntitlement(ctx context.Context, entitlementID string) error
	// ListEntitlements returns all entitlements for a principal
	ListEntitlements(ctx context.Context, principal Principal) ([]Entitlement, error)

	// CheckMultiplePermissions efficiently checks multiple authorization requests
	CheckMultiplePermissions(ctx context.Context, requests []AuthorizationRequest) ([]AuthorizationResult, error)
	// GrantMultipleEntitlements efficiently creates multiple entitlements
	GrantMultipleEntitlements(ctx context.Context, entitlements []Entitlement) error

	// RawAuthorize performs low-level authorization with full request/response details
	RawAuthorize(ctx context.Context, req AuthorizationRequest) (AuthorizationResult, error)
}

EntitlementManager defines the main interface for the entitlement management system.

This is the primary interface that applications use to interact with the entitlement system. It provides high-level methods for authorization checks, feature flag management, subscription handling, role-based access control, and entitlement administration.

The interface is organized into logical groups:

  • Authorization: CheckPermission, CanAccess, RawAuthorize
  • Feature Flags: HasFeature, IsFeatureEnabled, EnableFeature, DisableFeature
  • Subscriptions: HasSubscription, GetSubscriptionTier, SetSubscription
  • RBAC: HasRole, AssignRole, RemoveRole, GetRoles
  • Entitlements: GrantEntitlement, RevokeEntitlement, ListEntitlements
  • Batch Operations: CheckMultiplePermissions, GrantMultipleEntitlements

Example usage:

manager := goentitlement.NewEntitlementManager()

// Check if user can read a document
allowed, err := manager.CheckPermission(ctx, user, "read", document)

// Check if user has premium features
hasPremium, err := manager.HasSubscription(ctx, user, "premium")

// Enable a feature for a user
err = manager.EnableFeature(ctx, user.ID, "advanced_analytics", nil)

func NewEntitlementManager

func NewEntitlementManager(opts ...ManagerOption) EntitlementManager

NewEntitlementManager creates a new EntitlementManager with default in-memory storage.

This is the primary entry point for creating an entitlement manager. The manager provides all core functionality including authorization checks, feature flag management, subscription handling, and role-based access control.

Example:

manager := goentitlement.NewEntitlementManager(
	goentitlement.WithCache(5*time.Minute),
	goentitlement.WithAuditLogger(myLogger),
)

func NewEntitlementManagerWithStore

func NewEntitlementManagerWithStore(store EntitlementStore, opts ...ManagerOption) EntitlementManager

NewEntitlementManagerWithStore creates an EntitlementManager with a custom storage backend.

Use this function when you need to specify a custom storage implementation, such as a file-based store or a database-backed store. The store parameter must implement the EntitlementStore interface.

Example:

store, err := goentitlement.NewFileEntitlementStore("/path/to/data")
if err != nil {
	log.Fatal(err)
}
manager := goentitlement.NewEntitlementManagerWithStore(store)

func NewManager

func NewManager(opts ...ManagerOption) EntitlementManager

NewManager creates a new EntitlementManager with default in-memory storage.

This is a convenience function that creates a manager with an in-memory store and applies any provided configuration options.

Example:

manager := NewManager(
	WithCache(5*time.Minute),
	WithAuditLogger(myLogger),
)

func NewManagerWithStore

func NewManagerWithStore(store EntitlementStore, opts ...ManagerOption) EntitlementManager

NewManagerWithStore creates a manager with a custom store

type EntitlementStore

type EntitlementStore interface {
	// SavePolicy persists a policy to storage
	SavePolicy(ctx context.Context, policy Policy) error
	// GetPolicy retrieves a policy by ID
	GetPolicy(ctx context.Context, id string) (Policy, error)
	// ListPolicies retrieves all policies from storage
	ListPolicies(ctx context.Context) ([]Policy, error)
	// DeletePolicy removes a policy from storage
	DeletePolicy(ctx context.Context, id string) error

	// SavePrincipal persists a principal to storage
	SavePrincipal(ctx context.Context, principal Principal) error
	// GetPrincipal retrieves a principal by ID
	GetPrincipal(ctx context.Context, id string) (Principal, error)
	// SaveResource persists a resource to storage
	SaveResource(ctx context.Context, resource Resource) error
	// GetResource retrieves a resource by ID
	GetResource(ctx context.Context, id string) (Resource, error)

	// SaveEntitlement persists an entitlement to storage, handling duplicates appropriately
	SaveEntitlement(ctx context.Context, entitlement Entitlement) error
	// GetEntitlement retrieves an entitlement by ID
	GetEntitlement(ctx context.Context, id string) (Entitlement, error)
	// GetEntitlements retrieves all entitlements for a specific principal
	GetEntitlements(ctx context.Context, principalID string) ([]Entitlement, error)
	// DeleteEntitlement removes an entitlement from storage
	DeleteEntitlement(ctx context.Context, id string) error

	// SaveEntitlements persists multiple entitlements in a batch operation
	SaveEntitlements(ctx context.Context, entitlements []Entitlement) error

	// Health checks the storage backend's health status
	Health(ctx context.Context) error
	// Close gracefully shuts down the storage backend
	Close() error
}

EntitlementStore defines the interface for persistent storage backends.

This interface abstracts the storage layer, allowing different implementations such as in-memory, file-based, or database-backed storage. All storage operations are context-aware and support proper error handling.

The interface is organized into logical groups:

  • Policy storage: For Cedar policies
  • Entity storage: For principals and resources
  • Entitlement storage: For entitlement grants
  • Batch operations: For performance optimization
  • Health and maintenance: For operational concerns

Example implementation structure:

type MyStore struct {
	// Implementation-specific fields
}

func (s *MyStore) SavePolicy(ctx context.Context, policy Policy) error {
	// Store the policy
	return nil
}

func NewFileEntitlementStore

func NewFileEntitlementStore(baseDir string) (EntitlementStore, error)

NewFileEntitlementStore creates a new file-based storage backend.

The file store persists data as JSON files in the specified base directory. It automatically creates the necessary subdirectories for different entity types. This store is suitable for applications that need persistence but don't require a full database.

The baseDir parameter specifies the root directory where data will be stored. Subdirectories will be created for principals, resources, entitlements, and policies.

Example:

store, err := goentitlement.NewFileEntitlementStore("/var/lib/myapp/entitlements")
if err != nil {
	log.Fatal(err)
}

func NewInMemoryEntitlementStore

func NewInMemoryEntitlementStore() EntitlementStore

NewInMemoryEntitlementStore creates a new in-memory storage backend.

The in-memory store is suitable for development, testing, or applications that don't require persistent storage. All data is lost when the application terminates.

This store is thread-safe and provides good performance for read-heavy workloads.

func NewInMemoryStore

func NewInMemoryStore() EntitlementStore

NewInMemoryStore creates a new in-memory storage backend.

The returned store is immediately ready for use and requires no initialization or cleanup. All internal data structures are properly initialized.

Example:

store := NewInMemoryStore()
manager := NewEntitlementManagerWithStore(store)

type EntitlementType

type EntitlementType string

EntitlementType represents the category of an entitlement.

Entitlement types help organize different kinds of capabilities or permissions that can be granted to principals.

const (
	// EntitlementTypePermission represents standard access permissions
	EntitlementTypePermission EntitlementType = "permission"
	// EntitlementTypeFeatureFlag represents feature toggles or capabilities
	EntitlementTypeFeatureFlag EntitlementType = "feature_flag"
	// EntitlementTypeSubscription represents subscription-based access
	EntitlementTypeSubscription EntitlementType = "subscription"
	// EntitlementTypeRole represents role assignments
	EntitlementTypeRole EntitlementType = "role"
)

type EntityManager

type EntityManager interface {
	// Principal management
	CreatePrincipal(ctx context.Context, principal Principal) error
	GetPrincipal(ctx context.Context, id string) (Principal, error)
	UpdatePrincipal(ctx context.Context, principal Principal) error
	DeletePrincipal(ctx context.Context, id string) error
	ListPrincipals(ctx context.Context, filter PrincipalFilter) ([]Principal, error)

	// Resource management
	CreateResource(ctx context.Context, resource Resource) error
	GetResource(ctx context.Context, id string) (Resource, error)
	UpdateResource(ctx context.Context, resource Resource) error
	DeleteResource(ctx context.Context, id string) error
	ListResources(ctx context.Context, filter ResourceFilter) ([]Resource, error)
}

EntityManager interface for entity management

type ErrorCode

type ErrorCode string

ErrorCode represents specific categories of errors that can occur in the system.

Error codes provide a standardized way to categorize and handle different types of failures, making it easier for applications to respond appropriately to different error conditions.

const (
	// ErrorCodeNotFound indicates a requested entity was not found
	ErrorCodeNotFound ErrorCode = "NOT_FOUND"
	// ErrorCodeUnauthorized indicates a request was not authorized
	ErrorCodeUnauthorized ErrorCode = "UNAUTHORIZED"
	// ErrorCodeInvalidInput indicates invalid or malformed input data
	ErrorCodeInvalidInput ErrorCode = "INVALID_INPUT"
	// ErrorCodeStorageError indicates a problem with the storage backend
	ErrorCodeStorageError ErrorCode = "STORAGE_ERROR"
	// ErrorCodePolicyError indicates a problem with policy evaluation
	ErrorCodePolicyError ErrorCode = "POLICY_ERROR"
	// ErrorCodeDuplicateEntitlement indicates an attempt to create a duplicate entitlement
	ErrorCodeDuplicateEntitlement ErrorCode = "DUPLICATE_ENTITLEMENT"
)

type FileStore

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

FileStore implements the EntitlementStore interface using the local filesystem.

This implementation stores all data as JSON files in a structured directory hierarchy. It provides persistent storage without requiring a database, making it suitable for applications that need data persistence but have simple storage requirements.

Directory structure:

baseDir/
├── principals/     (Principal entities as JSON files)
├── resources/      (Resource entities as JSON files)
├── entitlements/   (Entitlement grants as JSON files)
└── policies/       (Cedar policies as JSON files)

Performance characteristics:

  • Reads: O(1) for direct lookups, O(n) for queries requiring file scanning
  • Writes: O(1) for single operations, involves filesystem I/O
  • Storage: Human-readable JSON files, suitable for version control

Note: This implementation is not suitable for high-concurrency scenarios as it lacks file locking and transactional guarantees.

func NewFileStore

func NewFileStore(baseDir string) (*FileStore, error)

NewFileStore creates a new file-based storage backend.

The function creates the necessary directory structure if it doesn't exist. All directories are created with permissions 0755, and files are written with permissions 0644.

The baseDir parameter specifies the root directory where all data will be stored. Subdirectories will be created automatically for different entity types.

Example:

store, err := NewFileStore("/var/lib/myapp/entitlements")
if err != nil {
	log.Fatal("Failed to create file store:", err)
}
manager := NewEntitlementManagerWithStore(store)

func (*FileStore) Close

func (fs *FileStore) Close() error

Close is a no-op for FileStore as files are closed after each operation.

func (*FileStore) DeleteEntitlement

func (fs *FileStore) DeleteEntitlement(ctx context.Context, id string) error

DeleteEntitlement deletes an entitlement JSON file.

func (*FileStore) DeletePolicy

func (fs *FileStore) DeletePolicy(ctx context.Context, id string) error

DeletePolicy deletes a policy JSON file.

func (*FileStore) GetEntitlement

func (fs *FileStore) GetEntitlement(ctx context.Context, id string) (Entitlement, error)

GetEntitlement retrieves an entitlement from a JSON file.

func (*FileStore) GetEntitlements

func (fs *FileStore) GetEntitlements(ctx context.Context, principalID string) ([]Entitlement, error)

GetEntitlements retrieves all entitlements for a given principalID. This is a simplified implementation; a real one might need more complex querying or indexing.

func (*FileStore) GetPolicy

func (fs *FileStore) GetPolicy(ctx context.Context, id string) (Policy, error)

GetPolicy retrieves a policy from a JSON file.

func (*FileStore) GetPrincipal

func (fs *FileStore) GetPrincipal(ctx context.Context, id string) (Principal, error)

GetPrincipal retrieves a principal from a JSON file.

func (*FileStore) GetResource

func (fs *FileStore) GetResource(ctx context.Context, id string) (Resource, error)

GetResource retrieves a resource from a JSON file.

func (*FileStore) Health

func (fs *FileStore) Health(ctx context.Context) error

Health checks if the base directories are accessible.

func (*FileStore) ListPolicies

func (fs *FileStore) ListPolicies(ctx context.Context) ([]Policy, error)

ListPolicies retrieves all policies from the policies directory.

func (*FileStore) SaveEntitlement

func (fs *FileStore) SaveEntitlement(ctx context.Context, entitlement Entitlement) error

SaveEntitlement saves an entitlement to a JSON file.

func (*FileStore) SaveEntitlements

func (fs *FileStore) SaveEntitlements(ctx context.Context, entitlements []Entitlement) error

SaveEntitlements saves multiple entitlements. This is a basic implementation that calls SaveEntitlement for each. For performance, a real implementation might batch writes or use a transactional approach.

func (*FileStore) SavePolicy

func (fs *FileStore) SavePolicy(ctx context.Context, policy Policy) error

SavePolicy saves a policy to a JSON file.

func (*FileStore) SavePrincipal

func (fs *FileStore) SavePrincipal(ctx context.Context, principal Principal) error

SavePrincipal saves a principal to a JSON file.

func (*FileStore) SaveResource

func (fs *FileStore) SaveResource(ctx context.Context, resource Resource) error

SaveResource saves a resource to a JSON file.

type InMemoryStore

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

InMemoryStore implements EntitlementStore using in-memory storage.

This implementation stores all data in memory using Go maps, making it suitable for development, testing, or applications that don't require data persistence. All data is lost when the application terminates.

The store is thread-safe and uses read-write mutexes for optimal performance on read-heavy workloads. It includes an index for efficient principal-to-entitlement lookups and automatic duplicate handling.

Performance characteristics:

  • Reads: O(1) for direct lookups, O(n) for filtered operations
  • Writes: O(1) for single operations, O(n) for batch operations
  • Memory usage: Linear with the number of stored entities

func (*InMemoryStore) Close

func (s *InMemoryStore) Close() error

func (*InMemoryStore) DeleteEntitlement

func (s *InMemoryStore) DeleteEntitlement(ctx context.Context, id string) error

func (*InMemoryStore) DeletePolicy

func (s *InMemoryStore) DeletePolicy(ctx context.Context, id string) error

func (*InMemoryStore) GetEntitlement

func (s *InMemoryStore) GetEntitlement(ctx context.Context, id string) (Entitlement, error)

func (*InMemoryStore) GetEntitlements

func (s *InMemoryStore) GetEntitlements(ctx context.Context, principalID string) ([]Entitlement, error)

func (*InMemoryStore) GetPolicy

func (s *InMemoryStore) GetPolicy(ctx context.Context, id string) (Policy, error)

func (*InMemoryStore) GetPrincipal

func (s *InMemoryStore) GetPrincipal(ctx context.Context, id string) (Principal, error)

func (*InMemoryStore) GetResource

func (s *InMemoryStore) GetResource(ctx context.Context, id string) (Resource, error)

func (*InMemoryStore) Health

func (s *InMemoryStore) Health(ctx context.Context) error

func (*InMemoryStore) ListPolicies

func (s *InMemoryStore) ListPolicies(ctx context.Context) ([]Policy, error)

func (*InMemoryStore) SaveEntitlement

func (s *InMemoryStore) SaveEntitlement(ctx context.Context, entitlement Entitlement) error

func (*InMemoryStore) SaveEntitlements

func (s *InMemoryStore) SaveEntitlements(ctx context.Context, entitlements []Entitlement) error

func (*InMemoryStore) SavePolicy

func (s *InMemoryStore) SavePolicy(ctx context.Context, policy Policy) error

func (*InMemoryStore) SavePrincipal

func (s *InMemoryStore) SavePrincipal(ctx context.Context, principal Principal) error

func (*InMemoryStore) SaveResource

func (s *InMemoryStore) SaveResource(ctx context.Context, resource Resource) error

type ManagerConfig

type ManagerConfig struct {
	// Store specifies the storage backend for persistent data
	Store EntitlementStore
	// PolicyDir optionally specifies a directory containing Cedar policy files
	PolicyDir string

	// CacheEnabled determines whether to enable in-memory caching
	CacheEnabled bool
	// CacheTTL specifies how long cached items remain valid
	CacheTTL time.Duration
	// MaxConcurrency limits the number of concurrent operations
	MaxConcurrency int

	// AuditLogger optionally provides audit logging functionality
	AuditLogger AuditLogger
	// MetricsCollector optionally provides metrics collection functionality
	MetricsCollector MetricsCollector

	// EncryptionKey optionally provides a key for data encryption
	EncryptionKey []byte
	// SigningKey optionally provides a key for data signing
	SigningKey []byte
}

ManagerConfig represents configuration options for the EntitlementManager.

This structure allows customization of various aspects of the manager's behavior including storage, performance, observability, and security settings.

Example:

config := ManagerConfig{
	Store:            fileStore,
	CacheEnabled:     true,
	CacheTTL:         5 * time.Minute,
	MaxConcurrency:   20,
	AuditLogger:      myAuditLogger,
	MetricsCollector: myMetricsCollector,
}

type ManagerOption

type ManagerOption func(*ManagerConfig)

ManagerOption configures the EntitlementManager

func WithAuditLogger

func WithAuditLogger(logger AuditLogger) ManagerOption

func WithCache

func WithCache(ttl time.Duration) ManagerOption

func WithMetrics

func WithMetrics(collector MetricsCollector) ManagerOption

func WithPolicyDir

func WithPolicyDir(dir string) ManagerOption

type MetricsCollector

type MetricsCollector interface {
	// IncrementAuthorizationCount tracks the number of authorization requests
	IncrementAuthorizationCount(allowed bool)
	// RecordAuthorizationDuration tracks how long authorization requests take
	RecordAuthorizationDuration(duration time.Duration)
	// IncrementEntitlementCount tracks the number of entitlement operations
	IncrementEntitlementCount(operation string)
}

MetricsCollector defines the interface for metrics collection implementations.

Metrics collectors gather performance and usage statistics from the entitlement system, enabling monitoring, alerting, and capacity planning.

Example implementation:

type MyMetricsCollector struct{}

func (m *MyMetricsCollector) IncrementAuthorizationCount(allowed bool) {
	if allowed {
		authorizedCounter.Inc()
	} else {
		deniedCounter.Inc()
	}
}

func (m *MyMetricsCollector) RecordAuthorizationDuration(duration time.Duration) {
	authorizationDurationHistogram.Observe(duration.Seconds())
}

type Policy

type Policy struct {
	// ID is the unique identifier for this policy
	ID string `json:"id"`
	// Name is a human-readable name for this policy
	Name string `json:"name"`
	// Description explains what this policy does
	Description string `json:"description"`
	// Cedar contains the policy definition in Cedar language syntax
	Cedar string `json:"cedar"`
	// CreatedAt records when this policy was first created
	CreatedAt time.Time `json:"created_at"`
	// UpdatedAt records when this policy was last modified
	UpdatedAt time.Time `json:"updated_at"`
}

Policy represents a Cedar authorization policy.

Policies define the rules that govern authorization decisions in the system. They are written in the Cedar policy language and can express complex authorization logic including conditions, context evaluation, and hierarchical permissions.

Example:

policy := Policy{
	ID:          "doc-read-policy",
	Name:        "Document Read Access",
	Description: "Allows users to read documents they own",
	Cedar:       `permit(principal, action == "read", resource) when { principal == resource.owner };`,
}

type PolicyBuilder

type PolicyBuilder interface {
	// Create policies programmatically
	NewPolicy(name string) PolicyBuilder

	// Set policy components
	WithPrincipal(principal string) PolicyBuilder
	WithAction(action string) PolicyBuilder
	WithResource(resource string) PolicyBuilder
	WithCondition(condition string) PolicyBuilder

	// Build the policy
	Build() (Policy, error)

	// Generate Cedar policy text
	ToCedar() (string, error)
}

PolicyBuilder interface for building policies programmatically

type Principal

type Principal struct {
	// ID is the unique identifier for this principal
	ID string `json:"id"`
	// Type categorizes the kind of principal (user, service, role, group)
	Type PrincipalType `json:"type"`
	// Attributes stores custom key-value pairs for this principal
	Attributes map[string]interface{} `json:"attributes"`
	// Groups lists the group memberships for this principal
	Groups []string `json:"groups"`
	// CreatedAt records when this principal was first created
	CreatedAt time.Time `json:"created_at"`
	// UpdatedAt records when this principal was last modified
	UpdatedAt time.Time `json:"updated_at"`
}

Principal represents an entity that can be granted entitlements.

Principals are the subjects in authorization decisions. They can represent users, services, roles, or groups within your system. Each principal has a unique ID and can have custom attributes and group memberships.

Example:

user := Principal{
	ID:   "user123",
	Type: PrincipalTypeUser,
	Attributes: map[string]interface{}{
		"department": "engineering",
		"level":      "senior",
	},
	Groups: []string{"developers", "admins"},
}

func NewPrincipal

func NewPrincipal(id string, principalType PrincipalType) Principal

NewPrincipal creates a new Principal entity with current timestamps.

A Principal represents an entity that can be granted entitlements, such as a user, service account, role, or group. The principal serves as the subject in authorization decisions.

The id parameter should be unique within your system. The principalType parameter categorizes the principal for organizational and policy purposes.

Example:

user := goentitlement.NewPrincipal("user123", goentitlement.PrincipalTypeUser)
service := goentitlement.NewPrincipal("api-service", goentitlement.PrincipalTypeService)

type PrincipalFilter

type PrincipalFilter struct {
	// Type optionally filters by principal type
	Type *PrincipalType `json:"type,omitempty"`
	// Groups optionally filters by group membership
	Groups []string `json:"groups,omitempty"`
	// Limit optionally limits the number of results returned
	Limit int `json:"limit,omitempty"`
	// Offset optionally skips a number of results for pagination
	Offset int `json:"offset,omitempty"`
}

PrincipalFilter specifies criteria for querying principals.

This structure allows filtering principals by type, group membership, and provides pagination support for large result sets.

Example:

filter := PrincipalFilter{
	Type:   &goentitlement.PrincipalTypeUser,
	Groups: []string{"admins", "developers"},
	Limit:  50,
	Offset: 100,
}

type PrincipalType

type PrincipalType string

PrincipalType represents the category of a principal entity.

Principal types help organize and categorize different kinds of entities that can be granted entitlements in your system.

const (
	// PrincipalTypeUser represents individual human users
	PrincipalTypeUser PrincipalType = "user"
	// PrincipalTypeService represents automated services or applications
	PrincipalTypeService PrincipalType = "service"
	// PrincipalTypeRole represents a collection of permissions that can be assigned
	PrincipalTypeRole PrincipalType = "role"
	// PrincipalTypeGroup represents a collection of users or other principals
	PrincipalTypeGroup PrincipalType = "group"
)

type Resource

type Resource struct {
	// ID is the unique identifier for this resource
	ID string `json:"id"`
	// Type categorizes the kind of resource
	Type ResourceType `json:"type"`
	// Attributes stores custom key-value pairs for this resource
	Attributes map[string]interface{} `json:"attributes"`
	// Owner optionally specifies the principal that owns this resource
	Owner *Principal `json:"owner,omitempty"`
	// CreatedAt records when this resource was first created
	CreatedAt time.Time `json:"created_at"`
	// UpdatedAt records when this resource was last modified
	UpdatedAt time.Time `json:"updated_at"`
}

Resource represents something that can be accessed or acted upon.

Resources are the objects in authorization decisions. They represent anything that needs to be protected or controlled in your system, such as documents, API endpoints, features, or subscription tiers.

Example:

document := Resource{
	ID:   "doc123",
	Type: ResourceTypeDocument,
	Attributes: map[string]interface{}{
		"classification": "confidential",
		"project":        "alpha",
	},
}

func NewResource

func NewResource(id string, resourceType ResourceType) Resource

NewResource creates a new Resource entity with current timestamps.

A Resource represents something that can be accessed or acted upon in your system, such as documents, API endpoints, features, or subscription tiers. Resources serve as the object in authorization decisions.

The id parameter should be unique within your system for the given resource type. The resourceType parameter categorizes the resource for organizational and policy purposes.

Example:

document := goentitlement.NewResource("doc123", goentitlement.ResourceTypeDocument)
apiEndpoint := goentitlement.NewResource("/api/users", goentitlement.ResourceTypeAPI)

type ResourceFilter

type ResourceFilter struct {
	// Type optionally filters by resource type
	Type *ResourceType `json:"type,omitempty"`
	// OwnerID optionally filters by the resource owner's ID
	OwnerID *string `json:"owner_id,omitempty"`
	// Limit optionally limits the number of results returned
	Limit int `json:"limit,omitempty"`
	// Offset optionally skips a number of results for pagination
	Offset int `json:"offset,omitempty"`
}

ResourceFilter specifies criteria for querying resources.

This structure allows filtering resources by type, ownership, and provides pagination support for large result sets.

Example:

ownerID := "user123"
filter := ResourceFilter{
	Type:    &goentitlement.ResourceTypeDocument,
	OwnerID: &ownerID,
	Limit:   25,
}

type ResourceType

type ResourceType string

ResourceType represents the category of a resource entity.

Resource types help organize and categorize different kinds of objects that can be protected by entitlements in your system.

const (
	// ResourceTypeDocument represents files, documents, or content items
	ResourceTypeDocument ResourceType = "document"
	// ResourceTypeAPI represents API endpoints or web services
	ResourceTypeAPI ResourceType = "api"
	// ResourceTypeFeature represents application features or capabilities
	ResourceTypeFeature ResourceType = "feature"
	// ResourceTypeSubscription represents subscription tiers or plans
	ResourceTypeSubscription ResourceType = "subscription"
	// ResourceTypeCustom represents application-specific resource types
	ResourceTypeCustom ResourceType = "custom"
)

Jump to

Keyboard shortcuts

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