Skip to content

Commit 49d6d0f

Browse files
authored
chore: add built in organization roles to match site (#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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy