rbac

package module
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Jan 8, 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 target and assertions
ctx = rbac.WithTarget(ctx, target)
ctx = rbac.WithAssertions(ctx, assertion1, assertion2)

// Extract from context
claims := rbac.CtxClaims(ctx)
target := rbac.CtxTarget(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

View Source
const (
	DecisionDeny = iota
	DecisionAllow
)

Variables

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

Functions

func RequestAuthorizer

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

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

func WithTarget added in v0.0.2

func WithTarget(ctx context.Context, target *Target) 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, error)
}

func CtxAssertions

func CtxAssertions(ctx context.Context) []Assertion

type AssertionFunc

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

func (AssertionFunc) Assert

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

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, error)
}

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

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) (d Decision, err error)

type DefaultRole

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

func NewRole

func NewRole(name string) *DefaultRole

func (*DefaultRole) AddChild

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

func (*DefaultRole) AddParent

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

func (*DefaultRole) AddPermissions

func (r *DefaultRole) AddPermissions(permission string, rest ...string)

func (*DefaultRole) Children

func (r *DefaultRole) Children() []Role

func (*DefaultRole) HasAncestor

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

func (*DefaultRole) HasDescendant

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

func (*DefaultRole) HasPermission

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

func (*DefaultRole) Name

func (r *DefaultRole) Name() string

func (*DefaultRole) Parents

func (r *DefaultRole) Parents() []Role

func (*DefaultRole) Permissions

func (r *DefaultRole) Permissions(children bool) []string

func (*DefaultRole) RePermissions added in v0.0.5

func (r *DefaultRole) RePermissions(children bool) []*regexp.Regexp

func (*DefaultRole) String

func (r *DefaultRole) String() string

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) (bool, error)

func (*RBAC) Role

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

func (*RBAC) Roles

func (rbac *RBAC) Roles() []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 interface {
	fmt.Stringer
	Name() string
	AddPermissions(permission string, rest ...string)
	HasPermission(permission string) bool
	Permissions(children bool) []string
	RePermissions(children bool) []*regexp.Regexp
	AddParent(Role) error
	Parents() []Role
	AddChild(Role) error
	Children() []Role
	HasAncestor(role Role) bool
	HasDescendant(role Role) bool
}

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
}

func CtxTarget added in v0.0.2

func CtxTarget(ctx context.Context) *Target

Directories

Path Synopsis
fx module

Jump to

Keyboard shortcuts

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