authz

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2025 License: MIT Imports: 16 Imported by: 0

README

Prefab Authorization (authz)

The Prefab Authorization plugin provides a flexible role-based access control system for your Go applications. It integrates seamlessly with gRPC services using protocol buffer annotations.

Quick Start

// Define roles and actions
const (
    RoleAdmin  = authz.Role("admin")
    RoleEditor = authz.Role("editor")
    ActionView = authz.Action("view")
    ActionEdit = authz.Action("edit")
)

// Set up authorization with the builder pattern
authzPlugin := authz.NewBuilder().
    // Define policies (who can do what)
    WithPolicy(authz.Allow, RoleAdmin, ActionView).
    WithPolicy(authz.Allow, RoleAdmin, ActionEdit).
    WithPolicy(authz.Allow, RoleEditor, ActionView).
    
    // Set up role hierarchy (admin includes editor permissions)
    WithRoleHierarchy(RoleAdmin, RoleEditor).
    
    // Register functions to fetch objects by ID
    WithObjectFetcher("document", fetchDocument).
    
    // Register functions to determine user roles
    WithRoleDescriber("document", documentRoleDescriber).
    Build()

// Add to server
server := prefab.New(
    prefab.WithPlugin(authzPlugin),
    // Other plugins and options
)

Using Common Patterns

For even faster setup, use the common patterns:

// Use predefined roles and actions with CRUD permissions
authzPlugin := authz.NewCRUDBuilder().
    WithObjectFetcher("document", fetchDocument).
    WithRoleDescriber("document", documentRoleDescriber).
    Build()

Protocol Buffer Annotations

To protect your endpoints, annotate your .proto files:

rpc GetDocument(GetDocumentRequest) returns (GetDocumentResponse) {
  option (prefab.authz.action) = "documents.view";
  option (prefab.authz.resource) = "document";
}

message GetDocumentRequest {
  string org_id = 1 [(prefab.authz.domain) = true];
  string document_id = 2 [(prefab.authz.id) = true];
}

Role Describers and Object Fetchers

These components connect your business logic to the authorization system:

// Fetch a document by ID
func fetchDocument(ctx context.Context, id any) (any, error) {
    return documentRepository.GetByID(id.(string))
}

// Determine roles for a user relative to a document
func documentRoleDescriber(ctx context.Context, identity auth.Identity, 
                           object any, domain authz.Domain) ([]authz.Role, error) {
    doc := object.(Document)
    roles := []authz.Role{}
    
    // All authenticated users get basic role
    roles = append(roles, authz.RoleUser)
    
    // Document owner gets owner role
    if doc.OwnerID == identity.Subject {
        roles = append(roles, authz.RoleOwner)
    }
    
    // Check for admin role
    if isAdmin(identity) {
        roles = append(roles, authz.RoleAdmin)
    }
    
    return roles, nil
}

Examples

For complete examples, see:

  • examples/authz/custom/authzexample.go (fully custom configuration)
  • examples/authz/common-builder/authzexample.go (common builder pattern)

Documentation

Overview

Package authz provides a plugin for implementing role-based access control (RBAC). It uses protocol buffer annotations to define authorization rules that are enforced by a GRPC interceptor.

Getting Started.

The simplest way to get started with authorization is to use the builder pattern and common configuration helpers:

// Create a basic CRUD authorization plugin with common roles and permissions,
authzPlugin := authz.NewCRUDBuilder().
	WithObjectFetcher("document", fetchDocument).
	WithRoleDescriber("document", documentRoleDescriber).
	Build()

// Add to your Prefab server.
server := prefab.New(
	prefab.WithPlugin(authzPlugin),
	// Other plugins and options.
)

Core Concepts

Authz Policies are defined in terms of roles and actions, both of which are application defined strings. For example, an "editor" role might be allowed to perform the "document.edit" action.

Roles are context dependent and determined by application provided functions called "Role Describers". Role Describers return a list of roles for a given authenticated identity and object. For example, a user may have the role "owner" for a specific document and "admin" for their workspace.

Role Describers can chose to restrict whether a role is granted, based on other attributes. For example, an "admin" role could only be granted if the request comes from a specific IP address.

Role Describers can also be configured to accept a `domain` from the request. This is optional and is intended to simplify the implementation of multi-tenant systems or systems where a user might be part of multiple workspaces or groups, each with different permissions.

To map an incoming request to a resource, the Authz plugin uses "Object Fetchers". Fetchers can be registered against a key, which can be an arbitrary string, or derived from `reflect.Type`. The fetcher is then called with the value of a request parameter, per the field option.

