redtape

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: May 3, 2020 License: MIT Imports: 14 Imported by: 0

README

Redtape Go Report Card Go go.dev reference license

A flexible policy engine for Go

Redtape is based on the excellent ory/ladon package which is no longer maintained.

Instalallation

go get github.com/blushft/redtape

Usage

Roles

Roles are the basic permission unit for Redtape. A role is identified by a simple string like view_comments and can contain other roles.

myrole := redtape.NewRole("edit_comments")

myrole.AddRole(redtape.NewRole("view_comments"))

A role can also hold a name and description for display purposes.

myrole.Name = "Comment Editor"
myrole.Description = "This role is able to edit comments."

The role manager interface allows a persistence and storage mechanism for interacting with roles.

manager := redtape.NewRoleManager()
manager.Create(myrole)

role, err := manager.Get("edit_comments")
Requests

A request specifies a set of values that can be processed to determine what permission to apply.

req := redtape.NewRequest("/comments", "GET", "edit_comments", "post")

fmt.Printf(
    "Request:\nResource: %s, Action: %s, Role: %s, Scope: %s",
    req.Resource, // The resource describes what is being accessed
    req.Action,  // The action describes what is being done to the resource
    req.Role, // The role indicates what role the caller holds
    req.Scope, // The scope describes a context for the resource or action
)

Requests also contains a context that can carry metadata into policy objects like conditions.

req := NewRequest("/comments", "GET", "edit_comments", "post",
 map[string]interface{}{
    "headers": httpRequest.Header,
})

If you'd like to append an existing context with this metadata, use the NewRequestWithContext method.

Policies

Policies describe what permissions to apply to requests. A policy can contain a range of Resources, Actions, Roles, and Scopes used to match that policy to the request. Further, you can use conditions to express logic needed to determine a PolicyEffect.

Policies are built with the PolicyOptions struct using a functional options pattern.

policy, err := redtape.NewPolicy(
    redtape.PolicyName("allow_edit_comments"),
    redtape.PolicyDescription("Allows members of the edit_comments role to edit comments"),
    redtape.SetResources("/comments"),
    redtape.SetActions("GET", "POST", "PUT"),
    redtape.WithRole("edit_comments"),
    redtape.PolicyAllow(),
)

To enable efficient storage, you can also unmarshal policy options from json.

j := `
{
    "name": "allow_edit_comments",
    "description": "Allows members of the edit_comments role to edit comments.",
    "roles": [
        "edit_comments"
    ],
    "resources": [
        "/comments"
    ],
    "actions": [
        "GET",
        "POST",
        "PUT"
    ],
    "effect": "allow"
}
`
var opts redtape.PolicyOptions
err := json.Unmarshal([]byte(j), &opts)

policy := redtape.NewPolicy(redtape.SetPolicyOptions(opts))
Conditions

Conditions can be applied to policies to add additional logic to the application of permissions.

TODO: Document usage API for conditions.

PolicyManager

The policy manager interface provides basic methods to allow you to load policies from memory, a storage backend, or files. The default manager is memory backed without persistence.

manager := redtape.NewManager()

err := manager.Create(myPolicy)
Enforcer

An enforcer brings together a PolicyManager and Matcher to enforce permssions on requests.

enforcer, err := redtape.NewDefaultEnforcer(manager)

err := enforcer.Enforce(myRequest)

The default enforcer uses the default matcher which allows resources, actions, and scopes to be matched with wildcards.

Policies are evaluated in order to ensure matches against actions, then resources, then roles, then scopes, and finally conditions. If any matched policy evaluates to PolicyEffect deny, the request is actively denied. If no policy matches and the package level DefaultPolicyEffect is deny (the default), the request is implicitly denied.

Permission is determined by the error value returned by Enforce(). A nil error is considered permission allowed.

if err := enforcer.Enforce(req); err != nil {
    log.Println("Request Denied")
    return
}

// Do the request here
Todo
  • RoleManager interface
  • SQL backend for managers
  • KV Store backend for managers
  • URL backend for managers
  • Improve Condition API
  • Expand Scope utilities
  • Improve context.Context interopertation
  • Create middlewares for popular frameworks
  • Increased test coverage
  • Examples

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// DefaultMatcher is a simple matcher
	DefaultMatcher = NewMatcher()
	// DefaultPolicyEffect is the policy effect to apply when no other matches can be found
	DefaultPolicyEffect = PolicyEffectDeny
)

Functions

func MatchPolicy

func MatchPolicy(p Policy, def []string, val string) (bool, error)

MatchPolicy is a utility function that uses DefaultMatcher to evaluate whether p can be matched by val

func MatchRole

func MatchRole(r *Role, val string) (bool, error)

MatchRole is a utility function that uses the DefaultMatcher to evaluate whether role val matches the effective roles of r

func NewErrRequestDeniedExplicit

func NewErrRequestDeniedExplicit(err error) error

NewErrRequestDeniedExplicit returns an error with for explicit denials

func NewErrRequestDeniedImplicit

func NewErrRequestDeniedImplicit(err error) error

NewErrRequestDeniedImplicit returns an error with for implicit denials (no policy)

