mirror of https://github.com/coder/coder.git
chore: Drop resource_id support in rbac system (#3426)
This commit is contained in:
parent
ccf6f4e7ed
commit
db665e7261
|
@ -219,7 +219,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
authorizer.AlwaysReturn = rbac.ForbiddenWithInternal(xerrors.New("fake implementation"), nil, nil)
|
||||
|
||||
// Some quick reused objects
|
||||
workspaceRBACObj := rbac.ResourceWorkspace.InOrg(organization.ID).WithID(workspace.ID.String()).WithOwner(workspace.OwnerID.String())
|
||||
workspaceRBACObj := rbac.ResourceWorkspace.InOrg(organization.ID).WithOwner(workspace.OwnerID.String())
|
||||
|
||||
// skipRoutes allows skipping routes from being checked.
|
||||
skipRoutes := map[string]string{
|
||||
|
@ -346,7 +346,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
"GET:/api/v2/organizations/{organization}/templates": {
|
||||
StatusCode: http.StatusOK,
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"POST:/api/v2/organizations/{organization}/templates": {
|
||||
AssertAction: rbac.ActionCreate,
|
||||
|
@ -354,99 +354,99 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
},
|
||||
"DELETE:/api/v2/templates/{template}": {
|
||||
AssertAction: rbac.ActionDelete,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templates/{template}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"POST:/api/v2/files": {AssertAction: rbac.ActionCreate, AssertObject: rbac.ResourceFile},
|
||||
"GET:/api/v2/files/{fileHash}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceFile.WithOwner(admin.UserID.String()).WithID(file.Hash),
|
||||
AssertObject: rbac.ResourceFile.WithOwner(admin.UserID.String()),
|
||||
},
|
||||
"GET:/api/v2/templates/{template}/versions": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"PATCH:/api/v2/templates/{template}/versions": {
|
||||
AssertAction: rbac.ActionUpdate,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templates/{template}/versions/{templateversionname}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"PATCH:/api/v2/templateversions/{templateversion}/cancel": {
|
||||
AssertAction: rbac.ActionUpdate,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/logs": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/parameters": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/resources": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/schema": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"POST:/api/v2/templateversions/{templateversion}/dry-run": {
|
||||
// The first check is to read the template
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/dry-run/{templateversiondryrun}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/dry-run/{templateversiondryrun}/resources": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/templateversions/{templateversion}/dry-run/{templateversiondryrun}/logs": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID),
|
||||
},
|
||||
"PATCH:/api/v2/templateversions/{templateversion}/dry-run/{templateversiondryrun}/cancel": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(version.OrganizationID),
|
||||
},
|
||||
"GET:/api/v2/provisionerdaemons": {
|
||||
StatusCode: http.StatusOK,
|
||||
AssertObject: rbac.ResourceProvisionerDaemon.WithID(provisionerds[0].ID.String()),
|
||||
AssertObject: rbac.ResourceProvisionerDaemon,
|
||||
},
|
||||
|
||||
"POST:/api/v2/parameters/{scope}/{id}": {
|
||||
AssertAction: rbac.ActionUpdate,
|
||||
AssertObject: rbac.ResourceTemplate.WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate,
|
||||
},
|
||||
"GET:/api/v2/parameters/{scope}/{id}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate,
|
||||
},
|
||||
"DELETE:/api/v2/parameters/{scope}/{id}/{name}": {
|
||||
AssertAction: rbac.ActionUpdate,
|
||||
AssertObject: rbac.ResourceTemplate.WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate,
|
||||
},
|
||||
"GET:/api/v2/organizations/{organization}/templates/{templatename}": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID).WithID(template.ID.String()),
|
||||
AssertObject: rbac.ResourceTemplate.InOrg(template.OrganizationID),
|
||||
},
|
||||
"POST:/api/v2/organizations/{organization}/workspaces": {
|
||||
AssertAction: rbac.ActionCreate,
|
||||
// No ID when creating
|
||||
AssertObject: workspaceRBACObj.WithID(""),
|
||||
AssertObject: workspaceRBACObj,
|
||||
},
|
||||
"GET:/api/v2/workspaces/{workspace}/watch": {
|
||||
AssertAction: rbac.ActionRead,
|
||||
|
@ -546,9 +546,6 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
|
|||
if routeAssertions.AssertObject.OrgID != "" {
|
||||
assert.Equal(t, routeAssertions.AssertObject.OrgID, authorizer.Called.Object.OrgID, "resource org")
|
||||
}
|
||||
if routeAssertions.AssertObject.ResourceID != "" {
|
||||
assert.Equal(t, routeAssertions.AssertObject.ResourceID, authorizer.Called.Object.ResourceID, "resource ID")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, authorizer.Called, "authorize not expected")
|
||||
|
|
|
@ -275,10 +275,15 @@ func CreateFirstUser(t *testing.T, client *codersdk.Client) codersdk.CreateFirst
|
|||
|
||||
// CreateAnotherUser creates and authenticates a new user.
|
||||
func CreateAnotherUser(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, roles ...string) *codersdk.Client {
|
||||
userClient, _ := createAnotherUserRetry(t, client, organizationID, 5, roles...)
|
||||
return userClient
|
||||
}
|
||||
|
||||
func CreateAnotherUserWithUser(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, roles ...string) (*codersdk.Client, codersdk.User) {
|
||||
return createAnotherUserRetry(t, client, organizationID, 5, roles...)
|
||||
}
|
||||
|
||||
func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, retries int, roles ...string) *codersdk.Client {
|
||||
func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, retries int, roles ...string) (*codersdk.Client, codersdk.User) {
|
||||
req := codersdk.CreateUserRequest{
|
||||
Email: namesgenerator.GetRandomName(10) + "@coder.com",
|
||||
Username: randomUsername(),
|
||||
|
@ -337,7 +342,7 @@ func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationI
|
|||
require.NoError(t, err, "update org membership roles")
|
||||
}
|
||||
}
|
||||
return other
|
||||
return other, user
|
||||
}
|
||||
|
||||
// CreateTemplateVersion creates a template import provisioner job
|
||||
|
|
|
@ -5,37 +5,37 @@ import (
|
|||
)
|
||||
|
||||
func (t Template) RBACObject() rbac.Object {
|
||||
return rbac.ResourceTemplate.InOrg(t.OrganizationID).WithID(t.ID.String())
|
||||
return rbac.ResourceTemplate.InOrg(t.OrganizationID)
|
||||
}
|
||||
|
||||
func (t TemplateVersion) RBACObject() rbac.Object {
|
||||
// Just use the parent template resource for controlling versions
|
||||
return rbac.ResourceTemplate.InOrg(t.OrganizationID).WithID(t.TemplateID.UUID.String())
|
||||
return rbac.ResourceTemplate.InOrg(t.OrganizationID)
|
||||
}
|
||||
|
||||
func (w Workspace) RBACObject() rbac.Object {
|
||||
return rbac.ResourceWorkspace.InOrg(w.OrganizationID).WithID(w.ID.String()).WithOwner(w.OwnerID.String())
|
||||
return rbac.ResourceWorkspace.InOrg(w.OrganizationID).WithOwner(w.OwnerID.String())
|
||||
}
|
||||
|
||||
func (m OrganizationMember) RBACObject() rbac.Object {
|
||||
return rbac.ResourceOrganizationMember.InOrg(m.OrganizationID).WithID(m.UserID.String())
|
||||
return rbac.ResourceOrganizationMember.InOrg(m.OrganizationID)
|
||||
}
|
||||
|
||||
func (o Organization) RBACObject() rbac.Object {
|
||||
return rbac.ResourceOrganization.InOrg(o.ID).WithID(o.ID.String())
|
||||
return rbac.ResourceOrganization.InOrg(o.ID)
|
||||
}
|
||||
|
||||
func (d ProvisionerDaemon) RBACObject() rbac.Object {
|
||||
return rbac.ResourceProvisionerDaemon.WithID(d.ID.String())
|
||||
func (ProvisionerDaemon) RBACObject() rbac.Object {
|
||||
return rbac.ResourceProvisionerDaemon
|
||||
}
|
||||
|
||||
func (f File) RBACObject() rbac.Object {
|
||||
return rbac.ResourceFile.WithID(f.Hash).WithOwner(f.CreatedBy.String())
|
||||
return rbac.ResourceFile.WithOwner(f.CreatedBy.String())
|
||||
}
|
||||
|
||||
// RBACObject returns the RBAC object for the site wide user resource.
|
||||
// If you are trying to get the RBAC object for the UserData, use
|
||||
// rbac.ResourceUserData
|
||||
func (u User) RBACObject() rbac.Object {
|
||||
return rbac.ResourceUser.WithID(u.ID.String())
|
||||
func (User) RBACObject() rbac.Object {
|
||||
return rbac.ResourceUser
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ func (api *API) fileByHash(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead,
|
||||
rbac.ResourceFile.WithOwner(file.CreatedBy.String()).WithID(file.Hash)) {
|
||||
rbac.ResourceFile.WithOwner(file.CreatedBy.String())) {
|
||||
// Return 404 to not leak the file exists
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
|
|
|
@ -21,6 +21,7 @@ func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
organization := httpmw.OrganizationParam(r)
|
||||
member := httpmw.OrganizationMemberParam(r)
|
||||
apiKey := httpmw.APIKey(r)
|
||||
actorRoles := httpmw.AuthorizationUserRoles(r)
|
||||
|
||||
if apiKey.UserID == member.UserID {
|
||||
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
|
||||
|
@ -37,16 +38,22 @@ func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
// The org-member role is always implied.
|
||||
impliedTypes := append(params.Roles, rbac.RoleOrgMember(organization.ID))
|
||||
added, removed := rbac.ChangeRoleSet(member.Roles, impliedTypes)
|
||||
for _, roleName := range added {
|
||||
// Assigning a role requires the create permission.
|
||||
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceOrgRoleAssignment.WithID(roleName).InOrg(organization.ID)) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
||||
// Assigning a role requires the create permission.
|
||||
if len(added) > 0 && !api.Authorize(r, rbac.ActionCreate, rbac.ResourceOrgRoleAssignment.InOrg(organization.ID)) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
for _, roleName := range removed {
|
||||
// Removing a role requires the delete permission.
|
||||
if !api.Authorize(r, rbac.ActionDelete, rbac.ResourceOrgRoleAssignment.WithID(roleName).InOrg(organization.ID)) {
|
||||
|
||||
// Removing a role requires the delete permission.
|
||||
if len(removed) > 0 && !api.Authorize(r, rbac.ActionDelete, rbac.ResourceOrgRoleAssignment.InOrg(organization.ID)) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
||||
// Just treat adding & removing as "assigning" for now.
|
||||
for _, roleName := range append(added, removed...) {
|
||||
if !rbac.CanAssignRole(actorRoles.Roles, roleName) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ func (api *API) organization(rw http.ResponseWriter, r *http.Request) {
|
|||
organization := httpmw.OrganizationParam(r)
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceOrganization.
|
||||
InOrg(organization.ID).
|
||||
WithID(organization.ID.String())) {
|
||||
InOrg(organization.ID)) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package rbac_test
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
@ -30,9 +28,8 @@ func TestFilter(t *testing.T) {
|
|||
workspaceList := make([]rbac.Object, 0)
|
||||
fileList := make([]rbac.Object, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
idxStr := strconv.Itoa(i)
|
||||
workspace := rbac.ResourceWorkspace.WithID(idxStr).WithOwner("me")
|
||||
file := rbac.ResourceFile.WithID(idxStr).WithOwner("me")
|
||||
workspace := rbac.ResourceWorkspace.WithOwner("me")
|
||||
file := rbac.ResourceFile.WithOwner("me")
|
||||
|
||||
workspaceList = append(workspaceList, workspace)
|
||||
fileList = append(fileList, file)
|
||||
|
@ -116,7 +113,6 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
t.Parallel()
|
||||
defOrg := uuid.New()
|
||||
unuseID := uuid.New()
|
||||
wrkID := "1234"
|
||||
|
||||
user := subject{
|
||||
UserID: "me",
|
||||
|
@ -127,42 +123,28 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
}
|
||||
|
||||
testAuthorize(t, "Member", user, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
|
||||
// Other org + other us
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false},
|
||||
})
|
||||
|
||||
user = subject{
|
||||
|
@ -174,7 +156,6 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
{
|
||||
Negate: true,
|
||||
ResourceType: rbac.WildcardSymbol,
|
||||
ResourceID: rbac.WildcardSymbol,
|
||||
Action: rbac.WildcardSymbol,
|
||||
},
|
||||
},
|
||||
|
@ -182,42 +163,28 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
}
|
||||
|
||||
testAuthorize(t, "DeletedMember", user, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false},
|
||||
})
|
||||
|
||||
user = subject{
|
||||
|
@ -229,42 +196,28 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
}
|
||||
|
||||
testAuthorize(t, "OrgAdmin", user, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false},
|
||||
})
|
||||
|
||||
user = subject{
|
||||
|
@ -276,57 +229,44 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
}
|
||||
|
||||
testAuthorize(t, "SiteAdmin", user, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: true},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: true},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: true},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: true},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: true},
|
||||
})
|
||||
|
||||
// In practice this is a token scope on a regular subject
|
||||
// In practice this is a token scope on a regular subject.
|
||||
// So this unit test does not represent a practical role. It is just
|
||||
// testing the capabilities of the RBAC system.
|
||||
user = subject{
|
||||
UserID: "me",
|
||||
Roles: []rbac.Role{
|
||||
{
|
||||
Name: fmt.Sprintf("agent-%s", wrkID),
|
||||
Name: "WorkspaceToken",
|
||||
// This is at the site level to prevent the token from losing access if the user
|
||||
// is kicked from the org
|
||||
Site: []rbac.Permission{
|
||||
{
|
||||
Negate: false,
|
||||
ResourceType: rbac.ResourceWorkspace.Type,
|
||||
ResourceID: wrkID,
|
||||
Action: rbac.ActionRead,
|
||||
},
|
||||
},
|
||||
|
@ -334,48 +274,34 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
testAuthorize(t, "WorkspaceAgentToken", user,
|
||||
testAuthorize(t, "WorkspaceToken", user,
|
||||
// Read Actions
|
||||
cases(func(c authTestCase) authTestCase {
|
||||
c.actions = []rbac.Action{rbac.ActionRead}
|
||||
return c
|
||||
}, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: false},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.All(), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), allow: false},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: true},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: true},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: true},
|
||||
}),
|
||||
// Not read actions
|
||||
cases(func(c authTestCase) authTestCase {
|
||||
|
@ -383,42 +309,28 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
c.allow = false
|
||||
return c
|
||||
}, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All()},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID)},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id")},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me")},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id")},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id")},
|
||||
}),
|
||||
)
|
||||
|
||||
|
@ -433,7 +345,6 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
defOrg.String(): {{
|
||||
Negate: false,
|
||||
ResourceType: "*",
|
||||
ResourceID: "*",
|
||||
Action: rbac.ActionRead,
|
||||
}},
|
||||
},
|
||||
|
@ -441,7 +352,6 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
{
|
||||
Negate: false,
|
||||
ResourceType: "*",
|
||||
ResourceID: "*",
|
||||
Action: rbac.ActionRead,
|
||||
},
|
||||
},
|
||||
|
@ -455,42 +365,28 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
return c
|
||||
}, []authTestCase{
|
||||
// Read
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), allow: false},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), allow: false},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), allow: false},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false},
|
||||
}),
|
||||
|
||||
// Pass non-read actions
|
||||
|
@ -500,42 +396,28 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
return c
|
||||
}, []authTestCase{
|
||||
// Read
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All()},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID)},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id")},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me")},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id")},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id")},
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -544,7 +426,6 @@ func TestAuthorizeDomain(t *testing.T) {
|
|||
func TestAuthorizeLevels(t *testing.T) {
|
||||
defOrg := uuid.New()
|
||||
unusedID := uuid.New()
|
||||
wrkID := "1234"
|
||||
|
||||
user := subject{
|
||||
UserID: "me",
|
||||
|
@ -557,7 +438,6 @@ func TestAuthorizeLevels(t *testing.T) {
|
|||
{
|
||||
Negate: true,
|
||||
ResourceType: "*",
|
||||
ResourceID: "*",
|
||||
Action: "*",
|
||||
},
|
||||
},
|
||||
|
@ -570,7 +450,6 @@ func TestAuthorizeLevels(t *testing.T) {
|
|||
{
|
||||
Negate: true,
|
||||
ResourceType: rbac.WildcardSymbol,
|
||||
ResourceID: rbac.WildcardSymbol,
|
||||
Action: rbac.WildcardSymbol,
|
||||
},
|
||||
},
|
||||
|
@ -584,42 +463,28 @@ func TestAuthorizeLevels(t *testing.T) {
|
|||
c.allow = true
|
||||
return c
|
||||
}, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All()},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID).WithID(wrkID)},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID)},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me").WithID("not-id")},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me")},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID("not-id")},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID)},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id")},
|
||||
}))
|
||||
|
||||
user = subject{
|
||||
|
@ -631,7 +496,6 @@ func TestAuthorizeLevels(t *testing.T) {
|
|||
{
|
||||
Negate: true,
|
||||
ResourceType: "random",
|
||||
ResourceID: rbac.WildcardSymbol,
|
||||
Action: rbac.WildcardSymbol,
|
||||
},
|
||||
},
|
||||
|
@ -644,7 +508,6 @@ func TestAuthorizeLevels(t *testing.T) {
|
|||
{
|
||||
Negate: true,
|
||||
ResourceType: rbac.WildcardSymbol,
|
||||
ResourceID: rbac.WildcardSymbol,
|
||||
Action: rbac.WildcardSymbol,
|
||||
},
|
||||
},
|
||||
|
@ -657,42 +520,28 @@ func TestAuthorizeLevels(t *testing.T) {
|
|||
c.actions = allActions()
|
||||
return c
|
||||
}, []authTestCase{
|
||||
// Org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true},
|
||||
// Org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID(wrkID), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.All(), allow: false},
|
||||
|
||||
// Other org + me + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID).WithID(wrkID), allow: false},
|
||||
// Other org + me
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID(wrkID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID), allow: false},
|
||||
|
||||
// Other org + other user + id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true},
|
||||
// Other org + other user
|
||||
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
// Other org + other use + other id
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me").WithID("not-id"), allow: false},
|
||||
// Other org + other use
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.InOrg(unusedID), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false},
|
||||
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
|
||||
|
||||
{resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false},
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -714,6 +563,7 @@ type authTestCase struct {
|
|||
}
|
||||
|
||||
func testAuthorize(t *testing.T, name string, subject subject, sets ...[]authTestCase) {
|
||||
t.Helper()
|
||||
authorizer, err := rbac.NewAuthorizer()
|
||||
require.NoError(t, err)
|
||||
for _, cases := range sets {
|
||||
|
|
|
@ -82,7 +82,7 @@ var (
|
|||
// TODO: Finish the auditor as we add resources.
|
||||
auditor: func(_ string) Role {
|
||||
return Role{
|
||||
Name: "auditor",
|
||||
Name: auditor,
|
||||
DisplayName: "Auditor",
|
||||
Site: permissions(map[Object][]Action{
|
||||
// Should be able to read all template details, even in orgs they
|
||||
|
@ -103,7 +103,6 @@ var (
|
|||
{
|
||||
Negate: false,
|
||||
ResourceType: "*",
|
||||
ResourceID: "*",
|
||||
Action: "*",
|
||||
},
|
||||
},
|
||||
|
@ -123,24 +122,20 @@ var (
|
|||
// All org members can read the other members in their org.
|
||||
ResourceType: ResourceOrganizationMember.Type,
|
||||
Action: ActionRead,
|
||||
ResourceID: "*",
|
||||
},
|
||||
{
|
||||
// All org members can read the organization
|
||||
ResourceType: ResourceOrganization.Type,
|
||||
Action: ActionRead,
|
||||
ResourceID: "*",
|
||||
},
|
||||
{
|
||||
// All org members can read templates in the org
|
||||
ResourceType: ResourceTemplate.Type,
|
||||
Action: ActionRead,
|
||||
ResourceID: "*",
|
||||
},
|
||||
{
|
||||
// Can read available roles.
|
||||
ResourceType: ResourceOrgRoleAssignment.Type,
|
||||
ResourceID: "*",
|
||||
Action: ActionRead,
|
||||
},
|
||||
},
|
||||
|
@ -150,6 +145,58 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// assignRoles is a map of roles that can be assigned if a user has a given
|
||||
// role.
|
||||
// The first key is the actor role, the second is the roles they can assign.
|
||||
// map[actor_role][assign_role]<can_assign>
|
||||
assignRoles = map[string]map[string]bool{
|
||||
admin: {
|
||||
admin: true,
|
||||
auditor: true,
|
||||
member: true,
|
||||
orgAdmin: true,
|
||||
orgMember: true,
|
||||
},
|
||||
orgAdmin: {
|
||||
orgAdmin: true,
|
||||
orgMember: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// CanAssignRole is a helper function that returns true if the user can assign
|
||||
// the specified role. This also can be used for removing a role.
|
||||
// This is a simple implementation for now.
|
||||
func CanAssignRole(roles []string, assignedRole string) bool {
|
||||
assigned, assignedOrg, err := roleSplit(assignedRole)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, longRole := range roles {
|
||||
role, orgID, err := roleSplit(longRole)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if orgID != "" && orgID != assignedOrg {
|
||||
// Org roles only apply to the org they are assigned to.
|
||||
continue
|
||||
}
|
||||
|
||||
allowed, ok := assignRoles[role]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if allowed[assigned] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RoleByName returns the permissions associated with a given role name.
|
||||
// This allows just the role names to be stored and expanded when required.
|
||||
func RoleByName(name string) (Role, error) {
|
||||
|
@ -292,7 +339,6 @@ func permissions(perms map[Object][]Action) []Permission {
|
|||
list = append(list, Permission{
|
||||
Negate: false,
|
||||
ResourceType: k.Type,
|
||||
ResourceID: WildcardSymbol,
|
||||
Action: act,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -12,6 +12,87 @@ import (
|
|||
"github.com/coder/coder/coderd/rbac"
|
||||
)
|
||||
|
||||
// BenchmarkRBACFilter benchmarks the rbac.Filter method.
|
||||
// go test -bench BenchmarkRBACFilter -benchmem -memprofile memprofile.out -cpuprofile profile.out
|
||||
func BenchmarkRBACFilter(b *testing.B) {
|
||||
orgs := []uuid.UUID{
|
||||
uuid.MustParse("bf7b72bd-a2b1-4ef2-962c-1d698e0483f6"),
|
||||
uuid.MustParse("e4660c6f-b9de-422d-9578-cd888983a795"),
|
||||
uuid.MustParse("fb13d477-06f4-42d9-b957-f6b89bd63515"),
|
||||
}
|
||||
|
||||
users := []uuid.UUID{
|
||||
uuid.MustParse("10d03e62-7703-4df5-a358-4f76577d4e2f"),
|
||||
uuid.MustParse("4ca78b1d-f2d2-4168-9d76-cd93b51c6c1e"),
|
||||
uuid.MustParse("0632b012-49e0-4d70-a5b3-f4398f1dcd52"),
|
||||
uuid.MustParse("70dbaa7a-ea9c-4f68-a781-97b08af8461d"),
|
||||
}
|
||||
|
||||
benchCases := []struct {
|
||||
Name string
|
||||
Roles []string
|
||||
UserID uuid.UUID
|
||||
}{
|
||||
{
|
||||
Name: "NoRoles",
|
||||
Roles: []string{},
|
||||
UserID: users[0],
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
// Give some extra roles that an admin might have
|
||||
Roles: []string{rbac.RoleOrgMember(orgs[0]), "auditor", rbac.RoleAdmin(), rbac.RoleMember()},
|
||||
UserID: users[0],
|
||||
},
|
||||
{
|
||||
Name: "OrgAdmin",
|
||||
Roles: []string{rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgAdmin(orgs[0]), rbac.RoleMember()},
|
||||
UserID: users[0],
|
||||
},
|
||||
{
|
||||
Name: "OrgMember",
|
||||
// Member of 2 orgs
|
||||
Roles: []string{rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgMember(orgs[1]), rbac.RoleMember()},
|
||||
UserID: users[0],
|
||||
},
|
||||
{
|
||||
Name: "ManyRoles",
|
||||
// Admin of many orgs
|
||||
Roles: []string{
|
||||
rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgAdmin(orgs[0]),
|
||||
rbac.RoleOrgMember(orgs[1]), rbac.RoleOrgAdmin(orgs[1]),
|
||||
rbac.RoleOrgMember(orgs[2]), rbac.RoleOrgAdmin(orgs[2]),
|
||||
rbac.RoleMember()},
|
||||
UserID: users[0],
|
||||
},
|
||||
}
|
||||
|
||||
authorizer, err := rbac.NewAuthorizer()
|
||||
if err != nil {
|
||||
require.NoError(b, err)
|
||||
}
|
||||
for _, c := range benchCases {
|
||||
b.Run(c.Name, func(b *testing.B) {
|
||||
objects := benchmarkSetup(orgs, users, b.N)
|
||||
b.ResetTimer()
|
||||
allowed := rbac.Filter(context.Background(), authorizer, c.UserID.String(), c.Roles, rbac.ActionRead, objects)
|
||||
var _ = allowed
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkSetup(orgs []uuid.UUID, users []uuid.UUID, size int) []rbac.Object {
|
||||
// Create a "random" but deterministic set of objects.
|
||||
objectList := make([]rbac.Object, size)
|
||||
for i := range objectList {
|
||||
objectList[i] = rbac.ResourceWorkspace.
|
||||
InOrg(orgs[i%len(orgs)]).
|
||||
WithOwner(users[i%len(users)].String())
|
||||
}
|
||||
|
||||
return objectList
|
||||
}
|
||||
|
||||
type authSubject struct {
|
||||
// Name is helpful for test assertions
|
||||
Name string
|
||||
|
@ -61,7 +142,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "MyUser",
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceUser.WithID(currentUser.String()),
|
||||
Resource: rbac.ResourceUser,
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin},
|
||||
false: {},
|
||||
|
@ -80,7 +161,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
Name: "MyWorkspaceInOrg",
|
||||
// When creating the WithID won't be set, but it does not change the result.
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()).WithID(uuid.NewString()),
|
||||
Resource: rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, orgAdmin},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -89,7 +170,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "Templates",
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceTemplate.InOrg(orgID).WithID(uuid.NewString()),
|
||||
Resource: rbac.ResourceTemplate.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {memberMe, orgMemberMe, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -98,7 +179,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "ReadTemplates",
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceTemplate.InOrg(orgID).WithID(uuid.NewString()),
|
||||
Resource: rbac.ResourceTemplate.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, orgAdmin},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -116,7 +197,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "MyFile",
|
||||
Actions: []rbac.Action{rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceFile.WithID(uuid.NewString()).WithOwner(currentUser.String()),
|
||||
Resource: rbac.ResourceFile.WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, memberMe, orgMemberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -134,7 +215,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "Organizations",
|
||||
Actions: []rbac.Action{rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceOrganization.InOrg(orgID).WithID(orgID.String()),
|
||||
Resource: rbac.ResourceOrganization.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe},
|
||||
|
@ -143,7 +224,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "ReadOrganizations",
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceOrganization.InOrg(orgID).WithID(orgID.String()),
|
||||
Resource: rbac.ResourceOrganization.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe},
|
||||
false: {otherOrgAdmin, otherOrgMember, memberMe},
|
||||
|
@ -188,7 +269,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "APIKey",
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceAPIKey.WithOwner(currentUser.String()).WithID(uuid.NewString()),
|
||||
Resource: rbac.ResourceAPIKey.WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, memberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -197,7 +278,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "UserData",
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceUserData.WithOwner(currentUser.String()).WithID(currentUser.String()),
|
||||
Resource: rbac.ResourceUserData.WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgMemberMe, memberMe},
|
||||
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -206,7 +287,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "ManageOrgMember",
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
Resource: rbac.ResourceOrganizationMember.InOrg(orgID).WithID(uuid.NewString()),
|
||||
Resource: rbac.ResourceOrganizationMember.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin},
|
||||
false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember},
|
||||
|
@ -215,7 +296,7 @@ func TestRolePermissions(t *testing.T) {
|
|||
{
|
||||
Name: "ReadOrgMember",
|
||||
Actions: []rbac.Action{rbac.ActionRead},
|
||||
Resource: rbac.ResourceOrganizationMember.InOrg(orgID).WithID(uuid.NewString()),
|
||||
Resource: rbac.ResourceOrganizationMember.InOrg(orgID),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {admin, orgAdmin, orgMemberMe},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember},
|
||||
|
|
|
@ -50,9 +50,6 @@ func TestExample(t *testing.T) {
|
|||
// Note 'database.Workspace' could fulfill the object interface and be passed in directly
|
||||
err := authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg(defaultOrg).WithOwner(user.UserID))
|
||||
require.NoError(t, err, "this user can their workspace")
|
||||
|
||||
err = authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg(defaultOrg).WithOwner(user.UserID).WithID("1234"))
|
||||
require.NoError(t, err, "this user can read workspace '1234'")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ var (
|
|||
}
|
||||
|
||||
// ResourceOrganizationMember is a user's membership in an organization.
|
||||
// Has ONLY an organization owner. The resource ID is the user's ID
|
||||
// Has ONLY an organization owner.
|
||||
// create/delete = Create/delete member from org.
|
||||
// update = Update organization member
|
||||
// read = View member
|
||||
|
@ -108,8 +108,7 @@ var (
|
|||
// 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 {
|
||||
ResourceID string `json:"id"`
|
||||
Owner string `json:"owner"`
|
||||
Owner string `json:"owner"`
|
||||
// OrgID specifies which org the object is a part of.
|
||||
OrgID string `json:"org_owner"`
|
||||
|
||||
|
@ -125,39 +124,26 @@ func (z Object) RBACObject() Object {
|
|||
// All returns an object matching all resources of the same type.
|
||||
func (z Object) All() Object {
|
||||
return Object{
|
||||
ResourceID: "",
|
||||
Owner: "",
|
||||
OrgID: "",
|
||||
Type: z.Type,
|
||||
Owner: "",
|
||||
OrgID: "",
|
||||
Type: z.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// InOrg adds an org OwnerID to the resource
|
||||
func (z Object) InOrg(orgID uuid.UUID) Object {
|
||||
return Object{
|
||||
ResourceID: z.ResourceID,
|
||||
Owner: z.Owner,
|
||||
OrgID: orgID.String(),
|
||||
Type: z.Type,
|
||||
Owner: z.Owner,
|
||||
OrgID: orgID.String(),
|
||||
Type: z.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// WithOwner adds an OwnerID to the resource
|
||||
func (z Object) WithOwner(ownerID string) Object {
|
||||
return Object{
|
||||
ResourceID: z.ResourceID,
|
||||
Owner: ownerID,
|
||||
OrgID: z.OrgID,
|
||||
Type: z.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// WithID adds a ResourceID to the resource
|
||||
func (z Object) WithID(resourceID string) Object {
|
||||
return Object{
|
||||
ResourceID: resourceID,
|
||||
Owner: z.Owner,
|
||||
OrgID: z.OrgID,
|
||||
Type: z.Type,
|
||||
Owner: ownerID,
|
||||
OrgID: z.OrgID,
|
||||
Type: z.Type,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,17 +22,16 @@ bool_flip(b) = flipped {
|
|||
# perms_grant returns a set of boolean values {true, false}.
|
||||
# True means a positive permission in the set, false is a negative permission.
|
||||
# It will only return `bool_flip(perm.negate)` for permissions that affect a given
|
||||
# resource_type, resource_id, and action.
|
||||
# resource_type, and action.
|
||||
# The empty set is returned if no relevant permissions are found.
|
||||
perms_grant(permissions) = grants {
|
||||
# If there are no permissions, this value is the empty set {}.
|
||||
grants := { x |
|
||||
# All permissions ...
|
||||
perm := permissions[_]
|
||||
# Such that the permission action, type, and resource_id matches
|
||||
# Such that the permission action, and type matches
|
||||
perm.action in [input.action, "*"]
|
||||
perm.resource_type in [input.object.type, "*"]
|
||||
perm.resource_id in [input.object.id, "*"]
|
||||
x := bool_flip(perm.negate)
|
||||
}
|
||||
}
|
||||
|
@ -137,4 +136,4 @@ allow {
|
|||
not false in user
|
||||
# And all permissions are positive
|
||||
user[_]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ 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"`
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -74,10 +74,9 @@ func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
err := api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, rbac.Action(v.Action),
|
||||
rbac.Object{
|
||||
ResourceID: v.Object.ResourceID,
|
||||
Owner: v.Object.OwnerID,
|
||||
OrgID: v.Object.OrganizationID,
|
||||
Type: v.Object.ResourceType,
|
||||
Owner: v.Object.OwnerID,
|
||||
OrgID: v.Object.OrganizationID,
|
||||
Type: v.Object.ResourceType,
|
||||
})
|
||||
response[k] = err == nil
|
||||
}
|
||||
|
|
|
@ -258,7 +258,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
|
|||
user := httpmw.UserParam(r)
|
||||
organizationIDs, err := userOrganizationIDs(r.Context(), api, user)
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
|
|||
func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceUser) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
|
|||
user := httpmw.UserParam(r)
|
||||
apiKey := httpmw.APIKey(r)
|
||||
|
||||
if !api.Authorize(r, rbac.ActionDelete, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionDelete, rbac.ResourceUser) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// admins can change passwords without sending old_password
|
||||
if params.OldPassword == "" {
|
||||
if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceUser) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
|
||||
// User is the user to modify.
|
||||
user := httpmw.UserParam(r)
|
||||
roles := httpmw.AuthorizationUserRoles(r)
|
||||
actorRoles := httpmw.AuthorizationUserRoles(r)
|
||||
apiKey := httpmw.APIKey(r)
|
||||
|
||||
if apiKey.UserID == user.ID {
|
||||
|
@ -519,24 +519,30 @@ func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser.WithID(user.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
||||
// The member role is always implied.
|
||||
impliedTypes := append(params.Roles, rbac.RoleMember())
|
||||
added, removed := rbac.ChangeRoleSet(roles.Roles, impliedTypes)
|
||||
for _, roleName := range added {
|
||||
// Assigning a role requires the create permission.
|
||||
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceRoleAssignment.WithID(roleName)) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
added, removed := rbac.ChangeRoleSet(user.RBACRoles, impliedTypes)
|
||||
|
||||
// Assigning a role requires the create permission.
|
||||
if len(added) > 0 && !api.Authorize(r, rbac.ActionCreate, rbac.ResourceRoleAssignment) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
for _, roleName := range removed {
|
||||
// Removing a role requires the delete permission.
|
||||
if !api.Authorize(r, rbac.ActionDelete, rbac.ResourceRoleAssignment.WithID(roleName)) {
|
||||
|
||||
// Removing a role requires the delete permission.
|
||||
if len(removed) > 0 && !api.Authorize(r, rbac.ActionDelete, rbac.ResourceRoleAssignment) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
||||
// Just treat adding & removing as "assigning" for now.
|
||||
for _, roleName := range append(added, removed...) {
|
||||
if !rbac.CanAssignRole(actorRoles.Roles, roleName) {
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
|
@ -631,8 +637,7 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques
|
|||
|
||||
if !api.Authorize(r, rbac.ActionRead,
|
||||
rbac.ResourceOrganization.
|
||||
InOrg(organization.ID).
|
||||
WithID(organization.ID.String())) {
|
||||
InOrg(organization.ID)) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -466,7 +466,7 @@ func TestUpdateUserPassword(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGrantRoles(t *testing.T) {
|
||||
func TestGrantSiteRoles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
requireStatusCode := func(t *testing.T, err error, statusCode int) {
|
||||
|
@ -476,140 +476,165 @@ func TestGrantRoles(t *testing.T) {
|
|||
require.Equal(t, statusCode, e.StatusCode(), "correct status code")
|
||||
}
|
||||
|
||||
t.Run("UpdateIncorrectRoles", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var err error
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
var err error
|
||||
|
||||
admin := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, admin)
|
||||
member := coderdtest.CreateAnotherUser(t, admin, first.OrganizationID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
_, err = admin.UpdateUserRoles(ctx, codersdk.Me, codersdk.UpdateRoles{
|
||||
Roles: []string{rbac.RoleOrgAdmin(first.OrganizationID)},
|
||||
})
|
||||
require.Error(t, err, "org role in site")
|
||||
requireStatusCode(t, err, http.StatusBadRequest)
|
||||
|
||||
_, err = admin.UpdateUserRoles(ctx, uuid.New().String(), codersdk.UpdateRoles{
|
||||
Roles: []string{rbac.RoleOrgAdmin(first.OrganizationID)},
|
||||
})
|
||||
require.Error(t, err, "user does not exist")
|
||||
requireStatusCode(t, err, http.StatusBadRequest)
|
||||
|
||||
_, err = admin.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, codersdk.Me, codersdk.UpdateRoles{
|
||||
Roles: []string{rbac.RoleAdmin()},
|
||||
})
|
||||
require.Error(t, err, "site role in org")
|
||||
requireStatusCode(t, err, http.StatusBadRequest)
|
||||
|
||||
_, err = admin.UpdateOrganizationMemberRoles(ctx, uuid.New(), codersdk.Me, codersdk.UpdateRoles{
|
||||
Roles: []string{},
|
||||
})
|
||||
require.Error(t, err, "role in org without membership")
|
||||
requireStatusCode(t, err, http.StatusNotFound)
|
||||
|
||||
_, err = member.UpdateUserRoles(ctx, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{},
|
||||
})
|
||||
require.Error(t, err, "member cannot change other's roles")
|
||||
requireStatusCode(t, err, http.StatusForbidden)
|
||||
|
||||
_, err = member.UpdateUserRoles(ctx, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{},
|
||||
})
|
||||
require.Error(t, err, "member cannot change any roles")
|
||||
requireStatusCode(t, err, http.StatusForbidden)
|
||||
|
||||
_, err = member.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{},
|
||||
})
|
||||
require.Error(t, err, "member cannot change other's org roles")
|
||||
requireStatusCode(t, err, http.StatusForbidden)
|
||||
|
||||
_, err = admin.UpdateUserRoles(ctx, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{},
|
||||
})
|
||||
require.Error(t, err, "admin cannot change self roles")
|
||||
requireStatusCode(t, err, http.StatusBadRequest)
|
||||
|
||||
_, err = admin.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{},
|
||||
})
|
||||
require.Error(t, err, "admin cannot change self org roles")
|
||||
requireStatusCode(t, err, http.StatusBadRequest)
|
||||
admin := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, admin)
|
||||
member := coderdtest.CreateAnotherUser(t, admin, first.OrganizationID)
|
||||
orgAdmin := coderdtest.CreateAnotherUser(t, admin, first.OrganizationID, rbac.RoleOrgAdmin(first.OrganizationID))
|
||||
randOrg, err := admin.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
|
||||
Name: "random",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, randOrgUser := coderdtest.CreateAnotherUserWithUser(t, admin, randOrg.ID, rbac.RoleOrgAdmin(randOrg.ID))
|
||||
|
||||
t.Run("FirstUserRoles", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, client)
|
||||
const newUser = "newUser"
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Client *codersdk.Client
|
||||
OrgID uuid.UUID
|
||||
AssignToUser string
|
||||
Roles []string
|
||||
Error bool
|
||||
StatusCode int
|
||||
}{
|
||||
{
|
||||
Name: "OrgRoleInSite",
|
||||
Client: admin,
|
||||
AssignToUser: codersdk.Me,
|
||||
Roles: []string{rbac.RoleOrgAdmin(first.OrganizationID)},
|
||||
Error: true,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "UserNotExists",
|
||||
Client: admin,
|
||||
AssignToUser: uuid.NewString(),
|
||||
Roles: []string{rbac.RoleAdmin()},
|
||||
Error: true,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "MemberCannotUpdateRoles",
|
||||
Client: member,
|
||||
AssignToUser: first.UserID.String(),
|
||||
Roles: []string{},
|
||||
Error: true,
|
||||
StatusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
// Cannot update your own roles
|
||||
Name: "AdminOnSelf",
|
||||
Client: admin,
|
||||
AssignToUser: first.UserID.String(),
|
||||
Roles: []string{},
|
||||
Error: true,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "SiteRoleInOrg",
|
||||
Client: admin,
|
||||
OrgID: first.OrganizationID,
|
||||
AssignToUser: codersdk.Me,
|
||||
Roles: []string{rbac.RoleAdmin()},
|
||||
Error: true,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "RoleInNotMemberOrg",
|
||||
Client: orgAdmin,
|
||||
OrgID: randOrg.ID,
|
||||
AssignToUser: randOrgUser.ID.String(),
|
||||
Roles: []string{rbac.RoleOrgMember(randOrg.ID)},
|
||||
Error: true,
|
||||
StatusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
Name: "MemberAssignMember",
|
||||
Client: member,
|
||||
OrgID: first.OrganizationID,
|
||||
AssignToUser: first.UserID.String(),
|
||||
Roles: []string{},
|
||||
Error: true,
|
||||
StatusCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
Name: "AdminUpdateOrgSelf",
|
||||
Client: admin,
|
||||
OrgID: first.OrganizationID,
|
||||
AssignToUser: first.UserID.String(),
|
||||
Roles: []string{},
|
||||
Error: true,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
Name: "OrgAdminPromote",
|
||||
Client: orgAdmin,
|
||||
OrgID: first.OrganizationID,
|
||||
AssignToUser: newUser,
|
||||
Roles: []string{rbac.RoleOrgAdmin(first.OrganizationID)},
|
||||
Error: false,
|
||||
},
|
||||
}
|
||||
|
||||
roles, err := client.GetUserRoles(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, roles.Roles, []string{
|
||||
rbac.RoleAdmin(),
|
||||
}, "should be a member and admin")
|
||||
for _, c := range testCases {
|
||||
c := c
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
require.ElementsMatch(t, roles.OrganizationRoles[first.OrganizationID], []string{
|
||||
rbac.RoleOrgAdmin(first.OrganizationID),
|
||||
}, "should be a member and admin")
|
||||
})
|
||||
var err error
|
||||
if c.AssignToUser == newUser {
|
||||
orgID := first.OrganizationID
|
||||
if c.OrgID != uuid.Nil {
|
||||
orgID = c.OrgID
|
||||
}
|
||||
_, newUser := coderdtest.CreateAnotherUserWithUser(t, admin, orgID)
|
||||
c.AssignToUser = newUser.ID.String()
|
||||
}
|
||||
|
||||
t.Run("GrantAdmin", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
admin := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, admin)
|
||||
if c.OrgID != uuid.Nil {
|
||||
// Org assign
|
||||
_, err = c.Client.UpdateOrganizationMemberRoles(ctx, c.OrgID, c.AssignToUser, codersdk.UpdateRoles{
|
||||
Roles: c.Roles,
|
||||
})
|
||||
} else {
|
||||
// Site assign
|
||||
_, err = c.Client.UpdateUserRoles(ctx, c.AssignToUser, codersdk.UpdateRoles{
|
||||
Roles: c.Roles,
|
||||
})
|
||||
}
|
||||
|
||||
member := coderdtest.CreateAnotherUser(t, admin, first.OrganizationID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
roles, err := member.GetUserRoles(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, roles.Roles, []string{}, "should be a member")
|
||||
require.ElementsMatch(t,
|
||||
roles.OrganizationRoles[first.OrganizationID],
|
||||
[]string{},
|
||||
)
|
||||
|
||||
memberUser, err := member.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err, "fetch member")
|
||||
|
||||
// Grant
|
||||
_, err = admin.UpdateUserRoles(ctx, memberUser.ID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{
|
||||
// Promote to site admin
|
||||
rbac.RoleAdmin(),
|
||||
},
|
||||
if c.Error {
|
||||
require.Error(t, err)
|
||||
requireStatusCode(t, err, c.StatusCode)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
require.NoError(t, err, "grant member admin role")
|
||||
}
|
||||
}
|
||||
|
||||
// Promote to org admin
|
||||
_, err = admin.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, memberUser.ID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{
|
||||
// Promote to org admin
|
||||
rbac.RoleOrgAdmin(first.OrganizationID),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "grant member org admin role")
|
||||
// TestInitialRoles ensures the starting roles for the first user are correct.
|
||||
func TestInitialRoles(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
roles, err = member.GetUserRoles(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, roles.Roles, []string{
|
||||
rbac.RoleAdmin(),
|
||||
}, "should be a member and admin")
|
||||
roles, err := client.GetUserRoles(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, roles.Roles, []string{
|
||||
rbac.RoleAdmin(),
|
||||
}, "should be a member and admin")
|
||||
|
||||
require.ElementsMatch(t, roles.OrganizationRoles[first.OrganizationID], []string{
|
||||
rbac.RoleOrgAdmin(first.OrganizationID),
|
||||
}, "should be a member and admin")
|
||||
})
|
||||
require.ElementsMatch(t, roles.OrganizationRoles[first.OrganizationID], []string{
|
||||
rbac.RoleOrgAdmin(first.OrganizationID),
|
||||
}, "should be a member and admin")
|
||||
}
|
||||
|
||||
func TestPutUserSuspend(t *testing.T) {
|
||||
|
|
|
@ -25,8 +25,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
|||
workspaceBuild := httpmw.WorkspaceBuildParam(r)
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, workspace) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -240,8 +239,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
|
|||
func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
workspaceBuildName := chi.URLParam(r, "workspacebuildname")
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, workspace) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -304,8 +302,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
return
|
||||
}
|
||||
if !api.Authorize(r, action, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
if !api.Authorize(r, action, workspace) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -520,8 +517,7 @@ func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques
|
|||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionUpdate, workspace) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -575,8 +571,7 @@ func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, workspace) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
@ -602,8 +597,7 @@ func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceWorkspace.
|
||||
InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
if !api.Authorize(r, rbac.ActionRead, workspace) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue