mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
* chore: authz 'any_org' to return if at least 1 org has perms Allows checking if a user can do an action in any organization, rather than a specific one. Allows asking general questions on the UI to determine which elements to show. * more strict, add comments to policy * add unit tests and extend to /authcheck api * make field optional
216 lines
5.1 KiB
Go
216 lines
5.1 KiB
Go
package rbac
|
|
|
|
import (
|
|
"github.com/google/uuid"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/v2/coderd/rbac/policy"
|
|
)
|
|
|
|
// ResourceUserObject is a helper function to create a user object for authz checks.
|
|
func ResourceUserObject(userID uuid.UUID) Object {
|
|
return ResourceUser.WithID(userID).WithOwner(userID.String())
|
|
}
|
|
|
|
// 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.
|
|
type Object struct {
|
|
// ID is the resource's uuid
|
|
ID string `json:"id"`
|
|
Owner string `json:"owner"`
|
|
// OrgID specifies which org the object is a part of.
|
|
OrgID string `json:"org_owner"`
|
|
// AnyOrgOwner will disregard the org_owner when checking for permissions
|
|
// Use this to ask, "Can the actor do this action on any org?" when
|
|
// the exact organization is not important or known.
|
|
// E.g: The UI should show a "create template" button if the user
|
|
// can create a template in any org.
|
|
AnyOrgOwner bool `json:"any_org"`
|
|
|
|
// Type is "workspace", "project", "app", etc
|
|
Type string `json:"type"`
|
|
|
|
ACLUserList map[string][]policy.Action ` json:"acl_user_list"`
|
|
ACLGroupList map[string][]policy.Action ` json:"acl_group_list"`
|
|
}
|
|
|
|
// ValidAction checks if the action is valid for the given object type.
|
|
func (z Object) ValidAction(action policy.Action) error {
|
|
perms, ok := policy.RBACPermissions[z.Type]
|
|
if !ok {
|
|
return xerrors.Errorf("invalid type %q", z.Type)
|
|
}
|
|
if _, ok := perms.Actions[action]; !ok {
|
|
return xerrors.Errorf("invalid action %q for type %q", action, z.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AvailableActions returns all available actions for a given object.
|
|
// Wildcard is omitted.
|
|
func (z Object) AvailableActions() []policy.Action {
|
|
perms, ok := policy.RBACPermissions[z.Type]
|
|
if !ok {
|
|
return []policy.Action{}
|
|
}
|
|
|
|
actions := make([]policy.Action, 0, len(perms.Actions))
|
|
for action := range perms.Actions {
|
|
actions = append(actions, action)
|
|
}
|
|
|
|
return actions
|
|
}
|
|
|
|
func (z Object) Equal(b Object) bool {
|
|
if z.ID != b.ID {
|
|
return false
|
|
}
|
|
if z.Owner != b.Owner {
|
|
return false
|
|
}
|
|
if z.OrgID != b.OrgID {
|
|
return false
|
|
}
|
|
if z.Type != b.Type {
|
|
return false
|
|
}
|
|
|
|
if !equalACLLists(z.ACLUserList, b.ACLUserList) {
|
|
return false
|
|
}
|
|
|
|
if !equalACLLists(z.ACLGroupList, b.ACLGroupList) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func equalACLLists(a, b map[string][]policy.Action) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
|
|
for k, actions := range a {
|
|
if len(actions) != len(b[k]) {
|
|
return false
|
|
}
|
|
for i, a := range actions {
|
|
if a != b[k][i] {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (z Object) RBACObject() Object {
|
|
return z
|
|
}
|
|
|
|
// All returns an object matching all resources of the same type.
|
|
func (z Object) All() Object {
|
|
return Object{
|
|
Owner: "",
|
|
OrgID: "",
|
|
Type: z.Type,
|
|
ACLUserList: map[string][]policy.Action{},
|
|
ACLGroupList: map[string][]policy.Action{},
|
|
AnyOrgOwner: z.AnyOrgOwner,
|
|
}
|
|
}
|
|
|
|
func (z Object) WithIDString(id string) Object {
|
|
return Object{
|
|
ID: id,
|
|
Owner: z.Owner,
|
|
OrgID: z.OrgID,
|
|
Type: z.Type,
|
|
ACLUserList: z.ACLUserList,
|
|
ACLGroupList: z.ACLGroupList,
|
|
AnyOrgOwner: z.AnyOrgOwner,
|
|
}
|
|
}
|
|
|
|
func (z Object) WithID(id uuid.UUID) Object {
|
|
return Object{
|
|
ID: id.String(),
|
|
Owner: z.Owner,
|
|
OrgID: z.OrgID,
|
|
Type: z.Type,
|
|
ACLUserList: z.ACLUserList,
|
|
ACLGroupList: z.ACLGroupList,
|
|
AnyOrgOwner: z.AnyOrgOwner,
|
|
}
|
|
}
|
|
|
|
// InOrg adds an org OwnerID to the resource
|
|
func (z Object) InOrg(orgID uuid.UUID) Object {
|
|
return Object{
|
|
ID: z.ID,
|
|
Owner: z.Owner,
|
|
OrgID: orgID.String(),
|
|
Type: z.Type,
|
|
ACLUserList: z.ACLUserList,
|
|
ACLGroupList: z.ACLGroupList,
|
|
// InOrg implies AnyOrgOwner is false
|
|
AnyOrgOwner: false,
|
|
}
|
|
}
|
|
|
|
func (z Object) AnyOrganization() Object {
|
|
return Object{
|
|
ID: z.ID,
|
|
Owner: z.Owner,
|
|
// AnyOrgOwner cannot have an org owner also set.
|
|
OrgID: "",
|
|
Type: z.Type,
|
|
ACLUserList: z.ACLUserList,
|
|
ACLGroupList: z.ACLGroupList,
|
|
AnyOrgOwner: true,
|
|
}
|
|
}
|
|
|
|
// WithOwner adds an OwnerID to the resource
|
|
func (z Object) WithOwner(ownerID string) Object {
|
|
return Object{
|
|
ID: z.ID,
|
|
Owner: ownerID,
|
|
OrgID: z.OrgID,
|
|
Type: z.Type,
|
|
ACLUserList: z.ACLUserList,
|
|
ACLGroupList: z.ACLGroupList,
|
|
AnyOrgOwner: z.AnyOrgOwner,
|
|
}
|
|
}
|
|
|
|
// WithACLUserList adds an ACL list to a given object
|
|
func (z Object) WithACLUserList(acl map[string][]policy.Action) Object {
|
|
return Object{
|
|
ID: z.ID,
|
|
Owner: z.Owner,
|
|
OrgID: z.OrgID,
|
|
Type: z.Type,
|
|
ACLUserList: acl,
|
|
ACLGroupList: z.ACLGroupList,
|
|
AnyOrgOwner: z.AnyOrgOwner,
|
|
}
|
|
}
|
|
|
|
func (z Object) WithGroupACL(groups map[string][]policy.Action) Object {
|
|
return Object{
|
|
ID: z.ID,
|
|
Owner: z.Owner,
|
|
OrgID: z.OrgID,
|
|
Type: z.Type,
|
|
ACLUserList: z.ACLUserList,
|
|
ACLGroupList: groups,
|
|
AnyOrgOwner: z.AnyOrgOwner,
|
|
}
|
|
}
|