rbac

package module
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Feb 9, 2026 License: MIT Imports: 9 Imported by: 4

README

RBAC (Role-Based Access Control)

Go Reference Go Report Card codecov License

A comprehensive and flexible Go library for implementing role-based access control with hierarchical roles, regex pattern support, and custom assertions.

Features

  • Hierarchical Role Management: Create parent-child role relationships with permission inheritance
  • Flexible Permission System: Support for both exact string permissions and regex patterns
  • Custom Assertions: Inject custom business logic into authorization decisions
  • Context-Aware: Built-in support for request context and metadata
  • Performance Optimized: Object pooling and efficient permission checking
  • Configuration-Based: JSON/YAML configuration support for declarative setup
  • Circular Reference Protection: Prevents infinite loops in role hierarchies

Installation

go get -u github.com/gowool/rbac

Quick Start

Basic Usage
package main

import (
    "context"
    "fmt"

    "github.com/gowool/rbac"
)

func main() {
    // Create RBAC instance
    r := rbac.New()

    // Create roles
    adminRole := rbac.NewRole("admin")
    userRole := rbac.NewRole("user")

    // Add roles to RBAC (user inherits from admin)
    _ = r.AddRole(adminRole)
    _ = r.AddRole(userRole, adminRole)

    // Add permissions
    adminRole.AddPermissions("user.create", "user.delete", "user.*")
    userRole.AddPermissions("user.view", "user.edit")

    // Check permissions
    fmt.Println(r.IsGranted(context.Background(), "admin", "user.create")) // true
    fmt.Println(r.IsGranted(context.Background(), "user", "user.create"))  // true (inherited)
    fmt.Println(r.IsGranted(context.Background(), "user", "user.view"))    // true
    fmt.Println(r.IsGranted(context.Background(), "user", "system.admin")) // false
}
Configuration-Based Setup
package main

import "github.com/gowool/rbac"

func main() {
    config := rbac.Config{
        CreateMissingRoles: true,
        RoleHierarchy: []rbac.RoleConfig{
            {Role: "super_admin", Parents: []string{}},
            {Role: "admin", Parents: []string{"super_admin"}},
            {Role: "user", Parents: []string{"admin"}},
        },
        AccessControl: []rbac.AccessConfig{
            {Role: "super_admin", Permissions: []string{"*"}},
            {Role: "admin", Permissions: []string{"user.*", "post.*", "system.*"}},
            {Role: "user", Permissions: []string{"post.view", "post.create", "comment.*"}},
        },
    }

    rbac, err := rbac.NewWithConfig(config)
    if err != nil {
        panic(err)
    }

    // RBAC is now ready to use with all roles and permissions configured
}

Core Concepts

Roles

Roles are hierarchical entities that can have permissions and parent-child relationships:

// Create a role
adminRole := rbac.NewRole("admin")

// Add permissions (exact strings and regex patterns)
adminRole.AddPermissions("user.create", "user.delete", "post:\\d+:edit")

// Create hierarchy
userRole := rbac.NewRole("user")
userRole.AddParent(adminRole) // user inherits admin permissions

// Check permissions
hasPermission := adminRole.HasPermission("user.create")
Subjects

Subjects represent entities that can be authorized (users, services, etc.):

type UserSubject struct {
    userID string
    roles  []string
}

func (u *UserSubject) Roles() []string {
    return u.roles
}

subject := &UserSubject{userID: "123", roles: []string{"user", "editor"}}
Assertions

Assertions allow custom business logic in authorization decisions:

type TimeWindowAssertion struct {
    Start, End int // Hours of day
}

func (a *TimeWindowAssertion) Assert(ctx context.Context, role rbac.Role, permission string) (bool, error) {
    hour := time.Now().Hour()
    return hour >= a.Start && hour <= a.End, nil
}

// Use in authorization
target := &rbac.Target{
    Action:     "sensitive.operation",
    Assertions: []rbac.Assertion{&TimeWindowAssertion{9, 17}},
}
Context Integration

Built-in context functions for request-scoped data:

// Add claims to context
ctx := rbac.WithClaims(context.Background(), claims)

// Add assertions
ctx = rbac.WithAssertions(ctx, assertion1, assertion2)

// Extract from context
claims := rbac.CtxClaims(ctx)
assertions := rbac.CtxAssertions(ctx)

API Reference

Core Types
  • Role: Interface for role entities with permissions and hierarchy
  • Subject: Interface for entities that can be authorized
  • Authorizer: Interface for high-level authorization decisions
  • Assertion: Interface for custom authorization logic
  • Claims: Wraps subject with metadata
  • Target: Represents authorization requests
  • Decision: Authorization decision (allow/deny)
Main Functions
  • New() *RBAC: Create new RBAC instance
  • NewWithConfig(config Config) (*RBAC, error): Create RBAC with configuration
  • NewRole(name string) Role: Create new role
  • NewDefaultAuthorizer(rbac *RBAC) Authorizer: Create default authorizer
  • RequestAuthorizer(authorizer Authorizer, actions func(*http.Request) []string) func(*http.Request) error: Create HTTP middleware
Context Functions
  • WithClaims(ctx context.Context, claims *Claims) context.Context: Add claims to context
  • CtxClaims(ctx context.Context) *Claims: Extract claims from context
  • WithTarget(ctx context.Context, target *Target) context.Context: Add target to context
  • WithAssertions(ctx context.Context, assertions ...Assertion) context.Context: Add assertions to context

Configuration

The RBAC library supports JSON/YAML configuration for declarative setup:

{
  "createMissingRoles": true,
  "roleHierarchy": [
    {
      "role": "super_admin",
      "parents": []
    },
    {
      "role": "admin",
      "parents": ["super_admin"],
      "children": ["user"]
    },
    {
      "role": "user",
      "parents": ["admin"]
    }
  ],
  "accessControl": [
    {
      "role": "super_admin",
      "permissions": ["*"]
    },
    {
      "role": "admin",
      "permissions": ["user.*", "post.*", "system.view"]
    },
    {
      "role": "user",
      "permissions": ["post.view", "post.create", "comment.*"]
    }
  ]
}

License

Distributed under MIT License, please see license file within the code for more details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrCircularRef  = errors.New("circular reference detected")
	ErrRoleNil      = errors.New("role is nil")
	ErrRoleNotFound = errors.New("role not found")
	ErrInvalidRole  = errors.New("role must be a string or implement the Role interface")
)
View Source
var ErrDeny = errors.New("deny")

Functions

func RequestAuthorizer

func RequestAuthorizer(authorizer Authorizer, actions func(*http.Request) []string) func(*http.Request) Decision

func WithAssertions

func WithAssertions(ctx context.Context, assertions ...Assertion) context.Context

func WithClaims

func WithClaims(ctx context.Context, claims *Claims) context.Context

func WithRequestInfo added in v0.0.3

func WithRequestInfo(ctx context.Context, info RequestInfo) context.Context

Types

type AccessConfig

type AccessConfig struct {
	Role        string   `env:"ROLE" json:"role,omitempty" yaml:"role,omitempty"`
	Permissions []string `env:"PERMISSIONS" json:"permissions,omitempty" yaml:"permissions,omitempty"`
}

type Assertion

type Assertion interface {
	Assert(ctx context.Context, role *Role, permission string) bool
}

func CtxAssertions

func CtxAssertions(ctx context.Context) []Assertion

type AssertionFunc

type AssertionFunc func(ctx context.Context, role *Role, permission string) bool

func (AssertionFunc) Assert

func (f AssertionFunc) Assert(ctx context.Context, role *Role, permission string) bool

type AuthorizationChecker

type AuthorizationChecker interface {
	IsGranted(ctx context.Context, role any, permission string, assertions ...Assertion) bool
}

type Authorizer

type Authorizer interface {
	Authorize(ctx context.Context, claims *Claims, target *Target) Decision
}

type Claims

type Claims struct {
	Subject  Subject
	Metadata map[string]any
}

func CtxClaims

func CtxClaims(ctx context.Context) *Claims

type Config

type Config struct {
	CreateMissingRoles bool           `env:"CREATE_MISSING_ROLES" json:"createMissingRoles,omitempty" yaml:"createMissingRoles,omitempty"`
	RoleHierarchy      []RoleConfig   `envPrefix:"ROLE_CONFIG_" json:"roleHierarchy,omitempty" yaml:"roleHierarchy,omitempty"`
	AccessControl      []AccessConfig `envPrefix:"ACCESS_CONFIG_" json:"accessControl,omitempty" yaml:"accessControl,omitempty"`
}

type Decision

type Decision int8
const (
	DecisionDeny Decision = iota
	DecisionAllow
)

func (Decision) String

func (d Decision) String() string

type DefaultAuthorizer

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

func NewDefaultAuthorizer

func NewDefaultAuthorizer(rbac *RBAC) *DefaultAuthorizer

func (*DefaultAuthorizer) Authorize

func (a *DefaultAuthorizer) Authorize(ctx context.Context, claims *Claims, target *Target) Decision

func (*DefaultAuthorizer) AuthorizeE added in v0.0.9

func (a *DefaultAuthorizer) AuthorizeE(ctx context.Context, claims *Claims, target *Target) (d Decision, err error)

type RBAC

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

func New

func New() *RBAC

func NewWithConfig

func NewWithConfig(cfg Config) (*RBAC, error)

func (*RBAC) AddRole

func (rbac *RBAC) AddRole(role any, parents ...any) error

func (*RBAC) Apply

func (rbac *RBAC) Apply(cfg Config) error

func (*RBAC) CreateMissingRoles

func (rbac *RBAC) CreateMissingRoles() bool

func (*RBAC) HasRole

func (rbac *RBAC) HasRole(role any) (bool, error)

func (*RBAC) IsGranted

func (rbac *RBAC) IsGranted(ctx context.Context, role any, permission string, assertions ...Assertion) bool

func (*RBAC) IsGrantedE

func (rbac *RBAC) IsGrantedE(ctx context.Context, role any, permission string, assertions ...Assertion) (granted bool, err error)

func (*RBAC) Role

func (rbac *RBAC) Role(name string) (*Role, error)

func (*RBAC) Roles

func (rbac *RBAC) Roles() iter.Seq[*Role]

func (*RBAC) SetCreateMissingRoles

func (rbac *RBAC) SetCreateMissingRoles(createMissingRoles bool) *RBAC

type RequestInfo added in v0.0.3

type RequestInfo struct {
	Method     string
	Host       string
	RequestURI string
	Pattern    string
	RemoteAddr string
	Header     http.Header
	URL        *url.URL
	IsTLS      bool
}

func CtxRequestInfo added in v0.0.3

func CtxRequestInfo(ctx context.Context) RequestInfo

type Role

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

func NewRole

func NewRole(name string) *Role

func (*Role) AddChild

func (r *Role) AddChild(child *Role) error

func (*Role) AddParent

func (r *Role) AddParent(parent *Role) error

func (*Role) AddPermissions

func (r *Role) AddPermissions(permissions ...string)

func (*Role) Children

func (r *Role) Children() iter.Seq[*Role]

func (*Role) HasAncestor

func (r *Role) HasAncestor(role *Role) bool

func (*Role) HasDescendant

func (r *Role) HasDescendant(role *Role) bool

func (*Role) HasPermission

func (r *Role) HasPermission(permission string) bool

func (*Role) Name

func (r *Role) Name() string

func (*Role) Parents

func (r *Role) Parents() iter.Seq[*Role]

func (*Role) Permissions

func (r *Role) Permissions(children bool) iter.Seq[string]

func (*Role) String added in v0.0.9

func (r *Role) String() string

type RoleConfig

type RoleConfig struct {
	Role     string   `env:"ROLE" json:"role,omitempty" yaml:"role,omitempty"`
	Parents  []string `env:"PARENTS" json:"parents,omitempty" yaml:"parents,omitempty"`
	Children []string `env:"CHILDREN" json:"children,omitempty" yaml:"children,omitempty"`
}

type Subject

type Subject interface {
	Roles() []string
}

type Target

type Target struct {
	Action     string
	Assertions []Assertion
	Metadata   map[string]any
}

Directories

Path Synopsis
fx module

Jump to

Keyboard shortcuts

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