rbac

package
v0.7.1 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2022 License: AGPL-3.0 Imports: 6 Imported by: 0

README

Authz

Package authz implements AuthoriZation for Coder.

Overview

Authorization defines what permission a subject has to perform actions to objects:

  • Permission is binary: yes (allowed) or no (denied).
  • Subject in this case is anything that implements interface authz.Subject.
  • Action here is an enumerated list of actions, but we stick to Create, Read, Update, and Delete here.
  • Object here is anything that implements authz.Object.

Permission Structure

A permission is a rule that grants or denies access for a subject to perform an action on a object. A permission is always applied at a given level:

  • site level applies to all objects in a given Coder deployment.
  • org level applies to all objects that have an organization owner (org_owner)
  • user level applies to all objects that have an owner with the same ID as the subject.

Permissions at a higher level always override permissions at a lower level.

The effect of a permission can be:

  • positive (allows)
  • negative (denies)
  • abstain (neither allows or denies, not applicable)

Negative permissions always override positive permissions at the same level. Both negative and positive permissions override abstain at the same level.

This can be represented by the following truth table, where Y represents positive, N represents negative, and _ represents abstain:

Action Positive Negative Result
read Y _ Y
read Y N N
read _ _ _
read _ N Y

Permission Representation

Permissions are represented in string format as <sign>?<level>.<object>.<id>.<action>, where:

  • negated can be either + or -. If it is omitted, sign is assumed to be +.
  • level is either site, org, or user.
  • object is any valid resource type.
  • id is any valid UUID v4.
  • action is create, read, modify, or delete.

Example Permissions

  • +site.*.*.read: allowed to perform the read action against all objects of type app in a given Coder deployment.
  • -user.workspace.*.create: user is not allowed to create workspaces.

Roles

A role is a set of permissions. When evaluating a role's permission to form an action, all the relevant permissions for the role are combined at each level. Permissions at a higher level override permissions at a lower level.

The following table shows the per-level role evaluation. Y indicates that the role provides positive permissions, N indicates the role provides negative permissions, and _ indicates the role does not provide positive or negative permissions. YN_ indicates that the value in the cell does not matter for the access result.

Role (example) Site Org User Result
site-admin Y YN_ YN_ Y
no-permission N YN_ YN_ N
org-admin _ Y YN_ Y
non-org-member _ N YN_ N
user _ _ Y Y
_ _ N N
unauthenticated _ _ _ N

Documentation

Index

Constants

View Source
const (
	ActionCreate = "create"
	ActionRead   = "read"
	ActionUpdate = "update"
	ActionDelete = "delete"
)
View Source
const WildcardSymbol = "*"

Variables

View Source
var (
	// ResourceWorkspace CRUD. Org + User owner
	//	create/delete = make or delete workspaces
	// 	read = access workspace
	//	update = edit workspace variables
	ResourceWorkspace = Object{
		Type: "workspace",
	}

	// ResourceTemplate CRUD. Org owner only.
	//	create/delete = Make or delete a new template
	//	update = Update the template, make new template versions
	//	read = read the template and all versions associated
	ResourceTemplate = Object{
		Type: "template",
	}

	ResourceFile = Object{
		Type: "file",
	}

	ResourceProvisionerDaemon = Object{
		Type: "provisioner_daemon",
	}

	// ResourceOrganization CRUD. Has an org owner on all but 'create'.
	//	create/delete = make or delete organizations
	// 	read = view org information (Can add user owner for read)
	//	update = ??
	ResourceOrganization = Object{
		Type: "organization",
	}

	// ResourceRoleAssignment might be expanded later to allow more granular permissions
	// to modifying roles. For now, this covers all possible roles, so having this permission
	// allows granting/deleting **ALL** roles.
	// Never has an owner or org.
	//	create  = Assign roles
	//	update  = ??
	//	read	= View available roles to assign
	//	delete	= Remove role
	ResourceRoleAssignment = Object{
		Type: "assign_role",
	}

	// ResourceOrgRoleAssignment is just like ResourceRoleAssignment but for organization roles.
	ResourceOrgRoleAssignment = Object{
		Type: "assign_org_role",
	}

	// ResourceAPIKey is owned by a user.
	//	create  = Create a new api key for user
	//	update  = ??
	//	read	= View api key
	//	delete	= Delete api key
	ResourceAPIKey = Object{
		Type: "api_key",
	}

	// ResourceUser is the user in the 'users' table.
	// ResourceUser never has any owners or in an org, as it's site wide.
	// 	create/delete = make or delete a new user.
	// 	read = view all 'user' table data
	// 	update = update all 'user' table data
	ResourceUser = Object{
		Type: "user",
	}

	// ResourceUserData is any data associated with a user. A user has control
	// over their data (profile, password, etc). So this resource has an owner.
	ResourceUserData = Object{
		Type: "user_data",
	}

	// ResourceOrganizationMember is a user's membership in an organization.
	// Has ONLY an organization owner. The resource ID is the user's ID
	//	create/delete  = Create/delete member from org.
	//	update  = Update organization member
	//	read	= View member
	ResourceOrganizationMember = Object{
		Type: "organization_member",
	}

	// ResourceWildcard represents all resource types
	ResourceWildcard = Object{
		Type: WildcardSymbol,
	}
)

Resources are just typed objects. Making resources this way allows directly passing them into an Authorize function and use the chaining api.

Functions

func ChangeRoleSet added in v0.6.0

func ChangeRoleSet(from []string, to []string) (added []string, removed []string)

ChangeRoleSet is a helper function that finds the difference of 2 sets of roles. When setting a user's new roles, it is equivalent to adding and removing roles. This set determines the changes, so that the appropriate RBAC checks can be applied using "ActionCreate" and "ActionDelete" for "added" and "removed" roles respectively.

