Skip to content

Commit 194be12

Browse files
authored
chore: verify validity of built in rbac roles (#13296)
Verifies our built in roles are valid according to our policy.go. Working on custom roles requires the dynamic roles to adhere to these rules. Feels fair the built in ones do too.
1 parent a0fce36 commit 194be12

File tree

4 files changed

+84
-9
lines changed

4 files changed

+84
-9
lines changed

coderd/rbac/object.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package rbac
22

33
import (
4-
"fmt"
5-
64
"github.com/google/uuid"
5+
"golang.org/x/xerrors"
76

87
"github.com/coder/coder/v2/coderd/rbac/policy"
98
)
@@ -36,10 +35,10 @@ type Object struct {
3635
func (z Object) ValidAction(action policy.Action) error {
3736
perms, ok := policy.RBACPermissions[z.Type]
3837
if !ok {
39-
return fmt.Errorf("invalid type %q", z.Type)
38+
return xerrors.Errorf("invalid type %q", z.Type)
4039
}
4140
if _, ok := perms.Actions[action]; !ok {
42-
return fmt.Errorf("invalid action %q for type %q", action, z.Type)
41+
return xerrors.Errorf("invalid action %q for type %q", action, z.Type)
4342
}
4443

4544
return nil

coderd/rbac/roles.go

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package rbac
22

33
import (
4+
"errors"
45
"sort"
56
"strings"
67

@@ -369,6 +370,30 @@ type Permission struct {
369370
Action policy.Action `json:"action"`
370371
}
371372

373+
func (perm Permission) Valid() error {
374+
if perm.ResourceType == policy.WildcardSymbol {
375+
// Wildcard is tricky to check. Just allow it.
376+
return nil
377+
}
378+
379+
resource, ok := policy.RBACPermissions[perm.ResourceType]
380+
if !ok {
381+
return xerrors.Errorf("invalid resource type %q", perm.ResourceType)
382+
}
383+
384+
// Wildcard action is always valid
385+
if perm.Action == policy.WildcardSymbol {
386+
return nil
387+
}
388+
389+
_, ok = resource.Actions[perm.Action]
390+
if !ok {
391+
return xerrors.Errorf("invalid action %q for resource %q", perm.Action, perm.ResourceType)
392+
}
393+
394+
return nil
395+
}
396+
372397
// Role is a set of permissions at multiple levels:
373398
// - Site level permissions apply EVERYWHERE
374399
// - Org level permissions apply to EVERYTHING in a given ORG
@@ -393,6 +418,34 @@ type Role struct {
393418
cachedRegoValue ast.Value
394419
}
395420

421+
// Valid will check all it's permissions and ensure they are all correct
422+
// according to the policy. This verifies every action specified make sense
423+
// for the given resource.
424+
func (role Role) Valid() error {
425+
var errs []error
426+
for _, perm := range role.Site {
427+
if err := perm.Valid(); err != nil {
428+
errs = append(errs, xerrors.Errorf("site: %w", err))
429+
}
430+
}
431+
432+
for orgID, permissions := range role.Org {
433+
for _, perm := range permissions {
434+
if err := perm.Valid(); err != nil {
435+
errs = append(errs, xerrors.Errorf("org=%q: %w", orgID, err))
436+
}
437+
}
438+
}
439+
440+
for _, perm := range role.User {
441+
if err := perm.Valid(); err != nil {
442+
errs = append(errs, xerrors.Errorf("user: %w", err))
443+
}
444+
}
445+
446+
return errors.Join(errs...)
447+
}
448+
396449
type Roles []Role
397450

398451
func (roles Roles) Expand() ([]Role, error) {
@@ -402,7 +455,7 @@ func (roles Roles) Expand() ([]Role, error) {
402455
func (roles Roles) Names() []string {
403456
names := make([]string, 0, len(roles))
404457
for _, r := range roles {
405-
return append(names, r.Name)
458+
names = append(names, r.Name)
406459
}
407460
return names
408461
}

coderd/rbac/roles_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ type authSubject struct {
2020
Actor rbac.Subject
2121
}
2222

23+
// TestBuiltInRoles makes sure our built-in roles are valid by our own policy
24+
// rules. If this is incorrect, that is a mistake.
25+
func TestBuiltInRoles(t *testing.T) {
26+
t.Parallel()
27+
for _, r := range rbac.SiteRoles() {
28+
r := r
29+
t.Run(r.Name, func(t *testing.T) {
30+
t.Parallel()
31+
require.NoError(t, r.Valid(), "invalid role")
32+
})
33+
}
34+
35+
for _, r := range rbac.OrganizationRoles(uuid.New()) {
36+
r := r
37+
t.Run(r.Name, func(t *testing.T) {
38+
t.Parallel()
39+
require.NoError(t, r.Valid(), "invalid role")
40+
})
41+
}
42+
}
43+
2344
//nolint:tparallel,paralleltest
2445
func TestOwnerExec(t *testing.T) {
2546
owner := rbac.Subject{

scripts/rbacgen/main.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"slices"
1717
"strings"
1818

19+
"golang.org/x/xerrors"
20+
1921
"github.com/coder/coder/v2/coderd/rbac/policy"
2022
)
2123

@@ -148,7 +150,7 @@ func generateRbacObjects(templateSource string) ([]byte, error) {
148150
// Parse the policy.go file for the action enums
149151
f, err := parser.ParseFile(token.NewFileSet(), "./coderd/rbac/policy/policy.go", nil, parser.ParseComments)
150152
if err != nil {
151-
return nil, fmt.Errorf("parsing policy.go: %w", err)
153+
return nil, xerrors.Errorf("parsing policy.go: %w", err)
152154
}
153155
actionMap := fileActions(f)
154156
actionList := make([]ActionDetails, 0)
@@ -176,14 +178,14 @@ func generateRbacObjects(templateSource string) ([]byte, error) {
176178
x++
177179
v, ok := actionMap[string(action)]
178180
if !ok {
179-
errorList = append(errorList, fmt.Errorf("action value %q does not have a constant a matching enum constant", action))
181+
errorList = append(errorList, xerrors.Errorf("action value %q does not have a constant a matching enum constant", action))
180182
}
181183
return v
182184
},
183185
"concat": func(strs ...string) string { return strings.Join(strs, "") },
184186
}).Parse(templateSource)
185187
if err != nil {
186-
return nil, fmt.Errorf("parse template: %w", err)
188+
return nil, xerrors.Errorf("parse template: %w", err)
187189
}
188190

189191
// Convert to sorted list for autogen consistency.
@@ -203,7 +205,7 @@ func generateRbacObjects(templateSource string) ([]byte, error) {
203205

204206
err = tpl.Execute(&out, list)
205207
if err != nil {
206-
return nil, fmt.Errorf("execute template: %w", err)
208+
return nil, xerrors.Errorf("execute template: %w", err)
207209
}
208210

209211
if len(errorList) > 0 {

0 commit comments

Comments
 (0)