acl

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2021 License: Apache-2.0 Imports: 9 Imported by: 0

README

acl

Generic implementation of an Access Control List system for enforcing authorization across different resources. While originally meant for utilization in the Drago project, this ACL system was built with flexibility in mind, and should be straightforward to configure and integrate into other codebases.

Although different in many aspects, this implementation is largely based on the ACL systems built by Hashicorp for Nomad and Consul. It also took some inspiration from GCP's ACL system.

Concepts

  • Resource: any type of resource that might require authorization. Example: book.

  • Instance: an uniquely identifiable instance of a resource. Example: "The lord of the rings"

  • Capability: an operation that can be performed in the context of a resource or instance. In other words, a capability is an allowed operation. From an acessor perspective, it makes sense to talk in terms of capabilities, whereas from a resource perspective, its more usual to use the term operation.

  • Policy: a named set of rules for enabling/disabling capabilities on one or more instances.

  • Rule: a combination of a filter for targetting specific resources/instances, as well as a set of capabilities that should be enabled on them.

  • Alias: a convenience shorthand for representing multiple capabilities with a single string.

  • Secret: an object in possession of the acessor which can be associated to a set of policies. A secret can be anything, from an opaque tokens to a JWT, as long as it is possible to resolve it.

  • ACL: Object containing a compiled version of the access rules associated with a secret, and that can be used to find out whether a specific operation can be performed on a resource/instance.

Usage

First one needs to define the resources requiring protection, as well as the available operations/capabilities. Let's assume we're building a backend for an online library, and would like to limit access to our books API.

We start by defining the capabilities:

const (
	capBookRead   = "read"
	capBookWrite  = "write"
	capBookList   = "list"
)

Then we create a acl.Config object:

var c = &Config{
	Resources: map[string]*Resource{
        "book": &Resource{
			Name: "book",
			Capabilities: map[string]*Capability{
				capBookRead:  {capBookRead, "Allows a book to be read"},
				capBookWrite: {capBookWrite, "Allows a book to be written"},
				capBookList:  {capBookList, "Allows books to be listed"},
			},
			Aliases: map[string]*CapabilityAlias{
				"write": {"read", []string{capBookWrite, capBookRead, capBookList}},
				"read":  {"read", []string{capBookRead, capBookList}},
			},
		},
    },
}

Note that besides the capabilities associated to the book resource, we have also defined aliases, which are basically shorthands for expressing more than one capability at once. Since our ACL system always expands all aliases prior to processing the actual capabilities, we can have an alias and a capability sharing the same name.

Finally, we need to tell our ACL system how it can resolve policy names and secrets. In order to decouple the ACL system from the persistence layer, we make use of the acl.Policy and acl.Token interfaces for abstracting these two objects.

Both functions can be set in the configuration struct, as follows:

c.ResolvePolicy = func(policy string) (acl.Policy, error) {
    return someStorage.GetPolicyByName(policy)
}

c.ResolveToken = func(secret string) (acl.Token, error) {
    return someStorage.FindTokenBySecret(secret)
}

Now we can initialize our ACL:

// Initialize ACL
Initialize(c)

When we receive a request to perform an operation of any kind, say write on a specific book, we can build a new ACL object based on the secret contained in this request, and query the ACL if the operation is authorized or not:

acl, err := NewACLFromSecret("secret-in-the-request")
if err != nil {
    panic(err)
}

if !acl.IsAuthorized("book", "the-lord-of-the-rings-id", "write") {
    return fmt.Errorf("not authorized")
} 

// Proceed normally with the operation.
...

TODO:

  • Benchmark time and memory overhead;

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrTokenNotFound is returned when a token is not found
	// by the resolver function.
	ErrTokenNotFound = errors.New(errTokenNotFound)

	// ErrPolicyNotFound is returned when a policy is not found
	// by the resolver function.
	ErrPolicyNotFound = errors.New(errPolicyNotFound)

	// ErrMissingModel is returned when no Model is set in the Resolver
	// configuration.
	ErrMissingModel = errors.New(errMissingModel)

	// ErrMissingSecretResolver is returned when no SecretResolverFunc
	// is set in the Resolver configuration.
	ErrMissingSecretResolver = errors.New(errMissingSecretResolver)

	// ErrMissingPolicyResolver is returned when no PolicyResolverFunc
	// is set in the ACL configuration.
	ErrMissingPolicyResolver = errors.New(errMissingPolicyResolver)

	// ErrResolvingSecret is returned when an error occurs when resolving
	// a secret.
	ErrResolvingSecret = errors.New(errResolvingSecret)

	// ErrResolvingPolicy is returned when an error occurs when resolving
	// a policy.
	ErrResolvingPolicy = errors.New(errResolvingPolicy)

	// ErrUnauthorized is returned when the ACL does not have the
	// authorization to perform the requested operation on the specified resource.
	ErrUnauthorized = errors.New(errNotAuthorized)

	// ErrInvalidResource is returned when the resource being queried
	// is not properly configured in the ACL system.
	ErrInvalidResource = errors.New(errInvalidResource)

	// ErrInvalidOperation is returned when the operation being queried
	// is not properly configured in the ACL system.
	ErrInvalidOperation = errors.New(errInvalidOperation)
)

Functions

This section is empty.

Types

type ACL

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

ACL is used to convert a set of policies into a structure that can be efficiently evaluated to determine if an action is allowed.

func (*ACL) CheckAuthorized

func (a *ACL) CheckAuthorized(ctx context.Context, res string, path string, op string) error

CheckAuthorized verifies whether the ACL is authorized to perform a specific action. If the ACL is not authorized, an error is returned, which provides more details. If an operation is not explicitly enabled in the ACL, it is forbidden by default.

func (*ACL) String

func (a *ACL) String() string

String builds a string representation of an ACL which can be useful for debugging purposes.

type Model

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

Model ...

func NewModel

func NewModel() *Model

NewModel creates a new ACL model.

func (*Model) Resource

func (m *Model) Resource(res string) Resource

Resource configures a resource type within the ACL system, and returns a Resource interface which allows for the configuration of capabilities and aliases associated with this resource.

type Policy

type Policy interface {
	Name() string
	Rules() []Rule
}

Policy represents a named set of rules for allowing operations on specific resources.

type PolicyResolverFunc

type PolicyResolverFunc func(context.Context, string) (Policy, error)

PolicyResolverFunc returns a policy based on its name, or an error if it does not exist.

type Resolver

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

Resolver resolves ACL secrets and policies.

func NewResolver

func NewResolver(config *ResolverConfig) (*Resolver, error)

NewResolver ...

func (*Resolver) PolicyResolver

func (r *Resolver) PolicyResolver(f PolicyResolverFunc)

PolicyResolver configures how policy names are resolved to ACL policies.

func (*Resolver) ResolveSecret

func (r *Resolver) ResolveSecret(ctx context.Context, secret string) (*ACL, error)

ResolveSecret creates an ACL from a secret.

func (*Resolver) SecretResolver

func (r *Resolver) SecretResolver(f SecretResolverFunc)

SecretResolver configures how secrets are resolved to ACL tokens.

type ResolverConfig

type ResolverConfig struct {
	Logger         log.Logger
	Model          *Model
	SecretResolver SecretResolverFunc
	PolicyResolver PolicyResolverFunc
}

ResolverConfig contains configurations for an ACL Resolver.

func DefaultResolverConfig

func DefaultResolverConfig() *ResolverConfig

DefaultResolverConfig returns default configurations for an ACL Resolver.

func (*ResolverConfig) Merge

Merge merges two ResolverConfig structs, returning the result.

type Resource

type Resource interface {
	Capabilities(...string) Resource
	Alias(string, ...string) Resource
}

Resource provides functions for the configuration of capabilities and aliases associated to a resource.

type Rule

type Rule interface {
	// Resource targeted by this rule.
	Resource() string
	// Path used to target specific instances
	// of the target resource, if applicable.
	Path() string
	// Capabilities contains the actions allowed on
	// instances of a resource matching this rule.
	Capabilities() []string
}

Rule is used to allow operations on specific resources. In addition to the name of the target resource type and the allowed operations, a rule also specifies an optional glob pattern for targeting specific instances of the resource.

type SecretResolverFunc

type SecretResolverFunc func(context.Context, string) (Token, error)

SecretResolverFunc returns the token associated with a secret if it exists, or nil otherwise.

type Token

type Token interface {
	IsPrivileged() bool
	Policies() []string
}

Token ...

Jump to

Keyboard shortcuts

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