func Filter added in v0.6.0

func Filter[O Objecter](ctx context.Context, auth Authorizer, subjID string, subjRoles []string, action Action, objects []O) []O

Filter takes in a list of objects, and will filter the list removing all the elements the subject does not have permission for. Filter does not allocate a new slice, and will use the existing one passed in. This can cause memory leaks if the slice is held for a prolonged period of time.

func IsOrgRole added in v0.5.2

func IsOrgRole(roleName string) (string, bool)

func RoleAdmin

func RoleAdmin() string

func RoleMember

func RoleMember() string

func RoleOrgAdmin

func RoleOrgAdmin(organizationID uuid.UUID) string

func RoleOrgMember

func RoleOrgMember(organizationID uuid.UUID) string

Types

type Action

type Action string

Action represents the allowed actions to be done on an object.

type Authorizer added in v0.5.10

type Authorizer interface {
	ByRoleName(ctx context.Context, subjectID string, roleNames []string, action Action, object Object) error
}

type Object

type Object struct {
	ResourceID string `json:"id"`
	Owner      string `json:"owner"`
	// OrgID specifies which org the object is a part of.
	OrgID string `json:"org_owner"`

	// Type is "workspace", "project", "app", etc
	Type string `json:"type"`
}

Object is used to create objects for authz checks when you have none in hand to run the check on. An example is if you want to list all workspaces, you can create a Object that represents the set of workspaces you are trying to get access too. Do not export this type, as it can be created from a resource type constant.

func (Object) All

func (z Object) All() Object

All returns an object matching all resources of the same type.

func (Object) InOrg

func (z Object) InOrg(orgID uuid.UUID) Object

InOrg adds an org OwnerID to the resource

func (Object) RBACObject added in v0.6.0

func (z Object) RBACObject() Object

func (Object) WithID

func (z Object) WithID(resourceID string) Object

WithID adds a ResourceID to the resource

func (Object) WithOwner

func (z Object) WithOwner(ownerID string) Object

WithOwner adds an OwnerID to the resource

type Objecter added in v0.6.0

type Objecter interface {
	RBACObject() Object
}

Objecter returns the RBAC object for itself.

type Permission

type Permission struct {
	// Negate makes this a negative permission
	Negate       bool   `json:"negate"`
	ResourceType string `json:"resource_type"`
	ResourceID   string `json:"resource_id"`
	Action       Action `json:"action"`
}

Permission is the format passed into the rego.

type RegoAuthorizer

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

RegoAuthorizer will use a prepared rego query for performing authorize()

func NewAuthorizer

func NewAuthorizer() (*RegoAuthorizer, error)

func (RegoAuthorizer) Authorize

func (a RegoAuthorizer) Authorize(ctx context.Context, subjectID string, roles []Role, action Action, object Object) error

Authorize allows passing in custom Roles. This is really helpful for unit testing, as we can create custom roles to exercise edge cases.

func (RegoAuthorizer) ByRoleName added in v0.5.10

func (a RegoAuthorizer) ByRoleName(ctx context.Context, subjectID string, roleNames []string, action Action, object Object) error

ByRoleName will expand all roleNames into roles before calling Authorize(). This is the function intended to be used outside this package. The role is fetched from the builtin map located in memory.

type Role

type Role struct {
	Name string `json:"name"`
	// DisplayName is used for UI purposes. If the role has no display name,
	// that means the UI should never display it.
	DisplayName string       `json:"display_name"`
	Site        []Permission `json:"site"`
	// Org is a map of orgid to permissions. We represent orgid as a string.
	// We scope the organizations in the role so we can easily combine all the
	// roles.
	Org  map[string][]Permission `json:"org"`
	User []Permission            `json:"user"`
}

Role is a set of permissions at multiple levels: - Site level permissions apply EVERYWHERE - Org level permissions apply to EVERYTHING in a given ORG - User level permissions are the lowest This is the type passed into the rego as a json payload. Users of this package should instead **only** use the role names, and this package will expand the role names into their json payloads.

func OrganizationRoles added in v0.5.4

func OrganizationRoles(organizationID uuid.UUID) []Role

OrganizationRoles lists all roles that can be applied to an organization user in the given organization. This is the list of available roles, and specific to an organization.

This should be a list in a database, but until then we build the list from the builtins.

func RoleByName added in v0.5.2

func RoleByName(name string) (Role, error)

RoleByName returns the permissions associated with a given role name. This allows just the role names to be stored and expanded when required.

func SiteRoles added in v0.5.4

func SiteRoles() []Role

SiteRoles lists all roles that can be applied to a user. This is the list of available roles, and not specific to a user

This should be a list in a database, but until then we build the list from the builtins.

type UnauthorizedError

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

UnauthorizedError is the error type for authorization errors

func ForbiddenWithInternal

func ForbiddenWithInternal(internal error, input map[string]interface{}, output rego.ResultSet) *UnauthorizedError

ForbiddenWithInternal creates a new error that will return a simple "forbidden" to the client, logging internally the more detailed message provided.

func (UnauthorizedError) Error

func (UnauthorizedError) Error() string

Error implements the error interface.

func (*UnauthorizedError) Input

func (e *UnauthorizedError) Input() map[string]interface{}

func (*UnauthorizedError) Internal

func (e *UnauthorizedError) Internal() error

Internal allows the internal error message to be logged.

func (*UnauthorizedError) Output

func (e *UnauthorizedError) Output() rego.ResultSet

Output contains the results of the Rego query for debugging.

Jump to

Keyboard shortcuts

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