RPCs can be configured with a default effect of Allow. For example, a page might be configured to allow all users to view it, except those on mobile devices.

Protocol Buffer Annotations

To use authorization, you need to annotate your protocol buffer definitions:

rpc GetDocument(GetDocumentRequest) returns (GetDocumentResponse) {
  option (prefab.authz.action) = "documents.view";
  option (prefab.authz.resource) = "document";
  option (prefab.authz.default_effect) = "deny"; // Optional, defaults to "deny"
}

message GetDocumentRequest {
  string org_id = 1 [(prefab.authz.domain) = true]; // Optional scope/domain.
  string document_id = 2 [(prefab.authz.id) = true]; // Required to identify resource.
}

Common Patterns

This package provides several common patterns to simplify authorization setup:

- Builder pattern: Use `NewBuilder()` for a fluent configuration interface. - Predefined roles: `RoleAdmin`, `RoleEditor`, `RoleViewer`, etc.. - Common CRUD actions: `ActionCreate`, `ActionRead`, etc.. - CRUD builder: `NewCRUDBuilder()` for standard roles and CRUD permissions. - Type-safe interfaces: Use the typed helpers for compile-time type safety.

Role Hierarchy

You can establish a role hierarchy where parent roles inherit child roles:

authz.WithRoleHierarchy(RoleAdmin, RoleEditor, RoleViewer, RoleUser)

In this example, admins inherit all editor permissions, editors inherit viewer permissions, and viewers inherit user permissions.

Examples

For complete examples, see: - examples/authz/custom/authzexample.go (fully custom configuration) - examples/authz/common-builder/authzexample.go (common builder pattern)

Index

Constants

View Source
const (
	RoleAdmin  = Role("admin")
	RoleEditor = Role("editor")
	RoleViewer = Role("viewer")
	RoleOwner  = Role("owner")
	RoleUser   = Role("user")
)

Common predefined roles.

View Source
const (
	ActionCreate = Action("create")
	ActionRead   = Action("read")
	ActionUpdate = Action("update")
	ActionDelete = Action("delete")
	ActionList   = Action("list")
)

Common predefined actions.

View Source
const PluginName = "authz"

Constant name for identifying the core Authz plugin.

Variables

View Source
var (
	// optional string action = 50011;
	E_Action = &file_plugins_authz_authz_proto_extTypes[0]
	// optional string resource = 50012;
	E_Resource = &file_plugins_authz_authz_proto_extTypes[1]
	// optional string default_effect = 50013;
	E_DefaultEffect = &file_plugins_authz_authz_proto_extTypes[2]
)

Extension fields to descriptorpb.MethodOptions.

View Source
var (
	// optional bool id = 50021;
	E_Id = &file_plugins_authz_authz_proto_extTypes[3]
	// optional bool domain = 50022;
	E_Domain = &file_plugins_authz_authz_proto_extTypes[4] // Deprecated: use scope instead
	// optional bool scope = 50023;
	E_Scope = &file_plugins_authz_authz_proto_extTypes[5]
)

Extension fields to descriptorpb.FieldOptions.

View Source
var (
	ErrPermissionDenied = errors.Codef(codes.PermissionDenied, "you are not authorized to perform this action")
	ErrUnauthenticated  = errors.Codef(codes.Unauthenticated, "the requested action requires authentication")
)
View Source
var File_plugins_authz_authz_proto protoreflect.FileDescriptor

Functions

func FieldOptions

func FieldOptions(req proto.Message) (any, string, error)

FieldOptions returns proto fields that are tagged with Authz related options. It returns the object ID and scope string.

func MethodOptions

func MethodOptions(info *grpc.UnaryServerInfo) (objectKey string, action Action, defaultEffect Effect)

MethodOptions returns Authz related method options from the method descriptor. associated with the given info..

Types

type Action

type Action string

type AuthorizeParams

type AuthorizeParams struct {
	ObjectKey     string
	ObjectID      any
	Scope         Scope
	Action        Action
	DefaultEffect Effect
	Info          string
}

Parameters for the Authorize method.

type AuthzObject added in v0.2.0

type AuthzObject interface {
	// AuthzType returns a string identifier for the object type
	AuthzType() string
}

AuthzObject is the base interface for all objects used in authorization. While not strictly necessary, it is recommended to implement this interface for type safety.

type AuthzOption

type AuthzOption func(*AuthzPlugin)

Configuration options for the Authz Plugin.

func WithObjectFetcher

func WithObjectFetcher(objectKey string, fetcher ObjectFetcher) AuthzOption

WithObjectFetcher adds an object fetcher to the plugin.

func WithObjectFetcherFn added in v0.2.0

