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
- Variables
- func FieldOptions(req proto.Message) (any, string, error)
- func MethodOptions(info *grpc.UnaryServerInfo) (objectKey string, action Action, defaultEffect Effect)
- type Action
- type AuthorizeParams
- type AuthzObject
- type AuthzOption
- func WithObjectFetcher(objectKey string, fetcher ObjectFetcher) AuthzOption
- func WithObjectFetcherFn(objectKey string, fetcher func(ctx context.Context, key any) (any, error)) AuthzOption
- func WithPolicy(effect Effect, role Role, action Action) AuthzOption
- func WithRoleDescriber(objectKey string, describer RoleDescriber) AuthzOption
- func WithRoleDescriberFn(objectKey string, ...) AuthzOption
- func WithRoleHierarchy(roles ...Role) AuthzOption
- func WithTypeRegistration[K comparable, T any](objectKey string, fetcher TypedObjectFetcher[K, T], ...) AuthzOption
- func WithTypedObjectFetcher[K comparable, T any](objectKey string, fetcher TypedObjectFetcher[K, T]) AuthzOption
- func WithTypedRoleDescriber[T any](objectKey string, describer TypedRoleDescriber[T]) AuthzOption
- type AuthzPlugin
- func (ap *AuthzPlugin) Authorize(ctx context.Context, cfg AuthorizeParams) error
- func (ap *AuthzPlugin) DebugHandler(resp http.ResponseWriter, req *http.Request)
- func (ap *AuthzPlugin) DefinePolicy(effect Effect, role Role, action Action)
- func (ap *AuthzPlugin) Deps() []string
- func (ap *AuthzPlugin) DetermineEffect(action Action, roles []Role, defaultEffect Effect) Effect
- func (ap *AuthzPlugin) Interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, ...) (resp interface{}, err error)
- func (ap *AuthzPlugin) Name() string
- func (ap *AuthzPlugin) RegisterObjectFetcher(objectKey string, fetcher ObjectFetcher)
- func (ap *AuthzPlugin) RegisterRoleDescriber(objectKey string, describer RoleDescriber)
- func (ap *AuthzPlugin) RoleHierarchy(role Role) []Role
- func (ap *AuthzPlugin) RoleTree() map[Role][]Role
- func (ap *AuthzPlugin) ServerOptions() []prefab.ServerOption
- func (ap *AuthzPlugin) SetRoleHierarchy(roles ...Role)
- type Builder
- func (b *Builder) Build() *AuthzPlugin
- func (b *Builder) WithObjectFetcher(objectKey string, fetcher ObjectFetcher) *Builder
- func (b *Builder) WithObjectFetcherFn(objectKey string, fetcher func(ctx context.Context, key any) (any, error)) *Builder
- func (b *Builder) WithPolicy(effect Effect, role Role, action Action) *Builder
- func (b *Builder) WithRoleDescriber(objectKey string, describer RoleDescriber) *Builder
- func (b *Builder) WithRoleDescriberFn(objectKey string, ...) *Builder
- func (b *Builder) WithRoleHierarchy(roles ...Role) *Builder
- func (b *Builder) WithTypedObjectFetcher(objectKey string, fetcher interface{}) *Builder
- func (b *Builder) WithTypedRoleDescriber(objectKey string, describer interface{}) *Builder
- type Effect
- type ObjectFetcher
- type ObjectFetcherFn
- type OwnedObject
- type Role
- type RoleDescriber
- type RoleDescriberFn
- type Scope
- type ScopedObject
- type TypedBuilder
- func (tb *TypedBuilder[T]) Build() *AuthzPlugin
- func (tb *TypedBuilder[T]) WithIntObjectFetcher(objectKey string, fetcher func(ctx context.Context, key int) (T, error)) *TypedBuilder[T]
- func (tb *TypedBuilder[T]) WithPolicy(effect Effect, role Role, action Action) *TypedBuilder[T]
- func (tb *TypedBuilder[T]) WithRoleDescriber(objectKey string, ...) *TypedBuilder[T]
- func (tb *TypedBuilder[T]) WithRoleHierarchy(roles ...Role) *TypedBuilder[T]
- func (tb *TypedBuilder[T]) WithStringObjectFetcher(objectKey string, fetcher func(ctx context.Context, key string) (T, error)) *TypedBuilder[T]
- type TypedObjectFetcher
- type TypedRoleDescriber
Constants ¶
const ( RoleAdmin = Role("admin") RoleEditor = Role("editor") RoleViewer = Role("viewer") RoleOwner = Role("owner") RoleUser = Role("user") )
Common predefined roles.
const ( ActionCreate = Action("create") ActionRead = Action("read") ActionUpdate = Action("update") ActionDelete = Action("delete") ActionList = Action("list") )
Common predefined actions.
const PluginName = "authz"
Constant name for identifying the core Authz plugin.
Variables ¶
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.
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.
var ( ErrPermissionDenied = errors.Codef(codes.PermissionDenied, "you are not authorized to perform this action") ErrUnauthenticated = errors.Codef(codes.Unauthenticated, "the requested action requires authentication") )
var File_plugins_authz_authz_proto protoreflect.FileDescriptor
Functions ¶
func FieldOptions ¶
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 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 (*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) 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) 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
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
WithRoleHierarchy adds a role hierarchy to the builder.
func (*Builder) WithTypedObjectFetcher ¶ added in v0.2.0
WithTypedObjectFetcher adds a type-safe object fetcher using a helper function.
func (*Builder) WithTypedRoleDescriber ¶ added in v0.2.0
WithTypedRoleDescriber adds a type-safe role describer using a helper function.
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
ObjectFetcherFn adapts a function to the ObjectFetcher interface.
func (ObjectFetcherFn) FetchObject ¶ added in v0.2.0
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 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..
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.