Skip to content

Commit

Permalink
feat: support domain matching when getting permissions (#992)
Browse files Browse the repository at this point in the history
  • Loading branch information
Abingcbc committed Jul 3, 2022
1 parent 2eed551 commit 49154f4
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 27 deletions.
1 change: 1 addition & 0 deletions examples/rbac_with_domain_pattern_policy.csv
Expand Up @@ -2,6 +2,7 @@ p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write
p, admin, *, data3, read

g, alice, admin, *
g, bob, admin, domain2
30 changes: 20 additions & 10 deletions rbac/default-role-manager/role_manager.go
Expand Up @@ -201,12 +201,17 @@ func (rm *RoleManagerImpl) rebuild() {
})
}

func (rm *RoleManagerImpl) match(str string, pattern string) bool {
func (rm *RoleManagerImpl) Match(str string, pattern string) bool {
cacheKey := strings.Join([]string{str, pattern}, "$$")
if v, has := rm.matchingFuncCache.Get(cacheKey); has {
return v.(bool)
} else {
matched := rm.matchingFunc(str, pattern)
var matched bool
if rm.matchingFunc != nil {
matched = rm.matchingFunc(str, pattern)
} else {
matched = str == pattern
}
rm.matchingFuncCache.Put(cacheKey, matched)
return matched
}
Expand All @@ -215,9 +220,9 @@ func (rm *RoleManagerImpl) match(str string, pattern string) bool {
func (rm *RoleManagerImpl) rangeMatchingRoles(name string, isPattern bool, fn func(role *Role) bool) {
rm.allRoles.Range(func(key, value interface{}) bool {
name2 := key.(string)
if isPattern && name != name2 && rm.match(name2, name) {
if isPattern && name != name2 && rm.Match(name2, name) {
fn(value.(*Role))
} else if !isPattern && name != name2 && rm.match(name, name2) {
} else if !isPattern && name != name2 && rm.Match(name, name2) {
fn(value.(*Role))
}
return true
Expand Down Expand Up @@ -313,7 +318,7 @@ func (rm *RoleManagerImpl) DeleteLink(name1 string, name2 string, domains ...str

// HasLink determines whether role: name1 inherits role: name2.
func (rm *RoleManagerImpl) HasLink(name1 string, name2 string, domains ...string) (bool, error) {
if name1 == name2 || (rm.matchingFunc != nil && rm.match(name1, name2)) {
if name1 == name2 || (rm.matchingFunc != nil && rm.Match(name1, name2)) {
return true, nil
}

Expand All @@ -337,7 +342,7 @@ func (rm *RoleManagerImpl) hasLinkHelper(targetName string, roles map[string]*Ro

nextRoles := map[string]*Role{}
for _, role := range roles {
if targetName == role.name || (rm.matchingFunc != nil && rm.match(role.name, targetName)) {
if targetName == role.name || (rm.matchingFunc != nil && rm.Match(role.name, targetName)) {
return true
}
role.rangeRoles(func(key, value interface{}) bool {
Expand Down Expand Up @@ -507,12 +512,17 @@ func (dm *DomainManager) getDomain(domains ...string) (domain string, err error)
}
}

func (dm *DomainManager) match(str string, pattern string) bool {
func (dm *DomainManager) Match(str string, pattern string) bool {
cacheKey := strings.Join([]string{str, pattern}, "$$")
if v, has := dm.matchingFuncCache.Get(cacheKey); has {
return v.(bool)
} else {
matched := dm.domainMatchingFunc(str, pattern)
var matched bool
if dm.domainMatchingFunc != nil {
matched = dm.domainMatchingFunc(str, pattern)
} else {
matched = str == pattern
}
dm.matchingFuncCache.Put(cacheKey, matched)
return matched
}
Expand All @@ -522,7 +532,7 @@ func (dm *DomainManager) rangeAffectedRoleManagers(domain string, fn func(rm *Ro
if dm.domainMatchingFunc != nil {
dm.rmMap.Range(func(key, value interface{}) bool {
domain2 := key.(string)
if domain != domain2 && dm.match(domain2, domain) {
if domain != domain2 && dm.Match(domain2, domain) {
fn(value.(*RoleManagerImpl))
}
return true
Expand Down Expand Up @@ -551,7 +561,7 @@ func (dm *DomainManager) getRoleManager(domain string, store bool) *RoleManagerI
dm.rmMap.Range(func(key, value interface{}) bool {
domain2 := key.(string)
rm2 := value.(*RoleManagerImpl)
if domain != domain2 && dm.match(domain, domain2) {
if domain != domain2 && dm.Match(domain, domain2) {
rm.copyFrom(rm2)
}
return true
Expand Down
48 changes: 33 additions & 15 deletions rbac_api.go
Expand Up @@ -17,6 +17,7 @@ package casbin
import (
"github.com/casbin/casbin/v2/constant"
"github.com/casbin/casbin/v2/errors"
"github.com/casbin/casbin/v2/rbac/default-role-manager"
"github.com/casbin/casbin/v2/util"
)

Expand Down Expand Up @@ -294,22 +295,32 @@ func (e *Enforcer) GetImplicitPermissionsForUser(user string, domain ...string)
// GetImplicitPermissionsForUser("alice") can only get: [["admin", "data1", "read"]], whose policy is default policy "p"
// But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice")
func (e *Enforcer) GetNamedImplicitPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) {
roles, err := e.GetImplicitRolesForUser(user, domain...)
if err != nil {
return nil, err
}

roles = append([]string{user}, roles...)

var res [][]string
var permissions [][]string
for _, role := range roles {
permissions = e.GetNamedPermissionsForUser(ptype, role, domain...)

res = append(res, permissions...)
permission := make([][]string, 0)
rm := e.GetRoleManager()
domainIndex, _ := e.GetFieldIndex(ptype, constant.DomainIndex)
for _, rule := range e.model["p"][ptype].Policy {
if len(domain) == 0 {
matched, _ := rm.HasLink(user, rule[0])
if matched {
permission = append(permission, deepCopyPolicy(rule))
}
} else {
for _, d := range domain {
matched := rm.(*defaultrolemanager.RoleManager).Match(d, rule[domainIndex])
if !matched {
continue
}
matched, _ = rm.HasLink(user, rule[0], d)
if matched {
newRule := deepCopyPolicy(rule)
newRule[domainIndex] = d
permission = append(permission, newRule)
break
}
}
}
}

return res, nil
return permission, nil
}

// GetImplicitUsersForPermission gets implicit users for a permission.
Expand Down Expand Up @@ -397,3 +408,10 @@ func (e *Enforcer) GetImplicitResourcesForUser(user string, domain ...string) ([
}
return res, nil
}

// deepCopyPolicy returns a deepcopy version of the policy to prevent changing policies through returned slice
func deepCopyPolicy(src []string) []string {
newRule := make([]string, len(src))
copy(newRule, src)
return newRule
}
11 changes: 9 additions & 2 deletions rbac_api_test.go
Expand Up @@ -314,9 +314,9 @@ func TestImplicitRoleAPI(t *testing.T) {
testGetRoles(t, e, []string{"/book/1/2/3/4/5", "pen_admin"}, "cathy")
}

func testGetImplicitPermissions(t *testing.T, e *Enforcer, name string, res [][]string) {
func testGetImplicitPermissions(t *testing.T, e *Enforcer, name string, res [][]string, domain ...string) {
t.Helper()
myRes, _ := e.GetImplicitPermissionsForUser(name)
myRes, _ := e.GetImplicitPermissionsForUser(name, domain...)
t.Log("Implicit permissions for ", name, ": ", myRes)

if !util.Set2DEquals(res, myRes) {
Expand Down Expand Up @@ -353,10 +353,17 @@ func TestImplicitPermissionAPI(t *testing.T) {
testGetImplicitPermissions(t, e, "alice", [][]string{{"alice", "data1", "read"}, {"data1_admin", "data1", "read"}, {"data1_admin", "data1", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}})
testGetImplicitPermissions(t, e, "bob", [][]string{{"bob", "data2", "write"}})

e, _ = NewEnforcer("examples/rbac_with_domain_pattern_model.conf", "examples/rbac_with_domain_pattern_policy.csv")
e.AddNamedDomainMatchingFunc("g", "KeyMatch", util.KeyMatch)

testGetImplicitPermissions(t, e, "admin", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}, {"admin", "domain1", "data3", "read"}}, "domain1")
testGetImplicitPermissions(t, e, "admin", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}, {"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}, {"admin", "domain1", "data3", "read"}}, "domain1", "domain2")

e, _ = NewEnforcer("examples/rbac_with_multiple_policy_model.conf", "examples/rbac_with_multiple_policy_policy.csv")

testGetNamedImplicitPermissions(t, e, "p", "alice", [][]string{{"user", "/data", "GET"}, {"admin", "/data", "POST"}})
testGetNamedImplicitPermissions(t, e, "p2", "alice", [][]string{{"user", "view"}, {"admin", "create"}})

}

func TestImplicitPermissionAPIWithDomain(t *testing.T) {
Expand Down

0 comments on commit 49154f4

Please sign in to comment.