Files
coder/coderd/rbac/role.go
Steven Masley 770c567123 feat: Add RBAC package for managing user permissions (#929)
This PR adds an RBAC package for managing using permissions:
- The top-level `authz.Authorize` function is the main user-facing entrypoint to the package.
- Actual permission evaluation is handled in `policy.rego`.
- Unit tests for `authz.Authorize` are in `authz_test.go`
- Documentation for the package is in `README.md`.

Co-authored-by: Cian Johnston <cian@coder.com>
2022-04-13 08:35:35 -05:00

139 lines
3.4 KiB
Go

package rbac
import "fmt"
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"`
}
// 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
// In most cases, you will just want to use the pre-defined roles
// below.
type Role struct {
Name string `json:"name"`
Site []Permission `json:"site"`
// Org is a map of orgid to permissions. We represent orgid as a string.
// TODO: Maybe switch to uuid, but tokens might need to support a "wildcard" org
// which could be a special uuid (like all 0s?)
Org map[string][]Permission `json:"org"`
User []Permission `json:"user"`
}
// Roles are stored as structs, so they can be serialized and stored. Until we store them elsewhere,
// const's will do just fine.
var (
// RoleAdmin is a role that allows everything everywhere.
RoleAdmin = Role{
Name: "admin",
Site: permissions(map[Object][]Action{
ResourceWildcard: {WildcardSymbol},
}),
}
// RoleMember is a role that allows access to user-level resources.
RoleMember = Role{
Name: "member",
User: permissions(map[Object][]Action{
ResourceWildcard: {WildcardSymbol},
}),
}
// RoleAuditor is an example on how to give more precise permissions
RoleAuditor = Role{
Name: "auditor",
Site: permissions(map[Object][]Action{
// TODO: @emyrk when audit logs are added, add back a read perm
//ResourceAuditLogs: {ActionRead},
// Should be able to read user details to associate with logs.
// Without this the user-id in logs is not very helpful
ResourceWorkspace: {ActionRead},
}),
}
)
func RoleOrgDenyAll(orgID string) Role {
return Role{
Name: "org-deny-" + orgID,
Org: map[string][]Permission{
orgID: {
{
Negate: true,
ResourceType: "*",
ResourceID: "*",
Action: "*",
},
},
},
}
}
// RoleOrgAdmin returns a role with all actions allows in a given
// organization scope.
func RoleOrgAdmin(orgID string) Role {
return Role{
Name: "org-admin-" + orgID,
Org: map[string][]Permission{
orgID: {
{
Negate: false,
ResourceType: "*",
ResourceID: "*",
Action: "*",
},
},
},
}
}
// RoleOrgMember returns a role with default permissions in a given
// organization scope.
func RoleOrgMember(orgID string) Role {
return Role{
Name: "org-member-" + orgID,
Org: map[string][]Permission{
orgID: {},
},
}
}
// RoleWorkspaceAgent returns a role with permission to read a given
// workspace.
func RoleWorkspaceAgent(workspaceID string) Role {
return Role{
Name: fmt.Sprintf("agent-%s", workspaceID),
// This is at the site level to prevent the token from losing access if the user
// is kicked from the org
Site: []Permission{
{
Negate: false,
ResourceType: ResourceWorkspace.Type,
ResourceID: workspaceID,
Action: ActionRead,
},
},
}
}
func permissions(perms map[Object][]Action) []Permission {
list := make([]Permission, 0, len(perms))
for k, actions := range perms {
for _, act := range actions {
act := act
list = append(list, Permission{
Negate: false,
ResourceType: k.Type,
ResourceID: WildcardSymbol,
Action: act,
})
}
}
return list
}