rbac

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT, MIT Imports: 12 Imported by: 0

README

GoModularAuth RBAC Plugin

gomodularauth-rbac provides decoupled, deterministic role-based access control (RBAC) authorization for GoModularAuth applications. It operates purely over Core Principal identities independently of authentication mechanisms and transport protocols.

Installation

go get github.com/BladiCreator/go-modular-auth/plugins/rbac

Quick Start

package main

import (
	"context"
	"log"

	auth "github.com/BladiCreator/go-modular-auth"
	adaptermem "github.com/BladiCreator/go-modular-auth/adapters/memory"
	rbac "github.com/BladiCreator/go-modular-auth/plugins/rbac"
	"github.com/BladiCreator/go-modular-auth/plugins/rbac/plugins"
)

func main() {
	ctx := context.Background()

	// 1. Initialize plugin
	rbacPlugin := rbac.New()

	// 2. Initialize Core with RBAC plugin
	a, err := auth.New(auth.Config{
		Adapter: adaptermem.NewAdapter(),
		Plugins: []auth.PluginModule{rbacPlugin},
	})
	if err != nil {
		log.Fatalf("failed to initialize auth: %v", err)
	}

	// 3. Resolve strongly typed capability
	authz, err := auth.Get[plugins.RBAC](a)
	if err != nil {
		log.Fatalf("failed to resolve rbac capability: %v", err)
	}

	// 4. Define role
	err = authz.CreateRole(ctx, &rbac.Role{
		ID:          "role-editor",
		Name:        "editor",
		Scope:       rbac.ScopeGlobal,
		Permissions: []string{"articles:*", "media:upload"},
	})
	if err != nil {
		log.Fatalf("failed to create role: %v", err)
	}

	// 5. Assign role to principal
	principal := auth.Principal{
		Subject: "usr_12345",
		Type:    auth.PrincipalUser,
	}
	err = authz.AssignRole(ctx, &rbac.RoleAssignment{
		RoleID:           "role-editor",
		PrincipalSubject: principal.Subject,
		PrincipalType:    principal.Type,
		Scope:            rbac.ScopeGlobal,
	})
	if err != nil {
		log.Fatalf("failed to assign role: %v", err)
	}

	// 6. Authorize action
	res := rbac.NewResource("article", "art_987")
	err = authz.Authorize(ctx, principal, "articles:publish", res)
	if err != nil {
		log.Printf("access denied: %v", err)
		return
	}

	log.Println("principal authorized to publish article!")
}

Detailed guide: doc/plugins/rbac.md.

Documentation

Overview

Package rbac provides decoupled role-based access control (RBAC) authorization for GoModularAuth applications, operating over Core Principal identities independently of authentication protocols and transport mechanisms.

Index

Constants

View Source
const (
	ModelRBACRoles       = "rbac_roles"
	ModelRBACAssignments = "rbac_assignments"
)
View Source
const (
	EventRoleCreated     auth.EventName = "rbac.role.created"
	EventRoleUpdated     auth.EventName = "rbac.role.updated"
	EventRoleDeleted     auth.EventName = "rbac.role.deleted"
	EventRoleAssigned    auth.EventName = "rbac.role.assigned"
	EventRoleRevoked     auth.EventName = "rbac.role.revoked"
	EventAccessEvaluated auth.EventName = "rbac.access.evaluated"
)
View Source
const PluginID = "rbac"

PluginID identifies this plugin in the GoModularAuth runtime.

Variables

View Source
var (
	// ErrForbidden indicates that the principal is not authorized to perform the requested action.
	ErrForbidden = errors.New("principal is not authorized to perform this action")

	// ErrRoleNotFound indicates that the requested role does not exist.
	ErrRoleNotFound = errors.New("role not found")

	// ErrRoleAlreadyExists indicates that a role with the same name already exists.
	ErrRoleAlreadyExists = errors.New("role already exists")

	// ErrAssignmentNotFound indicates that the requested role assignment does not exist.
	ErrAssignmentNotFound = errors.New("role assignment not found")

	// ErrAssignmentAlreadyExists indicates that the role is already assigned to the target principal in the given scope.
	ErrAssignmentAlreadyExists = errors.New("role assignment already exists")

	// ErrInvalidInput indicates that one or more provided arguments failed validation.
	ErrInvalidInput = errors.New("invalid rbac input")

	// ErrInvalidScope indicates an unsupported or malformed scope value.
	ErrInvalidScope = errors.New("invalid role scope")

	// ErrPrincipalRequired indicates that a nil or empty principal was supplied for evaluation.
	ErrPrincipalRequired = errors.New("principal subject is required")

	// ErrPermissionRequired indicates that an empty permission string was supplied for evaluation.
	ErrPermissionRequired = errors.New("permission is required")

	// ErrRoleInUse indicates that a role cannot be deleted because it still has active assignments.
	ErrRoleInUse = errors.New("role is currently assigned to one or more principals")
)

