Skip to content

Commit 21f2cee

Browse files
committed
thread safe role cache'
1 parent aa85df1 commit 21f2cee

File tree

3 files changed

+17
-20
lines changed

3 files changed

+17
-20
lines changed

coderd/database/migrations/000209_custom_roles.up.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
CREATE TABLE custom_roles (
2-
-- name is globally unique. Org scoped roles have their orgid prepended
2+
-- name is globally unique. Org scoped roles have their orgid appended
33
-- like: "name":"organization-admin:bbe8c156-c61e-4d36-b91e-697c6b1477e8"
44
name text primary key,
55
-- display_name is the actual name of the role displayed to the user.

coderd/rbac/roles.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ type Permission struct {
380380
}
381381

382382
func (perm Permission) Valid() error {
383+
if perm.ResourceType == policy.WildcardSymbol {
384+
// Wildcard is tricky to check. Just allow it.
385+
return nil
386+
}
387+
383388
resource, ok := policy.RBACPermissions[perm.ResourceType]
384389
if !ok {
385390
return fmt.Errorf("invalid resource type %q", perm.ResourceType)

coderd/rbac/rolestore/rolestore.go

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99

1010
"github.com/coder/coder/v2/coderd/database"
1111
"github.com/coder/coder/v2/coderd/rbac"
12+
"github.com/coder/coder/v2/coderd/util/syncmap"
1213
)
1314

14-
type (
15-
customRoleCtxKey struct{}
16-
customRoleCache map[string]rbac.Role
17-
)
15+
type customRoleCtxKey struct{}
1816

17+
// CustomRoleMW adds a custom role cache on the ctx to prevent duplicate
18+
// db fetches.
1919
func CustomRoleMW(next http.Handler) http.Handler {
2020
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2121
r = r.WithContext(CustomRoleCacheContext(r.Context()))
@@ -27,33 +27,25 @@ func CustomRoleMW(next http.Handler) http.Handler {
2727
// same request lifecycle. Optimizing this to span requests should be done
2828
// in the future.
2929
func CustomRoleCacheContext(ctx context.Context) context.Context {
30-
return context.WithValue(ctx, customRoleCtxKey{}, customRoleCache{})
30+
return context.WithValue(ctx, customRoleCtxKey{}, syncmap.New[string, rbac.Role]())
3131
}
3232

33-
func roleCache(ctx context.Context) customRoleCache {
34-
c, ok := ctx.Value(customRoleCtxKey{}).(customRoleCache)
33+
func roleCache(ctx context.Context) *syncmap.Map[string, rbac.Role] {
34+
c, ok := ctx.Value(customRoleCtxKey{}).(*syncmap.Map[string, rbac.Role])
3535
if !ok {
36-
return customRoleCache{}
36+
return syncmap.New[string, rbac.Role]()
3737
}
3838
return c
3939
}
4040

41-
func store(ctx context.Context, name string, role rbac.Role) {
42-
roleCache(ctx)[name] = role
43-
}
44-
45-
func load(ctx context.Context, name string) (rbac.Role, bool) {
46-
r, ok := roleCache(ctx)[name]
47-
return r, ok
48-
}
49-
5041
// Expand will expand built in roles, and fetch custom roles from the database.
5142
func Expand(ctx context.Context, db database.Store, names []string) (rbac.Roles, error) {
5243
if len(names) == 0 {
5344
// That was easy
5445
return []rbac.Role{}, nil
5546
}
5647

48+
cache := roleCache(ctx)
5749
lookup := make([]string, 0)
5850
roles := make([]rbac.Role, 0, len(names))
5951

@@ -66,7 +58,7 @@ func Expand(ctx context.Context, db database.Store, names []string) (rbac.Roles,
6658
}
6759

6860
// Check custom role cache
69-
customRole, ok := load(ctx, name)
61+
customRole, ok := cache.Load(name)
7062
if ok {
7163
roles = append(roles, customRole)
7264
continue
@@ -92,7 +84,7 @@ func Expand(ctx context.Context, db database.Store, names []string) (rbac.Roles,
9284
return nil, xerrors.Errorf("convert db role %q: %w", dbrole, err)
9385
}
9486
roles = append(roles, converted)
95-
store(ctx, dbrole.Name, converted)
87+
cache.Store(dbrole.Name, converted)
9688
}
9789
}
9890

0 commit comments

Comments
 (0)