func NewRequestContext

func NewRequestContext(ctx context.Context, meta ...map[string]interface{}) context.Context

NewRequestContext builds a context object from an existing context, embedding request metadata. If nil values are provided to both arguments, new values are created or returned

Types

type Auditor

type Auditor interface{}

Auditor is not yet implemented

type BoolCondition

type BoolCondition struct {
	Value bool `json:"value"`
}

BoolCondition matches a boolean value from context to the preconfigured value

func (*BoolCondition) Meets

func (c *BoolCondition) Meets(val interface{}, _ *Request) bool

Meets evaluates whether parameter val matches the Condition Value

func (*BoolCondition) Name

func (c *BoolCondition) Name() string

Name fulfills the Name method of Condition

type Condition

type Condition interface {
	Name() string
	Meets(interface{}, *Request) bool
}

Condition is the interface allowing different types of conditional expressions

type ConditionBuilder

type ConditionBuilder func() Condition

ConditionBuilder is a typed function that returns a Condition

type ConditionOptions

type ConditionOptions struct {
	Name    string                 `json:"name"`
	Type    string                 `json:"type"`
	Options map[string]interface{} `json:"options"`
}

ConditionOptions contains the values used to build a Condition

type ConditionRegistry

type ConditionRegistry map[string]ConditionBuilder

ConditionRegistry is a map contiaining named ConditionBuilders

func NewConditionRegistry

func NewConditionRegistry(conds ...map[string]ConditionBuilder) ConditionRegistry

NewConditionRegistry returns a ConditionRegistry containing the default Conditions and accepts an array of map[string]ConditionBuilder to add custom conditions to the set

type Conditions

type Conditions map[string]Condition

Conditions is a map of named Conditions

func NewConditions

func NewConditions(opts []ConditionOptions, reg ConditionRegistry) (Conditions, error)

NewConditions accepts an array of options and an optional ConditionRegistry and returns a Conditions map

type Enforcer

type Enforcer interface {
	Enforce(*Request) error
}

Enforcer interface provides methods to enforce policies against a request

func NewDefaultEnforcer added in v0.1.2

func NewDefaultEnforcer(manager PolicyManager) (Enforcer, error)

func NewEnforcer

func NewEnforcer(manager PolicyManager, matcher Matcher, auditor Auditor) (Enforcer, error)

NewEnforcer returns a default Enforcer combining a PolicyManager, Matcher, and Auditor

type Error

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

Error is a customized error implementation with additional context for policy evaluation

func (*Error) Reason

func (e *Error) Reason() string

Reason contains information about the policy decision that resulted in the error

func (*Error) RequestID

func (e *Error) RequestID() string

RequestID allows errors to be tracked against custom request ids

func (*Error) Status

func (e *Error) Status() string

Status is a text explanation of the error code

func (*Error) StatusCode

func (e *Error) StatusCode() int

StatusCode can contain application or standard integer codes eg http 401

type IPWhitelistCondition

type IPWhitelistCondition struct {
	Networks []string `json:"networks" structs:"networks"`
}

IPWhitelistCondition performs CIDR matching for a range of Networks against a provided value

func (*IPWhitelistCondition) Meets

func (c *IPWhitelistCondition) Meets(val interface{}, _ *Request) bool

Meets evaluates true when the network address in val is contained within one of the CIDR ranges of IPWhitelistCondition#Networks

func (*IPWhitelistCondition) Name

func (c *IPWhitelistCondition) Name() string

Name fulfills the Name method of Condition

type Matcher

type Matcher interface {
	MatchPolicy(p Policy, def []string, val string) (bool, error)
	MatchRole(r *Role, val string) (bool, error)
}

Matcher provides methods to facilitate matching policies to different request elements

func NewMatcher

func NewMatcher() Matcher

NewMatcher returns the default Matcher implementation

func NewRegexMatcher

func NewRegexMatcher() Matcher

NewRegexMatcher returns a Matcher using delimited regex for matching

type Policy

type Policy interface {
	ID() string
	Description() string
	Roles() []*Role
	Resources() []string
	Actions() []string
	Scopes() []string
	Conditions() Conditions
	Effect() PolicyEffect
	Context() context.Context
}

Policy provides methods to return data about a configured policy

func MustNewPolicy

func MustNewPolicy(opts ...PolicyOption) Policy

MustNewPolicy returns a default policy implementation or panics on error

func NewPolicy

func NewPolicy(opts ...PolicyOption) (Policy, error)

NewPolicy returns a default policy implementation from a set of provided options

type PolicyEffect

type PolicyEffect string

PolicyEffect type is returned by Enforcer to describe the outcome of a policy evaluation

const (
	// PolicyEffectAllow indicates explicit permission of the request
	PolicyEffectAllow PolicyEffect = "allow"
	// PolicyEffectDeny indicates explicti denial of the request
	PolicyEffectDeny PolicyEffect = "deny"
)

func NewPolicyEffect

func NewPolicyEffect(s string) PolicyEffect

NewPolicyEffect returns a PolicyEffect for a given string

type PolicyManager

