Skip to content

Commit 66107d3

Browse files
committed
update roles_test to include all actions and resources
1 parent c70c526 commit 66107d3

File tree

2 files changed

+212
-25
lines changed

2 files changed

+212
-25
lines changed

coderd/rbac/roles.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
159159
// All users can see the provisioner daemons.
160160
ResourceProvisionerDaemon.Type: {policy.ActionRead},
161161
// All users can see OAuth2 provider applications.
162-
ResourceOauth2App.Type: {policy.ActionRead},
162+
ResourceOauth2App.Type: {policy.ActionRead},
163+
ResourceWorkspaceProxy.Type: {policy.ActionRead},
163164
}),
164165
Org: map[string][]Permission{},
165166
User: append(allPermsExcept(ResourceWorkspaceDormant, ResourceUser, ResourceOrganizationMember),

coderd/rbac/roles_test.go

+210-24
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ func TestOwnerExec(t *testing.T) {
3636

3737
auth := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
3838
// Exec a random workspace
39-
err := auth.Authorize(context.Background(), owner, policy.ActionCreate,
40-
rbac.ResourceWorkspaceExecution.WithID(uuid.New()).InOrg(uuid.New()).WithOwner(uuid.NewString()))
39+
err := auth.Authorize(context.Background(), owner, policy.ActionSSH,
40+
rbac.ResourceWorkspace.WithID(uuid.New()).InOrg(uuid.New()).WithOwner(uuid.NewString()))
4141
require.ErrorAsf(t, err, &rbac.UnauthorizedError{}, "expected unauthorized error")
4242
})
4343

@@ -50,8 +50,8 @@ func TestOwnerExec(t *testing.T) {
5050
auth := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
5151

5252
// Exec a random workspace
53-
err := auth.Authorize(context.Background(), owner, policy.ActionCreate,
54-
rbac.ResourceWorkspaceExecution.WithID(uuid.New()).InOrg(uuid.New()).WithOwner(uuid.NewString()))
53+
err := auth.Authorize(context.Background(), owner, policy.ActionSSH,
54+
rbac.ResourceWorkspace.WithID(uuid.New()).InOrg(uuid.New()).WithOwner(uuid.NewString()))
5555
require.NoError(t, err, "expected owner can")
5656
})
5757
}
@@ -60,6 +60,8 @@ func TestOwnerExec(t *testing.T) {
6060
func TestRolePermissions(t *testing.T) {
6161
t.Parallel()
6262

63+
crud := []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete}
64+
6365
auth := rbac.NewCachingAuthorizer(prometheus.NewRegistry())
6466

6567
// currentUser is anything that references "me", "mine", or "my".
@@ -145,8 +147,8 @@ func TestRolePermissions(t *testing.T) {
145147
{
146148
Name: "MyWorkspaceInOrgExecution",
147149
// When creating the WithID won't be set, but it does not change the result.
148-
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
149-
Resource: rbac.ResourceWorkspaceExecution.WithID(workspaceID).InOrg(orgID).WithOwner(currentUser.String()),
150+
Actions: []policy.Action{policy.ActionSSH},
151+
Resource: rbac.ResourceWorkspace.WithID(workspaceID).InOrg(orgID).WithOwner(currentUser.String()),
150152
AuthorizeMap: map[bool][]authSubject{
151153
true: {owner, orgMemberMe},
152154
false: {orgAdmin, memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
@@ -155,16 +157,16 @@ func TestRolePermissions(t *testing.T) {
155157
{
156158
Name: "MyWorkspaceInOrgAppConnect",
157159
// When creating the WithID won't be set, but it does not change the result.
158-
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
159-
Resource: rbac.ResourceWorkspaceApplicationConnect.WithID(workspaceID).InOrg(orgID).WithOwner(currentUser.String()),
160+
Actions: []policy.Action{policy.ActionApplicationConnect},
161+
Resource: rbac.ResourceWorkspace.WithID(workspaceID).InOrg(orgID).WithOwner(currentUser.String()),
160162
AuthorizeMap: map[bool][]authSubject{
161-
true: {owner, orgAdmin, orgMemberMe},
162-
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
163+
true: {owner, orgMemberMe},
164+
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin, orgAdmin},
163165
},
164166
},
165167
{
166168
Name: "Templates",
167-
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
169+
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete, policy.ActionViewInsights},
168170
Resource: rbac.ResourceTemplate.WithID(templateID).InOrg(orgID),
169171
AuthorizeMap: map[bool][]authSubject{
170172
true: {owner, orgAdmin, templateAdmin},
@@ -191,7 +193,7 @@ func TestRolePermissions(t *testing.T) {
191193
},
192194
{
193195
Name: "MyFile",
194-
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
196+
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead},
195197
Resource: rbac.ResourceFile.WithID(fileID).WithOwner(currentUser.String()),
196198
AuthorizeMap: map[bool][]authSubject{
197199
true: {owner, memberMe, orgMemberMe, templateAdmin},
@@ -227,8 +229,8 @@ func TestRolePermissions(t *testing.T) {
227229
},
228230
{
229231
Name: "RoleAssignment",
230-
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
231-
Resource: rbac.ResourceRoleAssignment,
232+
Actions: []policy.Action{policy.ActionAssign, policy.ActionDelete},
233+
Resource: rbac.ResourceAssignRole,
232234
AuthorizeMap: map[bool][]authSubject{
233235
true: {owner, userAdmin},
234236
false: {orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin},
@@ -237,16 +239,16 @@ func TestRolePermissions(t *testing.T) {
237239
{
238240
Name: "ReadRoleAssignment",
239241
Actions: []policy.Action{policy.ActionRead},
240-
Resource: rbac.ResourceRoleAssignment,
242+
Resource: rbac.ResourceAssignRole,
241243
AuthorizeMap: map[bool][]authSubject{
242244
true: {owner, orgAdmin, orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
243245
false: {},
244246
},
245247
},
246248
{
247249
Name: "OrgRoleAssignment",
248-
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
249-
Resource: rbac.ResourceOrgRoleAssignment.InOrg(orgID),
250+
Actions: []policy.Action{policy.ActionAssign, policy.ActionDelete},
251+
Resource: rbac.ResourceAssignOrgRole.InOrg(orgID),
250252
AuthorizeMap: map[bool][]authSubject{
251253
true: {owner, orgAdmin},
252254
false: {orgMemberMe, otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
@@ -255,7 +257,7 @@ func TestRolePermissions(t *testing.T) {
255257
{
256258
Name: "ReadOrgRoleAssignment",
257259
Actions: []policy.Action{policy.ActionRead},
258-
Resource: rbac.ResourceOrgRoleAssignment.InOrg(orgID),
260+
Resource: rbac.ResourceAssignOrgRole.InOrg(orgID),
259261
AuthorizeMap: map[bool][]authSubject{
260262
true: {owner, orgAdmin, orgMemberMe},
261263
false: {otherOrgAdmin, otherOrgMember, memberMe, templateAdmin, userAdmin},
@@ -264,16 +266,16 @@ func TestRolePermissions(t *testing.T) {
264266
{
265267
Name: "APIKey",
266268
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
267-
Resource: rbac.ResourceAPIKey.WithID(apiKeyID).WithOwner(currentUser.String()),
269+
Resource: rbac.ResourceApiKey.WithID(apiKeyID).WithOwner(currentUser.String()),
268270
AuthorizeMap: map[bool][]authSubject{
269271
true: {owner, orgMemberMe, memberMe},
270272
false: {orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
271273
},
272274
},
273275
{
274276
Name: "UserData",
275-
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
276-
Resource: rbac.ResourceUserData.WithID(currentUser).WithOwner(currentUser.String()),
277+
Actions: []policy.Action{policy.ActionReadPersonal, policy.ActionUpdatePersonal},
278+
Resource: rbac.ResourceUserObject(currentUser),
277279
AuthorizeMap: map[bool][]authSubject{
278280
true: {owner, orgMemberMe, memberMe, userAdmin},
279281
false: {orgAdmin, otherOrgAdmin, otherOrgMember, templateAdmin},
@@ -312,6 +314,15 @@ func TestRolePermissions(t *testing.T) {
312314
},
313315
{
314316
Name: "Groups",
317+
Actions: []policy.Action{policy.ActionCreate, policy.ActionDelete, policy.ActionUpdate},
318+
Resource: rbac.ResourceGroup.WithID(groupID).InOrg(orgID),
319+
AuthorizeMap: map[bool][]authSubject{
320+
true: {owner, orgAdmin, userAdmin},
321+
false: {memberMe, otherOrgAdmin, orgMemberMe, otherOrgMember, templateAdmin},
322+
},
323+
},
324+
{
325+
Name: "GroupsRead",
315326
Actions: []policy.Action{policy.ActionRead},
316327
Resource: rbac.ResourceGroup.WithID(groupID).InOrg(orgID),
317328
AuthorizeMap: map[bool][]authSubject{
@@ -330,19 +341,183 @@ func TestRolePermissions(t *testing.T) {
330341
},
331342
{
332343
Name: "WorkspaceBuild",
333-
Actions: rbac.AllActions(),
334-
Resource: rbac.ResourceWorkspaceBuild.WithID(uuid.New()).InOrg(orgID).WithOwner(memberMe.Actor.ID),
344+
Actions: []policy.Action{policy.ActionWorkspaceBuild},
345+
Resource: rbac.ResourceWorkspace.WithID(uuid.New()).InOrg(orgID).WithOwner(memberMe.Actor.ID),
335346
AuthorizeMap: map[bool][]authSubject{
336347
true: {owner, orgAdmin, orgMemberMe},
337348
false: {userAdmin, otherOrgAdmin, otherOrgMember, templateAdmin, memberMe},
338349
},
339350
},
351+
// Some admin style resources
352+
{
353+
Name: "Licences",
354+
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionDelete},
355+
Resource: rbac.ResourceLicense,
356+
AuthorizeMap: map[bool][]authSubject{
357+
true: {owner},
358+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
359+
},
360+
},
361+
{
362+
Name: "DeploymentStats",
363+
Actions: []policy.Action{policy.ActionRead},
364+
Resource: rbac.ResourceDeploymentStats,
365+
AuthorizeMap: map[bool][]authSubject{
366+
true: {owner},
367+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
368+
},
369+
},
370+
{
371+
Name: "DeploymentConfig",
372+
Actions: []policy.Action{policy.ActionRead},
373+
Resource: rbac.ResourceDeploymentConfig,
374+
AuthorizeMap: map[bool][]authSubject{
375+
true: {owner},
376+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
377+
},
378+
},
379+
{
380+
Name: "DebugInfo",
381+
Actions: []policy.Action{policy.ActionUse},
382+
Resource: rbac.ResourceDebugInfo,
383+
AuthorizeMap: map[bool][]authSubject{
384+
true: {owner},
385+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
386+
},
387+
},
388+
{
389+
Name: "Replicas",
390+
Actions: []policy.Action{policy.ActionRead},
391+
Resource: rbac.ResourceReplicas,
392+
AuthorizeMap: map[bool][]authSubject{
393+
true: {owner},
394+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
395+
},
396+
},
397+
{
398+
Name: "TailnetCoordinator",
399+
Actions: crud,
400+
Resource: rbac.ResourceTailnetCoordinator,
401+
AuthorizeMap: map[bool][]authSubject{
402+
true: {owner},
403+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
404+
},
405+
},
406+
{
407+
Name: "AuditLogs",
408+
Actions: []policy.Action{policy.ActionRead},
409+
Resource: rbac.ResourceAuditLog,
410+
AuthorizeMap: map[bool][]authSubject{
411+
true: {owner},
412+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
413+
},
414+
},
415+
{
416+
Name: "ProvisionerDaemons",
417+
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
418+
Resource: rbac.ResourceProvisionerDaemon.InOrg(orgID),
419+
AuthorizeMap: map[bool][]authSubject{
420+
true: {owner, templateAdmin, orgAdmin},
421+
false: {otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, userAdmin},
422+
},
423+
},
424+
{
425+
Name: "ProvisionerDaemonsRead",
426+
Actions: []policy.Action{policy.ActionRead},
427+
Resource: rbac.ResourceProvisionerDaemon.InOrg(orgID),
428+
AuthorizeMap: map[bool][]authSubject{
429+
// This should be fixed when multi-org goes live
430+
true: {owner, templateAdmin, orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, userAdmin},
431+
false: {},
432+
},
433+
},
434+
{
435+
Name: "UserProvisionerDaemons",
436+
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
437+
Resource: rbac.ResourceProvisionerDaemon.WithOwner(currentUser.String()).InOrg(orgID),
438+
AuthorizeMap: map[bool][]authSubject{
439+
true: {owner, templateAdmin, orgMemberMe, orgAdmin},
440+
false: {memberMe, otherOrgAdmin, otherOrgMember, userAdmin},
441+
},
442+
},
443+
{
444+
Name: "System",
445+
Actions: crud,
446+
Resource: rbac.ResourceSystem,
447+
AuthorizeMap: map[bool][]authSubject{
448+
true: {owner},
449+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
450+
},
451+
},
452+
{
453+
Name: "Oauth2App",
454+
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
455+
Resource: rbac.ResourceOauth2App,
456+
AuthorizeMap: map[bool][]authSubject{
457+
true: {owner},
458+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
459+
},
460+
},
461+
{
462+
Name: "Oauth2AppRead",
463+
Actions: []policy.Action{policy.ActionRead},
464+
Resource: rbac.ResourceOauth2App,
465+
AuthorizeMap: map[bool][]authSubject{
466+
true: {owner, orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
467+
false: {},
468+
},
469+
},
470+
{
471+
Name: "Oauth2AppSecret",
472+
Actions: crud,
473+
Resource: rbac.ResourceOauth2AppSecret,
474+
AuthorizeMap: map[bool][]authSubject{
475+
true: {owner},
476+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
477+
},
478+
},
479+
{
480+
Name: "Oauth2Token",
481+
Actions: crud,
482+
Resource: rbac.ResourceOauth2AppCodeToken,
483+
AuthorizeMap: map[bool][]authSubject{
484+
true: {owner},
485+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
486+
},
487+
},
488+
{
489+
Name: "WorkspaceProxy",
490+
Actions: []policy.Action{policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
491+
Resource: rbac.ResourceWorkspaceProxy,
492+
AuthorizeMap: map[bool][]authSubject{
493+
true: {owner},
494+
false: {orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
495+
},
496+
},
497+
{
498+
Name: "WorkspaceProxyRead",
499+
Actions: []policy.Action{policy.ActionRead},
500+
Resource: rbac.ResourceWorkspaceProxy,
501+
AuthorizeMap: map[bool][]authSubject{
502+
true: {owner, orgAdmin, otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe, templateAdmin, userAdmin},
503+
false: {},
504+
},
505+
},
506+
}
507+
508+
// We expect every permission to be tested above.
509+
remainingPermissions := make(map[string]map[policy.Action]bool)
510+
for rtype, perms := range policy.RBACPermissions {
511+
remainingPermissions[rtype] = make(map[policy.Action]bool)
512+
for action := range perms.Actions {
513+
remainingPermissions[rtype][action] = true
514+
}
340515
}
341516

342517
for _, c := range testCases {
343518
c := c
519+
// nolint:tparallel -- These share the same remainingPermissions map
344520
t.Run(c.Name, func(t *testing.T) {
345-
t.Parallel()
346521
remainingSubjs := make(map[string]struct{})
347522
for _, subj := range requiredSubjects {
348523
remainingSubjs[subj.Name] = struct{}{}
@@ -359,6 +534,8 @@ func TestRolePermissions(t *testing.T) {
359534
if actor.Scope == nil {
360535
actor.Scope = rbac.ScopeAll
361536
}
537+
538+
delete(remainingPermissions[c.Resource.Type], action)
362539
err := auth.Authorize(context.Background(), actor, action, c.Resource)
363540
if result {
364541
assert.NoError(t, err, fmt.Sprintf("Should pass: %s", msg))
@@ -371,6 +548,15 @@ func TestRolePermissions(t *testing.T) {
371548
require.Empty(t, remainingSubjs, "test should cover all subjects")
372549
})
373550
}
551+
552+
for rtype, v := range remainingPermissions {
553+
// nolint:tparallel -- Making a subtest for easier diagnosing failures.
554+
t.Run(fmt.Sprintf("%s-AllActions", rtype), func(t *testing.T) {
555+
if len(v) > 0 {
556+
assert.Equal(t, map[policy.Action]bool{}, v, "remaining permissions should be empty for type %q", rtype)
557+
}
558+
})
559+
}
374560
}
375561

376562
func TestIsOrgRole(t *testing.T) {

0 commit comments

Comments
 (0)