Skip to content

Commit 851833b

Browse files
committed
added mutex guards around each call of the casbin context enforcer
1 parent f08580b commit 851833b

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

accesscontrol/casbin_rbac.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"log/slog"
2222
"os"
2323
"strings"
24+
"sync"
2425

2526
"github.com/casbin/casbin/v3"
2627
gormadapter "github.com/casbin/gorm-adapter/v3"
@@ -34,6 +35,10 @@ import (
3435
var _ shared.AccessControl = &casbinRBAC{}
3536
var casbinEnforcer *casbin.ContextEnforcer
3637

38+
// protect against concurrent access on shared rbac structures like maps
39+
// in practical terms this means that whenever we call a function of the casbin context enforcer, we wrap the call inside a mutex lock and unlock
40+
var concurrencyMutex sync.RWMutex
41+
3742
type casbinRBAC struct {
3843
domain string // scopes this to a specific domain - or organization
3944
enforcer *casbin.ContextEnforcer
@@ -55,7 +60,9 @@ func (c *casbinRBAC) GetExternalEntityProviderID() *string {
5560
}
5661

5762
func (c *casbinRBAC) GetOwnerOfOrganization() (string, error) {
63+
concurrencyMutex.RLock()
5864
listOfUsers := c.enforcer.GetUsersForRoleInDomain("role::owner", "domain::"+c.domain)
65+
concurrencyMutex.RUnlock()
5966
if len(listOfUsers) == 0 {
6067
return "", fmt.Errorf("no owner found for organization")
6168
}
@@ -66,7 +73,9 @@ func (c *casbinRBAC) GetOwnerOfOrganization() (string, error) {
6673
}
6774

6875
func (c *casbinRBAC) GetAllMembersOfOrganization() ([]string, error) {
76+
concurrencyMutex.RLock()
6977
users, err := c.enforcer.GetAllUsersByDomain("domain::" + c.domain)
78+
concurrencyMutex.RUnlock()
7079
if err != nil {
7180
return nil, err
7281
}
@@ -78,7 +87,9 @@ func (c *casbinRBAC) GetAllMembersOfOrganization() ([]string, error) {
7887
}
7988

8089
func (c *casbinRBAC) GetAllMembersOfProject(projectID string) ([]string, error) {
90+
concurrencyMutex.RLock()
8191
users, err := c.enforcer.GetImplicitUsersForRole("project::"+projectID+"|role::member", "domain::"+c.domain)
92+
concurrencyMutex.RUnlock()
8293
if err != nil {
8394
return nil, err
8495
}
@@ -90,7 +101,9 @@ func (c *casbinRBAC) GetAllMembersOfProject(projectID string) ([]string, error)
90101
}
91102

92103
func (c *casbinRBAC) GetAllMembersOfAsset(assetID string) ([]string, error) {
104+
concurrencyMutex.RLock()
93105
users, err := c.enforcer.GetImplicitUsersForRole("asset::"+assetID+"|role::member", "domain::"+c.domain)
106+
concurrencyMutex.RUnlock()
94107
if err != nil {
95108
return nil, err
96109
}
@@ -102,13 +115,17 @@ func (c *casbinRBAC) GetAllMembersOfAsset(assetID string) ([]string, error) {
102115
}
103116

104117
func (c *casbinRBAC) HasAccess(ctx context.Context, user string) (bool, error) {
118+
concurrencyMutex.RLock()
105119
roles := c.enforcer.GetRolesForUserInDomain("user::"+user, "domain::"+c.domain)
120+
concurrencyMutex.RUnlock()
106121
return len(roles) > 0, nil
107122
}
108123

109124
func (c *casbinRBAC) GetAllProjectsForUser(user string) ([]string, error) {
110125
projectIDs := []string{}
126+
concurrencyMutex.RLock()
111127
roles, _ := c.enforcer.GetImplicitRolesForUser("user::"+user, "domain::"+c.domain)
128+
concurrencyMutex.RUnlock()
112129
for _, role := range roles {
113130
if !strings.HasPrefix(role, "project::") || !strings.Contains(role, "role::") {
114131
continue
@@ -120,7 +137,9 @@ func (c *casbinRBAC) GetAllProjectsForUser(user string) ([]string, error) {
120137

121138
func (c *casbinRBAC) GetAllAssetsForUser(user string) ([]string, error) {
122139
assetIDs := []string{}
140+
concurrencyMutex.RLock()
123141
roles, _ := c.enforcer.GetImplicitRolesForUser("user::"+user, "domain::"+c.domain)
142+
concurrencyMutex.RUnlock()
124143
for _, role := range roles {
125144
if !strings.HasPrefix(role, "asset::") || !strings.Contains(role, "role::") {
126145
continue
@@ -131,7 +150,9 @@ func (c *casbinRBAC) GetAllAssetsForUser(user string) ([]string, error) {
131150
}
132151

133152
func (c *casbinRBAC) GetAllRoles(user string) []string {
153+
concurrencyMutex.RLock()
134154
roles, err := c.enforcer.GetImplicitRolesForUser("user::"+user, "domain::"+c.domain)
155+
concurrencyMutex.RUnlock()
135156
if err != nil {
136157
slog.Error("GetAllRoles failed", "err", err)
137158
return []string{}
@@ -204,31 +225,43 @@ func (c *casbinRBAC) getAssetRoleName(role shared.Role, asset string) string {
204225
}
205226

206227
func (c *casbinRBAC) GrantRole(ctx context.Context, user string, role shared.Role) error {
228+
concurrencyMutex.Lock()
229+
defer concurrencyMutex.Unlock()
207230
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, "user::"+user, "role::"+string(role), "domain::"+c.domain)
208231
return err
209232
}
210233

211234
func (c *casbinRBAC) RevokeRole(ctx context.Context, user string, role shared.Role) error {
235+
concurrencyMutex.Lock()
236+
defer concurrencyMutex.Unlock()
212237
_, err := c.enforcer.DeleteRoleForUserInDomainCtx(ctx, "user::"+user, "role::"+string(role), "domain::"+c.domain)
213238
return err
214239
}
215240

216241
func (c *casbinRBAC) GrantRoleInProject(ctx context.Context, user string, role shared.Role, project string) error {
242+
concurrencyMutex.Lock()
243+
defer concurrencyMutex.Unlock()
217244
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, "user::"+user, "project::"+project+"|role::"+string(role), "domain::"+c.domain)
218245
return err
219246
}
220247

221248
func (c *casbinRBAC) GrantRoleInAsset(ctx context.Context, user string, role shared.Role, asset string) error {
249+
concurrencyMutex.Lock()
250+
defer concurrencyMutex.Unlock()
222251
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, "user::"+user, "asset::"+asset+"|role::"+string(role), "domain::"+c.domain)
223252
return err
224253
}
225254

226255
func (c *casbinRBAC) RevokeRoleInProject(ctx context.Context, user string, role shared.Role, project string) error {
256+
concurrencyMutex.Lock()
257+
defer concurrencyMutex.Unlock()
227258
_, err := c.enforcer.DeleteRoleForUserInDomainCtx(ctx, "user::"+user, "project::"+project+"|role::"+string(role), "domain::"+c.domain)
228259
return err
229260
}
230261

231262
func (c *casbinRBAC) RevokeRoleInAsset(ctx context.Context, user string, role shared.Role, project string) error {
263+
concurrencyMutex.Lock()
264+
defer concurrencyMutex.Unlock()
232265
_, err := c.enforcer.DeleteRoleForUserInDomainCtx(ctx, "user::"+user, "asset::"+project+"|role::"+string(role), "domain::"+c.domain)
233266
return err
234267
}
@@ -252,31 +285,43 @@ func (c *casbinRBAC) RevokeAllRolesInAssetForUser(ctx context.Context, user stri
252285
}
253286

254287
func (c *casbinRBAC) InheritRole(ctx context.Context, roleWhichGetsPermissions, roleWhichProvidesPermissions shared.Role) error {
288+
concurrencyMutex.Lock()
289+
defer concurrencyMutex.Unlock()
255290
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, "role::"+string(roleWhichGetsPermissions), "role::"+string(roleWhichProvidesPermissions), "domain::"+c.domain)
256291
return err
257292
}
258293

259294
func (c *casbinRBAC) InheritProjectRole(ctx context.Context, roleWhichGetsPermissions, roleWhichProvidesPermissions shared.Role, project string) error {
295+
concurrencyMutex.Lock()
296+
defer concurrencyMutex.Unlock()
260297
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, c.getProjectRoleName(roleWhichGetsPermissions, project), c.getProjectRoleName(roleWhichProvidesPermissions, project), "domain::"+c.domain)
261298
return err
262299
}
263300

264301
func (c *casbinRBAC) InheritAssetRole(ctx context.Context, roleWhichGetsPermissions, roleWhichProvidesPermissions shared.Role, asset string) error {
302+
concurrencyMutex.Lock()
303+
defer concurrencyMutex.Unlock()
265304
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, c.getAssetRoleName(roleWhichGetsPermissions, asset), c.getAssetRoleName(roleWhichProvidesPermissions, asset), "domain::"+c.domain)
266305
return err
267306
}
268307

269308
func (c *casbinRBAC) InheritProjectRolesAcrossProjects(ctx context.Context, roleWhichGetsPermissions, roleWhichProvidesPermissions shared.ProjectRole) error {
309+
concurrencyMutex.Lock()
310+
defer concurrencyMutex.Unlock()
270311
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, c.getProjectRoleName(roleWhichGetsPermissions.Role, roleWhichGetsPermissions.Project), c.getProjectRoleName(roleWhichProvidesPermissions.Role, roleWhichProvidesPermissions.Project), "domain::"+c.domain)
271312
return err
272313
}
273314

274315
func (c *casbinRBAC) LinkDomainAndProjectRole(ctx context.Context, domainRoleWhichGetsPermission, projectRoleWhichProvidesPermissions shared.Role, project string) error {
316+
concurrencyMutex.Lock()
317+
defer concurrencyMutex.Unlock()
275318
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, "role::"+string(domainRoleWhichGetsPermission), c.getProjectRoleName(projectRoleWhichProvidesPermissions, project), "domain::"+c.domain)
276319
return err
277320
}
278321

279322
func (c *casbinRBAC) LinkProjectAndAssetRole(ctx context.Context, projectRoleWhichGetsPermission, assetRoleWhichProvidesPermissions shared.Role, project string, asset string) error {
323+
concurrencyMutex.Lock()
324+
defer concurrencyMutex.Unlock()
280325
_, err := c.enforcer.AddRoleForUserInDomainCtx(ctx, c.getProjectRoleName(projectRoleWhichGetsPermission, project), c.getAssetRoleName(assetRoleWhichProvidesPermissions, asset), "domain::"+c.domain)
281326
return err
282327
}
@@ -286,6 +331,8 @@ func (c *casbinRBAC) AllowRole(ctx context.Context, role shared.Role, object sha
286331
for i, ac := range action {
287332
policies[i] = []string{"role::" + string(role), "domain::" + c.domain, "obj::" + string(object), "act::" + string(ac)}
288333
}
334+
concurrencyMutex.Lock()
335+
defer concurrencyMutex.Unlock()
289336
_, err := c.enforcer.AddPoliciesCtx(ctx, policies)
290337
return err
291338
}
@@ -295,6 +342,8 @@ func (c *casbinRBAC) AllowRoleInProject(ctx context.Context, project string, rol
295342
for i, ac := range action {
296343
policies[i] = []string{"project::" + project + "|role::" + string(role), "domain::" + c.domain, "project::" + project + "|obj::" + string(object), "act::" + string(ac)}
297344
}
345+
concurrencyMutex.Lock()
346+
defer concurrencyMutex.Unlock()
298347
_, err := c.enforcer.AddPoliciesCtx(ctx, policies)
299348
return err
300349
}
@@ -304,12 +353,16 @@ func (c *casbinRBAC) AllowRoleInAsset(ctx context.Context, asset string, role sh
304353
for i, ac := range action {
305354
policies[i] = []string{"asset::" + asset + "|role::" + string(role), "domain::" + c.domain, "asset::" + asset + "|obj::" + string(object), "act::" + string(ac)}
306355
}
356+
concurrencyMutex.Lock()
357+
defer concurrencyMutex.Unlock()
307358
_, err := c.enforcer.AddPoliciesCtx(ctx, policies)
308359
return err
309360
}
310361

311362
func (c *casbinRBAC) IsAllowed(ctx context.Context, user string, object shared.Object, action shared.Action) (bool, error) {
363+
concurrencyMutex.RLock()
312364
permissions, err := c.enforcer.GetImplicitPermissionsForUser("user::"+user, "domain::"+c.domain)
365+
concurrencyMutex.RUnlock()
313366
if err != nil {
314367
return false, err
315368
}
@@ -322,7 +375,9 @@ func (c *casbinRBAC) IsAllowed(ctx context.Context, user string, object shared.O
322375
}
323376

324377
func (c *casbinRBAC) IsAllowedInProject(ctx context.Context, project *models.Project, user string, object shared.Object, action shared.Action) (bool, error) {
378+
concurrencyMutex.RLock()
325379
permissions, err := c.enforcer.GetImplicitPermissionsForUser("user::"+user, "domain::"+c.domain)
380+
concurrencyMutex.RUnlock()
326381
if err != nil {
327382
return false, err
328383
}
@@ -336,7 +391,9 @@ func (c *casbinRBAC) IsAllowedInProject(ctx context.Context, project *models.Pro
336391
}
337392

338393
func (c *casbinRBAC) IsAllowedInAsset(ctx context.Context, asset *models.Asset, user string, object shared.Object, action shared.Action) (bool, error) {
394+
concurrencyMutex.RLock()
339395
permissions, err := c.enforcer.GetImplicitPermissionsForUser("user::"+user, "domain::"+c.domain)
396+
concurrencyMutex.RUnlock()
340397
if err != nil {
341398
return false, err
342399
}
@@ -350,7 +407,9 @@ func (c *casbinRBAC) IsAllowedInAsset(ctx context.Context, asset *models.Asset,
350407
}
351408

352409
func (c casbinRBACProvider) DomainsOfUser(user string) ([]string, error) {
410+
concurrencyMutex.RLock()
353411
domains, err := c.enforcer.GetDomainsForUser("user::" + user)
412+
concurrencyMutex.RUnlock()
354413
if err != nil {
355414
return nil, err
356415
}
@@ -396,6 +455,8 @@ func buildEnforcer(db *gorm.DB, broker shared.PubSubBroker) (*casbin.ContextEnfo
396455
return nil, fmt.Errorf("could not set watcher: %w", err)
397456
}
398457
err = watcher.SetUpdateCallback(func(string) {
458+
concurrencyMutex.Lock()
459+
defer concurrencyMutex.Unlock()
399460
err := e.LoadPolicy()
400461
if err != nil {
401462
slog.Error("error while loading policy after update", "err", err)

0 commit comments

Comments
 (0)