type PolicyManager interface {
	Create(Policy) error
	Update(Policy) error
	Get(string) (Policy, error)
	Delete(string) error
	All(limit, offset int) ([]Policy, error)

	FindByRequest(*Request) ([]Policy, error)
	FindByRole(string) ([]Policy, error)
	FindByResource(string) ([]Policy, error)
	FindByScope(string) ([]Policy, error)
}

PolicyManager contains methods to allow query, update, and removal of policies

func NewManager

func NewManager() PolicyManager

NewManager returns a default memory backed policy manager

type PolicyOption

type PolicyOption func(*PolicyOptions)

PolicyOption is a typed function allowing updates to PolicyOptions through functional options

func PolicyAllow

func PolicyAllow() PolicyOption

PolicyAllow sets the PolicyEffect to allow

func PolicyDeny

func PolicyDeny() PolicyOption

PolicyDeny sets the PolicyEffect to deny

func PolicyDescription

func PolicyDescription(d string) PolicyOption

PolicyDescription sets the policy description Option

func PolicyName

func PolicyName(n string) PolicyOption

PolicyName sets the policy Name Option

func SetActions

func SetActions(s ...string) PolicyOption

SetActions replaces the option Actions with the provided values

func SetContext

func SetContext(ctx context.Context) PolicyOption

SetContext sets the Context option

func SetPolicyOptions

func SetPolicyOptions(opts PolicyOptions) PolicyOption

SetPolicyOptions is a PolicyOption setting all PolicyOptions to the provided values

func SetResources

func SetResources(s ...string) PolicyOption

SetResources replaces the option Resources with the provided values

func WithCondition

func WithCondition(co ConditionOptions) PolicyOption

WithCondition adds a Condition to the Conditions option

func WithRole

func WithRole(r *Role) PolicyOption

WithRole adds a Role to the Roles option

type PolicyOptions

type PolicyOptions struct {
	Name        string             `json:"name"`
	Description string             `json:"description"`
	Roles       []*Role            `json:"roles"`
	Resources   []string           `json:"resources"`
	Actions     []string           `json:"actions"`
	Scopes      []string           `json:"scopes"`
	Conditions  []ConditionOptions `json:"conditions"`
	Effect      string             `json:"effect"`
	Context     context.Context    `json:"-"`
}

PolicyOptions struct allows different Policy implementations to be configured with marshalable data

func NewPolicyOptions

func NewPolicyOptions(opts ...PolicyOption) PolicyOptions

NewPolicyOptions returns PolicyOptions configured with the provided functional options

type Request

type Request struct {
	Resource string          `json:"resource"`
	Action   string          `json:"action"`
	Role     string          `json:"subject"`
	Scope    string          `json:"scope"`
	Context  context.Context `json:"-"`
}

Request represents a request to be matched against a policy set

func NewRequest

func NewRequest(res, action, role, scope string, meta ...map[string]interface{}) *Request

func NewRequestWithContext added in v0.1.2

func NewRequestWithContext(ctx context.Context, res, action, role, scope string, meta ...map[string]interface{}) *Request

NewRequestWithContext builds a request from the provided parameters

func (*Request) Metadata

func (r *Request) Metadata() RequestMetadata

Metadata returns metadata stored in context or an empty set

type RequestMetadata

type RequestMetadata map[string]interface{}

RequestMetadata is a helper type to allow type safe retrieval

func RequestMetadataFromContext

func RequestMetadataFromContext(ctx context.Context) RequestMetadata

RequestMetadataFromContext extracts RequestMetadata from a given context or returns an empty metadata set

type RequestMetadataKey

type RequestMetadataKey struct{}

RequestMetadataKey is a type to identify RequestMetadata embedded in context

type Role

type Role struct {
	ID          string  `json:"id"`
	Name        string  `json:"name"`
	Description string  `json:"description"`
	Roles       []*Role `json:"roles"`
}

Role represents a named association to a set of permissionable capability

func NewRole

func NewRole(id string, roles ...*Role) *Role

NewRole returns a Role configured with the provided options

func (*Role) AddRole

func (r *Role) AddRole(role *Role) error

AddRole adds a subrole

func (*Role) EffectiveRoles

func (r *Role) EffectiveRoles() ([]*Role, error)

EffectiveRoles returns a flattened slice of all roles embedded in the Role

type RoleEqualsCondition

type RoleEqualsCondition struct{}

RoleEqualsCondition matches the Request role against the required role passed to the condition

func (*RoleEqualsCondition) Meets

func (c *RoleEqualsCondition) Meets(val interface{}, r *Request) bool

Meets evaluates true when the role val matches Request#Role

func (*RoleEqualsCondition) Name

func (c *RoleEqualsCondition) Name() string

Name fulfills the Name method of Condition

type RoleManager

type RoleManager interface {
	Create(*Role) error
	Update(*Role) error
	Get(string) (*Role, error)
	GetByName(string) (*Role, error)
	Delete(string) error
	All(limit, offset int) ([]*Role, error)

	GetMatching(string) ([]*Role, error)
}

RoleManager provides methods to store and retrieve role sets

func NewRoleManager

func NewRoleManager() RoleManager

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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