func WithObjectFetcherFn(objectKey string, fetcher func(ctx context.Context, key any) (any, error)) AuthzOption

WithObjectFetcherFn adds a function-based object fetcher to the plugin.

func WithPolicy

func WithPolicy(effect Effect, role Role, action Action) AuthzOption

WithPolicy adds an Authz policy to the plugin.

func WithRoleDescriber

func WithRoleDescriber(objectKey string, describer RoleDescriber) AuthzOption

WithRoleDescriber adds a role describer to the plugin.

func WithRoleDescriberFn added in v0.2.0

func WithRoleDescriberFn(objectKey string, describer func(ctx context.Context, subject auth.Identity, object any, scope Scope) ([]Role, error)) AuthzOption

WithRoleDescriberFn adds a function-based role describer to the plugin.

func WithRoleHierarchy

func WithRoleHierarchy(roles ...Role) AuthzOption

WithRoleHierarchy configures the plugin with a hierarchy of roles.

The first role is the most powerful, and the last role has no hierarchy from a single call. Multiple calls can be used to define a tree hierarchies.

Example:

WithRoleHierarchy("owner", "admin", "editor", "viewer", "member")
WithRoleHierarchy("suggester", "viewer")

In this example, the "owner" role is an "admin", "editor", "viewer", and "member". An "admin" is an "editor", "viewer", and "member". An "editor" is also a "viewer" and a "member".

A "suggester" is a "viewer" and a "member", since the ancestry of "viewer" was defined by the previous call.

func WithTypeRegistration added in v0.2.0

func WithTypeRegistration[K comparable, T any](objectKey string, fetcher TypedObjectFetcher[K, T], describer TypedRoleDescriber[T]) AuthzOption

WithTypeRegistration registers a role describer and object fetcher for a specific type.. Since Go doesn't support generic methods, this is provided as a package function..

func WithTypedObjectFetcher added in v0.2.0

func WithTypedObjectFetcher[K comparable, T any](objectKey string, fetcher TypedObjectFetcher[K, T]) AuthzOption

WithTypedObjectFetcher adds a type-safe object fetcher to the builder.. Since Go doesn't support generic methods, this is provided as a package function..

func WithTypedRoleDescriber added in v0.2.0

func WithTypedRoleDescriber[T any](objectKey string, describer TypedRoleDescriber[T]) AuthzOption

WithTypedRoleDescriber adds a type-safe role describer to the builder.. Since Go doesn't support generic methods, this is provided as a package function..

type AuthzPlugin

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

AuthzPlugin provides functionality for authorizing requests and access to resources.

func Plugin

func Plugin(opts ...AuthzOption) *AuthzPlugin

Plugin returns a new AuthzPlugin.

func (*AuthzPlugin) Authorize

func (ap *AuthzPlugin) Authorize(ctx context.Context, cfg AuthorizeParams) error

Authorize takes the configuration and verifies that the caller is authorized to perform the action on the object.

func (*AuthzPlugin) DebugHandler

func (ap *AuthzPlugin) DebugHandler(resp http.ResponseWriter, req *http.Request)

DebugHandler renders information about registered policies and roles.

func (*AuthzPlugin) DefinePolicy

func (ap *AuthzPlugin) DefinePolicy(effect Effect, role Role, action Action)

DefinePolicy defines an policy which allows/denies the given role to perform the action.

func (*AuthzPlugin) Deps

func (ap *AuthzPlugin) Deps() []string

From plugin.DependentPlugin.

func (*AuthzPlugin) DetermineEffect

func (ap *AuthzPlugin) DetermineEffect(action Action, roles []Role, defaultEffect Effect) Effect

DetermineEffect checks to see if there are any policies which explicitly apply to this role and action. If there are, then all roles must explicitly revert the default effect.

In otherwords, if an RPC is default deny and two roles explicitly match a policy, then both roles must allow access. This can be used to create exclusion groups: e.g. all admins, except nyc-admins.

func (*AuthzPlugin) Interceptor

func (ap *AuthzPlugin) Interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error)

Interceptor that enforces authorization policies configured on the GRPC service descriptors.

This interceptor: 1. Uses method options to get object key and action. 2. Uses proto field options to get an object id and optionally scope. 3. Fetches the object based on the object key and id (ObjectFetcher). 4. Gets the user's role relative to the object (RoleDescriber). 5. Checks if the role can perform the action on the object.

func (*AuthzPlugin) Name

func (ap *AuthzPlugin) Name() string

From plugin.Plugin.

func (*AuthzPlugin) RegisterObjectFetcher

func (ap *AuthzPlugin) RegisterObjectFetcher(objectKey string, fetcher ObjectFetcher)

RegisterObjectFetcher registers an object fetcher for a specified object key. '*' can be used as a wildcard to match any key which doesn't have a more specific fetcher.

func (*AuthzPlugin) RegisterRoleDescriber

func (ap *AuthzPlugin) RegisterRoleDescriber(objectKey string, describer RoleDescriber)

RegisterRoleDescriber registers a role describer for a specified object key. '*' can be used as a wildcard to match any key which doesn't have a more specific describer.

func (*AuthzPlugin) RoleHierarchy

func (ap *AuthzPlugin) RoleHierarchy(role Role) []Role

RoleHierarchy returns the ancestry of a single role.

func (*AuthzPlugin) RoleTree

func (ap *AuthzPlugin) RoleTree() map[Role][]Role

RoleTree returns the hierarchy of roles in tree form.

func (*AuthzPlugin) ServerOptions

func (ap *AuthzPlugin) ServerOptions() []prefab.ServerOption

From prefab.OptionProvider, registers an additional interceptor.

func (*AuthzPlugin) SetRoleHierarchy

func (ap *AuthzPlugin) SetRoleHierarchy(roles ...Role)

SetRoleHierarchy sets the hierarchy of roles.

type Builder added in v0.2.0

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

Builder provides a fluent interface for configuring the authz plugin.

func NewBuilder added in v0.2.0

func NewBuilder() *Builder

NewBuilder creates a new builder for the authz plugin.

func NewCommonBuilder added in v0.2.0

func NewCommonBuilder() *Builder

NewCommonBuilder creates a new builder with common configuration including predefined roles and a standard role hierarchy with CRUD permissions.

Role hierarchy: Admin > Editor > Viewer > User

Default permissions: - Admin can do everything - Editor can create, read, update, list - Viewer can read and list - User has minimal permissions

The builder can be further customized by adding additional policies, object fetchers, and role describers.

func (*Builder) Build added in v0.2.0

func (b *Builder) Build() *AuthzPlugin

Build finalizes the builder and returns the configured plugin.

func (*Builder) WithObjectFetcher added in v0.2.0

func (b *Builder) WithObjectFetcher(objectKey string, fetcher ObjectFetcher) *Builder

WithObjectFetcher adds an object fetcher to the builder.

func (*Builder) WithObjectFetcherFn added in v0.2.0

func (b *Builder) WithObjectFetcherFn(objectKey string, fetcher func(ctx context.Context, key any) (any, error)) *Builder

WithObjectFetcherFn adds a function-based object fetcher to the builder.

func (*Builder) WithPolicy added in v0.2.0

func (b *Builder) WithPolicy(effect Effect, role Role, action Action) *Builder

WithPolicy adds a policy to the builder.

func (*Builder) WithRoleDescriber added in v0.2.0

func (b *Builder) WithRoleDescriber(objectKey string, describer RoleDescriber) *Builder

WithRoleDescriber adds a role describer to the builder.

func (*Builder) WithRoleDescriberFn added in v0.2.0

func (b *Builder) WithRoleDescriberFn(objectKey string, describer func(ctx context.Context, subject auth.Identity, object any, scope Scope) ([]Role, error)) *Builder

WithRoleDescriberFn adds a function-based role describer to the builder.

func (*Builder) WithRoleHierarchy added in v0.2.0

func (b *Builder) WithRoleHierarchy(roles ...Role) *Builder

WithRoleHierarchy adds a role hierarchy to the builder.

func (*Builder) WithTypedObjectFetcher added in v0.2.0

func (b *Builder) WithTypedObjectFetcher(objectKey string, fetcher interface{}) *Builder

WithTypedObjectFetcher adds a type-safe object fetcher using a helper function.

func (*Builder) WithTypedRoleDescriber added in v0.2.0

func (b *Builder) WithTypedRoleDescriber(objectKey string, describer interface{}) *Builder

WithTypedRoleDescriber adds a type-safe role describer using a helper function.

type Effect

type Effect int
const (
	Deny Effect = iota
	Allow
)

func (Effect) Reverse

func (e Effect) Reverse() Effect

func (Effect) String

func (e Effect) String() string

type ObjectFetcher

type ObjectFetcher interface {
	// FetchObject retrieves an object based on the provided key
	FetchObject(ctx context.Context, key any) (any, error)
}

ObjectFetcher is an interface for fetching objects based on a request parameter.

func AsObjectFetcher added in v0.2.0

func AsObjectFetcher[K comparable, T any](fetcher TypedObjectFetcher[K, T]) ObjectFetcher

