diff --git a/examples/rbac_with_domain_pattern_policy.csv b/examples/rbac_with_domain_pattern_policy.csv index c8abd35e..783f721e 100644 --- a/examples/rbac_with_domain_pattern_policy.csv +++ b/examples/rbac_with_domain_pattern_policy.csv @@ -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 \ No newline at end of file diff --git a/rbac/default-role-manager/role_manager.go b/rbac/default-role-manager/role_manager.go index 4e3edf72..6e69cf80 100644 --- a/rbac/default-role-manager/role_manager.go +++ b/rbac/default-role-manager/role_manager.go @@ -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 } @@ -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 @@ -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 } @@ -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 { @@ -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 } @@ -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 @@ -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 diff --git a/rbac_api.go b/rbac_api.go index b366a26f..4f6c2530 100644 --- a/rbac_api.go +++ b/rbac_api.go @@ -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" ) @@ -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. @@ -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 +} diff --git a/rbac_api_test.go b/rbac_api_test.go index b2ba1b81..97311063 100644 --- a/rbac_api_test.go +++ b/rbac_api_test.go @@ -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) { @@ -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) {