Functions

func RegisterSchema

func RegisterSchema(s *schema.Schema) error

RegisterSchema registers the rbac_roles and rbac_assignments models in the compiled schema.

Types

type AccessEvaluatedPayload

type AccessEvaluatedPayload struct {
	PrincipalSubject string             `json:"principal_subject"`
	PrincipalType    auth.PrincipalType `json:"principal_type"`
	Permission       string             `json:"permission"`
	Allowed          bool               `json:"allowed"`
	MatchedRole      string             `json:"matched_role,omitempty"`
	ResourceType     string             `json:"resource_type,omitempty"`
	ResourceID       string             `json:"resource_id,omitempty"`
	OrganizationID   string             `json:"organization_id,omitempty"`
	ServiceID        string             `json:"service_id,omitempty"`
	Timestamp        time.Time          `json:"timestamp"`
}

AccessEvaluatedPayload is emitted when an authorization decision is evaluated.

type Config

type Config struct {
	Cache    cache.Cache
	CacheTTL time.Duration
	Clock    func() time.Time
}

Config holds configuration parameters for the RBAC plugin.

type Decision

type Decision struct {
	Allowed          bool               `json:"allowed"`
	Reason           string             `json:"reason,omitempty"`
	MatchedRole      string             `json:"matched_role,omitempty"`
	Permission       string             `json:"permission"`
	PrincipalSubject string             `json:"principal_subject"`
	PrincipalType    auth.PrincipalType `json:"principal_type"`
	EvaluatedAt      time.Time          `json:"evaluated_at"`
}

Decision represents the structured outcome of an authorization evaluation.

type Module

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

Module implements auth.PluginModule for decoupled role-based access control.

func New

func New(opts ...Option) *Module

New creates an installable RBAC PluginModule configured with options.

func (*Module) ID

func (m *Module) ID() string

ID returns the module's unique identifier.

func (*Module) Init

func (m *Module) Init(h *auth.Host) error

Init configures infrastructure and registers the strongly typed RBAC capability.

func (*Module) RegisterSchema

func (m *Module) RegisterSchema(s *schema.Schema) error

RegisterSchema contributes rbac_roles and rbac_assignments models to the compiled schema.

type Option

type Option func(*Config)

Option configures an RBAC plugin instance.

func WithCache

func WithCache(cacheImpl cache.Cache) Option

WithCache configures an explicit authorization cache.

func WithCacheTTL

func WithCacheTTL(ttl time.Duration) Option

WithCacheTTL sets the time-to-live for cached authorization decisions.

func WithClock

func WithClock(clock func() time.Time) Option

WithClock sets a custom time provider for testing.

type RBAC

type RBAC interface {
	// Authorize evaluates whether principal is granted permission on the target resource.
	// Returns nil if authorized, or ErrForbidden if unauthorized.
	Authorize(ctx context.Context, principal auth.Principal, permission string, resource Resource) error

	// IsAuthorized performs deterministic evaluation and returns true if authorized, false if not.
	IsAuthorized(ctx context.Context, principal auth.Principal, permission string, resource Resource) (bool, error)

	// Evaluate provides detailed evaluation metadata including matched role and reason.
	Evaluate(ctx context.Context, principal auth.Principal, permission string, resource Resource) (*Decision, error)

	// CreateRole registers a new role definition.
	CreateRole(ctx context.Context, role *Role) error

	// GetRole retrieves a role by its unique ID.
	GetRole(ctx context.Context, roleID string) (*Role, error)

	// GetRoleByName retrieves a role by its unique name.
	GetRoleByName(ctx context.Context, name string) (*Role, error)

	// UpdateRole updates an existing role definition and invalidates related caches.
	UpdateRole(ctx context.Context, role *Role) error

	// DeleteRole removes an existing role, failing if active assignments exist.
	DeleteRole(ctx context.Context, roleID string) error

	// ListRoles lists roles filtered by scope, or all roles if scope is empty.
	ListRoles(ctx context.Context, scope Scope) ([]*Role, error)

	// AssignRole grants a role to a principal within the specified scope.
	AssignRole(ctx context.Context, assignment *RoleAssignment) error

	// RevokeRole removes a specific role assignment.
	RevokeRole(ctx context.Context, assignmentID string) error

	// ListAssignmentsByPrincipal returns all active role assignments for a principal.
	ListAssignmentsByPrincipal(ctx context.Context, principal auth.Principal) ([]*RoleAssignment, error)

	// ListAssignmentsByRole returns all active assignments associated with a role ID.
	ListAssignmentsByRole(ctx context.Context, roleID string) ([]*RoleAssignment, error)

	// InvalidatePrincipalCache evicts all cached authorization decisions for a principal.
	InvalidatePrincipalCache(ctx context.Context, principalSubject string) error

	// InvalidateAllCache purges all authorization evaluation caches.
	InvalidateAllCache(ctx context.Context) error
}

RBAC is the strongly typed capability exposed by the gomodularauth-rbac plugin.

type Resource

type Resource struct {
	Type           string `json:"type,omitempty"`
	ID             string `json:"id,omitempty"`
	OrganizationID string `json:"organization_id,omitempty"`
	ServiceID      string `json:"service_id,omitempty"`
}

Resource identifies the target entity or boundary being accessed.

func EmptyResource

func EmptyResource() Resource

EmptyResource returns an empty resource.

func NewOrganizationResource

func NewOrganizationResource(resType, id, orgID string) Resource

NewOrganizationResource creates an organization-scoped resource descriptor.

func NewResource

func NewResource(resType, id string) Resource

NewResource creates a general resource descriptor.

func NewServiceResource

func NewServiceResource(resType, id, serviceID string) Resource

NewServiceResource creates a service-scoped resource descriptor.

type Role

type Role struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Scope       Scope     `json:"scope"`
	Permissions []string  `json:"permissions"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

Role represents a defined set of permissions within a specific scope.

type RoleAssignedPayload

type RoleAssignedPayload struct {
	AssignmentID     string             `json:"assignment_id"`
	RoleID           string             `json:"role_id"`
	PrincipalSubject string             `json:"principal_subject"`
	PrincipalType    auth.PrincipalType `json:"principal_type"`
	Scope            Scope              `json:"scope"`
	ScopeID          string             `json:"scope_id,omitempty"`
	Timestamp        time.Time          `json:"timestamp"`
}

RoleAssignedPayload is emitted when a role is assigned to a principal.

type RoleAssignment

type RoleAssignment struct {
	ID               string             `json:"id"`
	RoleID           string             `json:"role_id"`
	PrincipalSubject string             `json:"principal_subject"`
	PrincipalType    auth.PrincipalType `json:"principal_type"`
	Scope            Scope              `json:"scope"`
	ScopeID          string             `json:"scope_id,omitempty"` // organization ID or service ID; empty if ScopeGlobal
	CreatedAt        time.Time          `json:"created_at"`
}

RoleAssignment binds a Core Principal to a Role within a given scope.

type RoleCreatedPayload

type RoleCreatedPayload struct {
	RoleID    string    `json:"role_id"`
	Name      string    `json:"name"`
	Scope     Scope     `json:"scope"`
	Timestamp time.Time `json:"timestamp"`
}

RoleCreatedPayload is emitted when a new role is defined.

type RoleDeletedPayload

type RoleDeletedPayload struct {
	RoleID    string    `json:"role_id"`
	Timestamp time.Time `json:"timestamp"`
}

RoleDeletedPayload is emitted when a role is removed.

type RoleRevokedPayload

type RoleRevokedPayload struct {
	AssignmentID     string    `json:"assignment_id"`
	RoleID           string    `json:"role_id"`
	PrincipalSubject string    `json:"principal_subject"`
	Timestamp        time.Time `json:"timestamp"`
}

RoleRevokedPayload is emitted when a role assignment is revoked.

type RoleUpdatedPayload

type RoleUpdatedPayload struct {
	RoleID    string    `json:"role_id"`
	Name      string    `json:"name"`
	Scope     Scope     `json:"scope"`
	Timestamp time.Time `json:"timestamp"`
}

RoleUpdatedPayload is emitted when a role definition is updated.

type Scope

type Scope string

Scope defines the operational boundary where a role or assignment applies.

const (
	// ScopeGlobal applies across the entire system without boundary restrictions.
	ScopeGlobal Scope = "global"

	// ScopeOrganization scopes authorization within a specific tenant or organization.
	ScopeOrganization Scope = "organization"

	// ScopeService scopes authorization within a specific microservice or API boundary.
	ScopeService Scope = "service"
)

func (Scope) Valid

func (s Scope) Valid() bool

Valid returns true if the scope is recognized.

Directories

Path Synopsis
Package plugins exposes capability aliases for GoModularAuth plugins.
Package plugins exposes capability aliases for GoModularAuth plugins.

Jump to

Keyboard shortcuts

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