Skip to content

Commit 49d6d0f

Browse files
authored
chore: add built in organization roles to match site (coder#13938)
* chore: add built in organization roles to match site Added org user admin, org template admin, and org auditor
1 parent 8beb0b1 commit 49d6d0f

File tree

7 files changed

+300
-145
lines changed

7 files changed

+300
-145
lines changed

coderd/authorize_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestCheckPermissions(t *testing.T) {
103103
Client: orgAdminClient,
104104
UserID: orgAdminUser.ID,
105105
Check: map[string]bool{
106-
readAllUsers: false,
106+
readAllUsers: true,
107107
readMyself: true,
108108
readOwnWorkspaces: true,
109109
readOrgWorkspaces: true,

coderd/members_test.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,23 @@ func TestAddMember(t *testing.T) {
3333
// Make a user not in the second organization
3434
_, user := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID)
3535

36-
members, err := owner.OrganizationMembers(ctx, org.ID)
36+
// Use scoped user admin in org to add the user
37+
client, userAdmin := coderdtest.CreateAnotherUser(t, owner, org.ID, rbac.ScopedRoleOrgUserAdmin(org.ID))
38+
39+
members, err := client.OrganizationMembers(ctx, org.ID)
3740
require.NoError(t, err)
38-
require.Len(t, members, 1) // Verify just the 1 member
41+
require.Len(t, members, 2) // Verify the 2 members at the start
3942

4043
// Add user to org
41-
_, err = owner.PostOrganizationMember(ctx, org.ID, user.Username)
44+
_, err = client.PostOrganizationMember(ctx, org.ID, user.Username)
4245
require.NoError(t, err)
4346

44-
members, err = owner.OrganizationMembers(ctx, org.ID)
47+
members, err = client.OrganizationMembers(ctx, org.ID)
4548
require.NoError(t, err)
46-
// Owner + new member
47-
require.Len(t, members, 2)
49+
// Owner + user admin + new member
50+
require.Len(t, members, 3)
4851
require.ElementsMatch(t,
49-
[]uuid.UUID{first.UserID, user.ID},
52+
[]uuid.UUID{first.UserID, user.ID, userAdmin.ID},
5053
db2sdk.List(members, onlyIDs))
5154
})
5255

coderd/rbac/roles.go

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ const (
2727
customSiteRole string = "custom-site-role"
2828
customOrganizationRole string = "custom-organization-role"
2929

30-
orgAdmin string = "organization-admin"
31-
orgMember string = "organization-member"
30+
orgAdmin string = "organization-admin"
31+
orgMember string = "organization-member"
32+
orgAuditor string = "organization-auditor"
33+
orgUserAdmin string = "organization-user-admin"
34+
orgTemplateAdmin string = "organization-template-admin"
3235
)
3336

3437
func init() {
@@ -144,18 +147,38 @@ func RoleOrgMember() string {
144147
return orgMember
145148
}
146149

150+
func RoleOrgAuditor() string {
151+
return orgAuditor
152+
}
153+
154+
func RoleOrgUserAdmin() string {
155+
return orgUserAdmin
156+
}
157+
158+
func RoleOrgTemplateAdmin() string {
159+
return orgTemplateAdmin
160+
}
161+
147162
// ScopedRoleOrgAdmin is the org role with the organization ID
148-
// Deprecated This was used before organization scope was included as a
149-
// field in all user facing APIs. Usage of 'ScopedRoleOrgAdmin()' is preferred.
150163
func ScopedRoleOrgAdmin(organizationID uuid.UUID) RoleIdentifier {
151-
return RoleIdentifier{Name: orgAdmin, OrganizationID: organizationID}
164+
return RoleIdentifier{Name: RoleOrgAdmin(), OrganizationID: organizationID}
152165
}
153166

154167
// ScopedRoleOrgMember is the org role with the organization ID
155-
// Deprecated This was used before organization scope was included as a
156-
// field in all user facing APIs. Usage of 'ScopedRoleOrgMember()' is preferred.
157168
func ScopedRoleOrgMember(organizationID uuid.UUID) RoleIdentifier {
158-
return RoleIdentifier{Name: orgMember, OrganizationID: organizationID}
169+
return RoleIdentifier{Name: RoleOrgMember(), OrganizationID: organizationID}
170+
}
171+
172+
func ScopedRoleOrgAuditor(organizationID uuid.UUID) RoleIdentifier {
173+
return RoleIdentifier{Name: RoleOrgAuditor(), OrganizationID: organizationID}
174+
}
175+
176+
func ScopedRoleOrgUserAdmin(organizationID uuid.UUID) RoleIdentifier {
177+
return RoleIdentifier{Name: RoleOrgUserAdmin(), OrganizationID: organizationID}
178+
}
179+
180+
func ScopedRoleOrgTemplateAdmin(organizationID uuid.UUID) RoleIdentifier {
181+
return RoleIdentifier{Name: RoleOrgTemplateAdmin(), OrganizationID: organizationID}
159182
}
160183

161184
func allPermsExcept(excepts ...Objecter) []Permission {
@@ -365,7 +388,11 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
365388
return Role{
366389
Identifier: RoleIdentifier{Name: orgAdmin, OrganizationID: organizationID},
367390
DisplayName: "Organization Admin",
368-
Site: []Permission{},
391+
Site: Permissions(map[string][]policy.Action{
392+
// To assign organization members, we need to be able to read
393+
// users at the site wide to know they exist.
394+
ResourceUser.Type: {policy.ActionRead},
395+
}),
369396
Org: map[string][]Permission{
370397
// Org admins should not have workspace exec perms.
371398
organizationID.String(): append(allPermsExcept(ResourceWorkspace, ResourceWorkspaceDormant, ResourceAssignRole), Permissions(map[string][]policy.Action{
@@ -377,8 +404,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
377404
}
378405
},
379406

380-
// orgMember has an empty set of permissions, this just implies their membership
381-
// in an organization.
407+
// orgMember is an implied role to any member in an organization.
382408
orgMember: func(organizationID uuid.UUID) Role {
383409
return Role{
384410
Identifier: RoleIdentifier{Name: orgMember, OrganizationID: organizationID},
@@ -406,6 +432,59 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
406432
},
407433
}
408434
},
435+
orgAuditor: func(organizationID uuid.UUID) Role {
436+
return Role{
437+
Identifier: RoleIdentifier{Name: orgAuditor, OrganizationID: organizationID},
438+
DisplayName: "Organization Auditor",
439+
Site: []Permission{},
440+
Org: map[string][]Permission{
441+
organizationID.String(): Permissions(map[string][]policy.Action{
442+
ResourceAuditLog.Type: {policy.ActionRead},
443+
}),
444+
},
445+
User: []Permission{},
446+
}
447+
},
448+
orgUserAdmin: func(organizationID uuid.UUID) Role {
449+
// Manages organization members and groups.
450+
return Role{
451+
Identifier: RoleIdentifier{Name: orgUserAdmin, OrganizationID: organizationID},
452+
DisplayName: "Organization User Admin",
453+
Site: Permissions(map[string][]policy.Action{
454+
// To assign organization members, we need to be able to read
455+
// users at the site wide to know they exist.
456+
ResourceUser.Type: {policy.ActionRead},
457+
}),
458+
Org: map[string][]Permission{
459+
organizationID.String(): Permissions(map[string][]policy.Action{
460+
// Assign, remove, and read roles in the organization.
461+
ResourceAssignOrgRole.Type: {policy.ActionAssign, policy.ActionDelete, policy.ActionRead},
462+
ResourceOrganizationMember.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
463+
ResourceGroup.Type: ResourceGroup.AvailableActions(),
464+
}),
465+
},
466+
User: []Permission{},
467+
}
468+
},
469+
orgTemplateAdmin: func(organizationID uuid.UUID) Role {
470+
// Manages organization members and groups.
471+
return Role{
472+
Identifier: RoleIdentifier{Name: orgTemplateAdmin, OrganizationID: organizationID},
473+
DisplayName: "Organization Template Admin",
474+
Site: []Permission{},
475+
Org: map[string][]Permission{
476+
organizationID.String(): Permissions(map[string][]policy.Action{
477+
ResourceTemplate.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete, policy.ActionViewInsights},
478+
ResourceFile.Type: {policy.ActionCreate, policy.ActionRead},
479+
ResourceWorkspace.Type: {policy.ActionRead},
480+
// Assigning template perms requires this permission.
481+
ResourceOrganizationMember.Type: {policy.ActionRead},
482+
ResourceGroup.Type: {policy.ActionRead},
483+
}),
484+
},
485+
User: []Permission{},
486+
}
487+
},
409488
}
410489
}
411490

@@ -421,6 +500,9 @@ var assignRoles = map[string]map[string]bool{
421500
member: true,
422501
orgAdmin: true,
423502
orgMember: true,
503+
orgAuditor: true,
504+
orgUserAdmin: true,
505+
orgTemplateAdmin: true,
424506
templateAdmin: true,
425507
userAdmin: true,
426508
customSiteRole: true,
@@ -432,6 +514,9 @@ var assignRoles = map[string]map[string]bool{
432514
member: true,
433515
orgAdmin: true,
434516
orgMember: true,
517+
orgAuditor: true,
518+
orgUserAdmin: true,
519+
orgTemplateAdmin: true,
435520
templateAdmin: true,
436521
userAdmin: true,
437522
customSiteRole: true,
@@ -444,8 +529,14 @@ var assignRoles = map[string]map[string]bool{
444529
orgAdmin: {
445530
orgAdmin: true,
446531
orgMember: true,
532+
orgAuditor: true,
533+
orgUserAdmin: true,
534+
orgTemplateAdmin: true,
447535
customOrganizationRole: true,
448536
},
537+
orgUserAdmin: {
538+
orgMember: true,
539+
},
449540
}
450541

451542
// ExpandableRoles is any type that can be expanded into a []Role. This is implemented

0 commit comments

Comments
 (0)