AsObjectFetcher converts a TypedObjectFetcher to the ObjectFetcher interface.

type ObjectFetcherFn added in v0.2.0

type ObjectFetcherFn func(ctx context.Context, key any) (any, error)

ObjectFetcherFn adapts a function to the ObjectFetcher interface.

func (ObjectFetcherFn) FetchObject added in v0.2.0

func (f ObjectFetcherFn) FetchObject(ctx context.Context, key any) (any, error)

FetchObject implements the ObjectFetcher interface.

type OwnedObject added in v0.2.0

type OwnedObject interface {
	AuthzObject
	// OwnerID returns the ID of the object's owner
	OwnerID() string
}

OwnedObject represents objects that have an owner.

type Role

type Role string

type RoleDescriber

type RoleDescriber interface {
	// DescribeRoles determines the roles a subject has relative to an object in a scope
	DescribeRoles(ctx context.Context, subject auth.Identity, object any, scope Scope) ([]Role, error)
}

RoleDescriber is an interface for describing roles relative to a type.

func AsRoleDescriber added in v0.2.0

func AsRoleDescriber[T any](describer TypedRoleDescriber[T]) RoleDescriber

AsRoleDescriber converts a TypedRoleDescriber to the RoleDescriber interface.

type RoleDescriberFn added in v0.2.0

type RoleDescriberFn func(ctx context.Context, subject auth.Identity, object any, scope Scope) ([]Role, error)

RoleDescriberFn adapts a function to the RoleDescriber interface..

func (RoleDescriberFn) DescribeRoles added in v0.2.0

func (f RoleDescriberFn) DescribeRoles(ctx context.Context, subject auth.Identity, object any, scope Scope) ([]Role, error)

DescribeRoles implements the RoleDescriber interface.

type Scope added in v0.2.0

type Scope string

Scope defines the context in which authorization occurs (e.g., organization, workspace).

type ScopedObject added in v0.2.0

type ScopedObject interface {
	AuthzObject
	// ScopeID returns the scope ID
	ScopeID() string
}

ScopedObject represents objects that belong to a specific scope.

type TypedBuilder added in v0.2.0

type TypedBuilder[T any] struct {
	// contains filtered or unexported fields
}

TypedBuilder is a specialized builder wrapper for working with a specific type. This allows for compile-time type checking.

func NewTypedBuilder added in v0.2.0

func NewTypedBuilder[T any]() *TypedBuilder[T]

NewTypedBuilder creates a new typed builder for a specific type.

func (*TypedBuilder[T]) Build added in v0.2.0

func (tb *TypedBuilder[T]) Build() *AuthzPlugin

Build finalizes the typed builder and returns the plugin.

func (*TypedBuilder[T]) WithIntObjectFetcher added in v0.2.0

func (tb *TypedBuilder[T]) WithIntObjectFetcher(objectKey string, fetcher func(ctx context.Context, key int) (T, error)) *TypedBuilder[T]

WithIntObjectFetcher adds a type-safe object fetcher with int keys.

func (*TypedBuilder[T]) WithPolicy added in v0.2.0

func (tb *TypedBuilder[T]) WithPolicy(effect Effect, role Role, action Action) *TypedBuilder[T]

WithPolicy adds a policy to the builder.

func (*TypedBuilder[T]) WithRoleDescriber added in v0.2.0

func (tb *TypedBuilder[T]) WithRoleDescriber(objectKey string, describer func(ctx context.Context, subject auth.Identity, object T, scope Scope) ([]Role, error)) *TypedBuilder[T]

WithRoleDescriber adds a type-safe role describer.

func (*TypedBuilder[T]) WithRoleHierarchy added in v0.2.0

func (tb *TypedBuilder[T]) WithRoleHierarchy(roles ...Role) *TypedBuilder[T]

WithRoleHierarchy adds a role hierarchy to the builder.

func (*TypedBuilder[T]) WithStringObjectFetcher added in v0.2.0

func (tb *TypedBuilder[T]) WithStringObjectFetcher(objectKey string, fetcher func(ctx context.Context, key string) (T, error)) *TypedBuilder[T]

WithStringObjectFetcher adds a type-safe object fetcher with string keys.

type TypedObjectFetcher added in v0.2.0

type TypedObjectFetcher[K comparable, T any] func(ctx context.Context, key K) (T, error)

TypedObjectFetcher is a function type for fetching objects with type safety.

type TypedRoleDescriber added in v0.2.0

type TypedRoleDescriber[T any] func(ctx context.Context, subject auth.Identity, object T, scope Scope) ([]Role, error)

TypedRoleDescriber is a function type for describing roles with type safety.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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