nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: casbin/casbin
Pull request #866 (2021-08-13 10:49:11)

feat: support bracket with in operator
Signed-off-by: closetool <c299999999@qq.com>

Fix: #863 

hsluoyz: @closetool AT them to review
closetool: @tangyang9464 @Zxilly please review
Zxilly: @closetool You can also add a unit test with `[]` grammar, to prevent potential break change in the future.
hsluoyz: > @closetool You can also add a unit test with `[]` grammar, to prevent potential breaking change in the future.

good point, @closetool 
closetool: @hsluoyz @Zxilly Added

Patch:
diff --git a/examples/rbac_model_matcher_using_in_op_bracket.conf b/examples/rbac_model_matcher_using_in_op_bracket.conf
new file mode 100644
index 0000000..6ff819f
--- /dev/null
+++ b/examples/rbac_model_matcher_using_in_op_bracket.conf
@@ -0,0 +1,14 @@
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.obj in ['data2', 'data3']
\ No newline at end of file
diff --git a/model/model.go b/model/model.go
index 4c36c83..4269dbc 100644
--- a/model/model.go
+++ b/model/model.go
@@ -75,6 +75,10 @@ func (model Model) AddDef(sec string, key string, value string) bool {
 		ast.Value = util.RemoveComments(util.EscapeAssertion(ast.Value))
 	}
 
+	if sec == "m" && strings.Contains(ast.Value,"in") {
+		ast.Value = strings.Replace(strings.Replace(ast.Value,"[","(",-1),"]",")",-1)
+	}
+
 	_, ok := model[sec]
 	if !ok {
 		model[sec] = make(AssertionMap)


Test patch:
diff --git a/enforcer_test.go b/enforcer_test.go
index 1b8c8a0..3673398 100644
--- a/enforcer_test.go
+++ b/enforcer_test.go
@@ -214,6 +214,18 @@ func TestMatcherUsingInOperator(t *testing.T) {
 	testEnforce(t, e, "anyone", "data3", "read", true)
 }
 
+func TestMatcherUsingInOperatorBracket(t *testing.T) {
+	e, _ := NewEnforcer("examples/rbac_model_matcher_using_in_op_bracket.conf")
+	_, _ = e.AddPermissionForUser("alice", "data1", "read")
+
+	testEnforce(t, e, "alice", "data1", "read", true)
+	testEnforce(t, e, "alice", "data2", "read", true)
+	testEnforce(t, e, "alice", "data3", "read", true)
+	testEnforce(t, e, "anyone", "data1", "read", false)
+	testEnforce(t, e, "anyone", "data2", "read", true)
+	testEnforce(t, e, "anyone", "data3", "read", true)
+}
+
 func TestReloadPolicy(t *testing.T) {
 	e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
Repository: casbin/casbin
Pull request #883 (2021-09-04 03:03:19)

fix: GetPermissionsForUserInDomain not work
Signed-off-by: tangyang9464 <tangyang9464@163.com>

fix: #882 


Patch:
diff --git a/rbac_api_with_domains.go b/rbac_api_with_domains.go
index 6495a55..356888e 100644
--- a/rbac_api_with_domains.go
+++ b/rbac_api_with_domains.go
@@ -28,7 +28,14 @@ func (e *Enforcer) GetRolesForUserInDomain(name string, domain string) []string
 
 // GetPermissionsForUserInDomain gets permissions for a user or role inside a domain.
 func (e *Enforcer) GetPermissionsForUserInDomain(user string, domain string) [][]string {
-	return e.GetFilteredPolicy(0, user, domain)
+	var res [][]string
+	users, _ := e.GetRolesForUser(user, domain)
+	users = append(users, user)
+	for _, singleUser := range users {
+		policy := e.GetFilteredPolicy(0, singleUser, domain)
+		res = append(res, policy...)
+	}
+	return res
 }
 
 // AddRoleForUserInDomain adds a role for a user inside a domain.


Test patch:
diff --git a/rbac_api_with_domains_test.go b/rbac_api_with_domains_test.go
index 65b7836..8144dcd 100644
--- a/rbac_api_with_domains_test.go
+++ b/rbac_api_with_domains_test.go
@@ -180,13 +180,13 @@ func testGetPermissionsInDomain(t *testing.T, e *Enforcer, name string, domain s
 func TestPermissionAPIInDomain(t *testing.T) {
 	e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv")
 
-	testGetPermissionsInDomain(t, e, "alice", "domain1", [][]string{})
+	testGetPermissionsInDomain(t, e, "alice", "domain1", [][]string{{"admin", "domain1", "data1", "read"},{"admin", "domain1", "data1", "write"}})
 	testGetPermissionsInDomain(t, e, "bob", "domain1", [][]string{})
 	testGetPermissionsInDomain(t, e, "admin", "domain1", [][]string{{"admin", "domain1", "data1", "read"}, {"admin", "domain1", "data1", "write"}})
 	testGetPermissionsInDomain(t, e, "non_exist", "domain1", [][]string{})
 
 	testGetPermissionsInDomain(t, e, "alice", "domain2", [][]string{})
-	testGetPermissionsInDomain(t, e, "bob", "domain2", [][]string{})
+	testGetPermissionsInDomain(t, e, "bob", "domain2", [][]string{{"admin", "domain2", "data2", "read"},{"admin", "domain2", "data2", "write"}})
 	testGetPermissionsInDomain(t, e, "admin", "domain2", [][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}})
 	testGetPermissionsInDomain(t, e, "non_exist", "domain2", [][]string{})
 }
Repository: casbin/casbin
Pull request #896 (2021-09-29 07:05:01)

fix: EnforceContext members private
Signed-off-by: tangyang9464 <tangyang9464@163.com>


Patch:
diff --git a/enforcer.go b/enforcer.go
index e0f69f2..62d6288 100644
--- a/enforcer.go
+++ b/enforcer.go
@@ -52,10 +52,10 @@ type Enforcer struct {
 
 // EnforceContext is used as the first element of the parameter "rvals" in method "enforce"
 type EnforceContext struct {
-	rType string
-	pType string
-	eType string
-	mType string
+	RType string
+	PType string
+	EType string
+	MType string
 }
 
 // NewEnforcer creates an enforcer via file or DB.
@@ -456,10 +456,10 @@ func (e *Enforcer) BuildIncrementalRoleLinks(op model.PolicyOp, ptype string, ru
 // NewEnforceContext Create a default structure based on the suffix
 func NewEnforceContext(suffix string) EnforceContext {
 	return EnforceContext{
-		rType: "r" + suffix,
-		pType: "p" + suffix,
-		eType: "e" + suffix,
-		mType: "m" + suffix,
+		RType: "r" + suffix,
+		PType: "p" + suffix,
+		EType: "e" + suffix,
+		MType: "m" + suffix,
 	}
 }
 
@@ -493,10 +493,10 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
 		switch rvals[0].(type) {
 		case EnforceContext:
 			enforceContext := rvals[0].(EnforceContext)
-			rType = enforceContext.rType
-			pType = enforceContext.pType
-			eType = enforceContext.eType
-			mType = enforceContext.mType
+			rType = enforceContext.RType
+			pType = enforceContext.PType
+			eType = enforceContext.EType
+			mType = enforceContext.MType
 			rvals = rvals[1:]
 		default:
 			break
@@ -667,7 +667,7 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
 		if len(*explains) > 0 {
 			logExplains = append(logExplains, *explains)
 		}
-		
+
 		if explainIndex != -1 && len(e.model["p"][pType].Policy) > explainIndex {
 			*explains = e.model["p"][pType].Policy[explainIndex]
 			logExplains = append(logExplains, *explains)


Test patch:
diff --git a/enforcer_test.go b/enforcer_test.go
index 3673398..cda3bb3 100644
--- a/enforcer_test.go
+++ b/enforcer_test.go
@@ -534,7 +534,7 @@ func TestSubjectPriorityWithDomain(t *testing.T) {
 func TestMultiplePolicyDefinitions(t *testing.T) {
 	e, _ := NewEnforcer("examples/multiple_policy_definitions_model.conf", "examples/multiple_policy_definitions_policy.csv")
 	enforceContext := NewEnforceContext("2")
-	enforceContext.eType = "e"
+	enforceContext.EType = "e"
 	testBatchEnforce(t, e, [][]interface{}{
 		{"alice", "data2", "read"},
 		{enforceContext, struct{ Age int }{Age: 70}, "/data1", "read"},
Repository: casbin/casbin
Pull request #940 (2021-12-17 06:53:45)

fix: Not set hit rule when matched allow rule for rbac_with_deny_policy
Signed-off-by: Jon Lee <techlee@qq.com>

casbin-bot:  @tangyang9464 @closetool @sagilio please review

Patch:
diff --git a/effector/default_effector.go b/effector/default_effector.go
index f05bec7..de5dc37 100644
--- a/effector/default_effector.go
+++ b/effector/default_effector.go
@@ -75,6 +75,8 @@ func (e *DefaultEffector) MergeEffects(expr string, effects []Effect, matches []
 
 			if eft == Allow {
 				result = Allow
+				// set hit rule to first matched allow rule
+				explainIndex = i
 				break
 			}
 		}
diff --git a/enforcer.go b/enforcer.go
index b9d8a60..41c44cd 100644
--- a/enforcer.go
+++ b/enforcer.go
@@ -573,7 +573,8 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
 				for _, ruleName := range ruleNames {
 					if j, ok := parameters.pTokens[ruleName]; ok {
 						rule := util.EscapeAssertion(pvals[j])
-						replacements[ruleName] = rule
+						// Increase the evaluate priority of the rule
+						replacements[ruleName] = "(" + rule + ")"
 					} else {
 						return false, errors.New("please make sure rule exists in policy when using eval() in matcher")
 					}
diff --git a/examples/eval_operator_model.conf b/examples/eval_operator_model.conf
new file mode 100644
index 0000000..9ffb5ba
--- /dev/null
+++ b/examples/eval_operator_model.conf
@@ -0,0 +1,11 @@
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub_rule, obj_rule, act
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = eval(p.sub_rule) && eval(p.obj_rule) && (p.act == '*' || r.act == p.act)
diff --git a/examples/eval_operator_policy.csv b/examples/eval_operator_policy.csv
new file mode 100644
index 0000000..85665a8
--- /dev/null
+++ b/examples/eval_operator_policy.csv
@@ -0,0 +1,1 @@
+p, r.sub == 'admin' || false, r.obj == 'users', write
\ No newline at end of file


Test patch:
diff --git a/enforcer_test.go b/enforcer_test.go
index cda3bb3..7c35608 100644
--- a/enforcer_test.go
+++ b/enforcer_test.go
@@ -598,3 +598,10 @@ func TestReloadPolicyWithFunc(t *testing.T) {
 	testEnforce(t, e, "alice", "/book/1", "GET", true)
 	testEnforce(t, e, "bob", "/pen/3", "GET", true)
 }
+
+func TestEvalPriority(t *testing.T) {
+	e, _ := NewEnforcer("examples/eval_operator_model.conf", "examples/eval_operator_policy.csv")
+	testEnforce(t, e, "admin", "users", "write", true)
+	testEnforce(t, e, "admin", "none", "write", false)
+	testEnforce(t, e, "user", "users", "write", false)
+}
Repository: casbin/casbin
Pull request #941 (2021-12-17 15:51:17)

fix: priority of eval operator
Fix: https://github.com/casbin/casbin/issues/939

casbin-bot:  @tangyang9464 @closetool @sagilio please review
casbin-bot:  @tangyang9464 @closetool @sagilio please review
Abingcbc: @maxtwardowski PTAL
The newly added test case is a simplified version of your example.
maxtwardowski: @Abingcbc 
https://replit.com/@MaxTwardowski1/Casbin-OR-bug-fix-test?v=1 LGTM!

Patch:
diff --git a/enforcer.go b/enforcer.go
index b9d8a60..41c44cd 100644
--- a/enforcer.go
+++ b/enforcer.go
@@ -573,7 +573,8 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
 				for _, ruleName := range ruleNames {
 					if j, ok := parameters.pTokens[ruleName]; ok {
 						rule := util.EscapeAssertion(pvals[j])
-						replacements[ruleName] = rule
+						// Increase the evaluate priority of the rule
+						replacements[ruleName] = "(" + rule + ")"
 					} else {
 						return false, errors.New("please make sure rule exists in policy when using eval() in matcher")
 					}
diff --git a/examples/eval_operator_model.conf b/examples/eval_operator_model.conf
new file mode 100644
index 0000000..9ffb5ba
--- /dev/null
+++ b/examples/eval_operator_model.conf
@@ -0,0 +1,11 @@
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub_rule, obj_rule, act
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = eval(p.sub_rule) && eval(p.obj_rule) && (p.act == '*' || r.act == p.act)
diff --git a/examples/eval_operator_policy.csv b/examples/eval_operator_policy.csv
new file mode 100644
index 0000000..85665a8
--- /dev/null
+++ b/examples/eval_operator_policy.csv
@@ -0,0 +1,1 @@
+p, r.sub == 'admin' || false, r.obj == 'users', write
\ No newline at end of file


Test patch:
diff --git a/enforcer_test.go b/enforcer_test.go
index cda3bb3..7c35608 100644
--- a/enforcer_test.go
+++ b/enforcer_test.go
@@ -598,3 +598,10 @@ func TestReloadPolicyWithFunc(t *testing.T) {
 	testEnforce(t, e, "alice", "/book/1", "GET", true)
 	testEnforce(t, e, "bob", "/pen/3", "GET", true)
 }
+
+func TestEvalPriority(t *testing.T) {
+	e, _ := NewEnforcer("examples/eval_operator_model.conf", "examples/eval_operator_policy.csv")
+	testEnforce(t, e, "admin", "users", "write", true)
+	testEnforce(t, e, "admin", "none", "write", false)
+	testEnforce(t, e, "user", "users", "write", false)
+}
Repository: casbin/casbin
Pull request #991 (2022-04-20 06:18:16)

fix: EscapeAssertion should escapes "r[0-9]." instead "r"
Signed-off-by: tangyang9464 <tangyang9464@163.com>

Similar for Java: https://github.com/casbin/jcasbin/pull/268

casbin-bot:  @closetool @sagilio please review

Patch:
diff --git a/util/util.go b/util/util.go
index 9d060b7..1b8d4bf 100644
--- a/util/util.go
+++ b/util/util.go
@@ -21,16 +21,13 @@ import (
 	"sync"
 )
 
-var evalReg *regexp.Regexp = regexp.MustCompile(`\beval\((?P<rule>[^)]*)\)`)
+var evalReg = regexp.MustCompile(`\beval\((?P<rule>[^)]*)\)`)
+
+var escapeAssertionRegex = regexp.MustCompile(`\b((r|p)[0-9]*)\.`)
 
 // EscapeAssertion escapes the dots in the assertion, because the expression evaluation doesn't support such variable names.
 func EscapeAssertion(s string) string {
-	//Replace the first dot, because it can't be recognized by the regexp.
-	if strings.HasPrefix(s, "r") || strings.HasPrefix(s, "p") {
-		s = strings.Replace(s, ".", "_", 1)
-	}
-	var regex = regexp.MustCompile(`(\|| |=|\)|\(|&|<|>|,|\+|-|!|\*|\/)((r|p)[0-9]*)\.`)
-	s = regex.ReplaceAllStringFunc(s, func(m string) string {
+	s = escapeAssertionRegex.ReplaceAllStringFunc(s, func(m string) string {
 		return strings.Replace(m, ".", "_", 1)
 	})
 	return s


Test patch:
diff --git a/util/util_test.go b/util/util_test.go
index 368b100..37a2fdd 100644
--- a/util/util_test.go
+++ b/util/util_test.go
@@ -29,8 +29,13 @@ func testEscapeAssertion(t *testing.T, s string, res string) {
 }
 
 func TestEscapeAssertion(t *testing.T) {
+	testEscapeAssertion(t, "r_sub == r_obj.value", "r_sub == r_obj.value")
+	testEscapeAssertion(t, "p_sub == r_sub.value", "p_sub == r_sub.value")
+	testEscapeAssertion(t, "r.attr.value == p.attr", "r_attr.value == p_attr")
 	testEscapeAssertion(t, "r.attr.value == p.attr", "r_attr.value == p_attr")
 	testEscapeAssertion(t, "r.attp.value || p.attr", "r_attp.value || p_attr")
+	testEscapeAssertion(t, "r2.attr.value == p2.attr", "r2_attr.value == p2_attr")
+	testEscapeAssertion(t, "r2.attp.value || p2.attr", "r2_attp.value || p2_attr")
 	testEscapeAssertion(t, "r.attp.value &&p.attr", "r_attp.value &&p_attr")
 	testEscapeAssertion(t, "r.attp.value >p.attr", "r_attp.value >p_attr")
 	testEscapeAssertion(t, "r.attp.value <p.attr", "r_attp.value <p_attr")
Repository: casbin/casbin
Pull request #1012 (2022-05-13 11:15:15)

fix: handle duplicate adapter entries
fix: #1011 

Signed-off-by: Andreas Bichinger <andreas.bichinger@gmail.com>

casbin-bot:  @tangyang9464 @closetool @sagilio please review

Patch:
diff --git a/persist/adapter.go b/persist/adapter.go
index 270110c..5d9507c 100644
--- a/persist/adapter.go
+++ b/persist/adapter.go
@@ -44,8 +44,10 @@ func LoadPolicyLine(line string, m model.Model) {
 func LoadPolicyArray(rule []string, m model.Model) {
 	key := rule[0]
 	sec := key[:1]
-	m[sec][key].Policy = append(m[sec][key].Policy, rule[1:])
-	m[sec][key].PolicyMap[strings.Join(rule[1:], model.DefaultSep)] = len(m[sec][key].Policy) - 1
+	if m.HasPolicy(sec, key, rule[1:]) {
+		return
+	}
+	m.AddPolicy(sec, key, rule[1:])
 }
 
 // Adapter is the interface for Casbin adapters.


Test patch:
diff --git a/persist/persist_test.go b/persist/persist_test.go
index f3e4422..d38bd7b 100644
--- a/persist/persist_test.go
+++ b/persist/persist_test.go
@@ -1,9 +1,53 @@
-package persist
+// Copyright 2017 The casbin Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package persist_test
 
 import (
 	"testing"
+
+	"github.com/casbin/casbin/v2"
+	"github.com/casbin/casbin/v2/model"
+	"github.com/casbin/casbin/v2/persist"
 )
 
 func TestPersist(t *testing.T) {
 	//No tests yet
 }
+
+func testRuleCount(t *testing.T, model model.Model, expected int, sec string, ptype string, tag string) {
+	t.Helper()
+
+	ruleCount := len(model[sec][ptype].Policy)
+	if ruleCount != expected {
+		t.Errorf("[%s] rule count: %d, expected %d", tag, ruleCount, expected)
+	}
+}
+
+func TestDuplicateRuleInAdapter(t *testing.T) {
+	e, _ := casbin.NewEnforcer("../examples/basic_model.conf")
+
+	_, _ = e.AddPolicy("alice", "data1", "read")
+	_, _ = e.AddPolicy("alice", "data1", "read")
+
+	testRuleCount(t, e.GetModel(), 1, "p", "p", "AddPolicy")
+
+	e.ClearPolicy()
+
+	//simulate adapter.LoadPolicy with duplicate rules
+	persist.LoadPolicyArray([]string{"p", "alice", "data1", "read"}, e.GetModel())
+	persist.LoadPolicyArray([]string{"p", "alice", "data1", "read"}, e.GetModel())
+
+	testRuleCount(t, e.GetModel(), 1, "p", "p", "LoadPolicyArray")
+}
Repository: casbin/casbin
Pull request #1034 (2022-06-18 12:56:34)

fix: remove duplicate element when getRoles
Fix: https://github.com/casbin/casbin/issues/1033

casbin-bot:  @tangyang9464 @closetool @sagilio please review
hsluoyz: @tangyang9464 @sagilio plz review

Patch:
diff --git a/rbac/default-role-manager/role_manager.go b/rbac/default-role-manager/role_manager.go
index f6ec3ba..f9d2549 100644
--- a/rbac/default-role-manager/role_manager.go
+++ b/rbac/default-role-manager/role_manager.go
@@ -152,7 +152,7 @@ func (r *Role) getRoles() []string {
 		names = append(names, key.(string))
 		return true
 	})
-	return names
+	return util.RemoveDuplicateElement(names)
 }
 
 func (r *Role) getUsers() []string {


Test patch:
diff --git a/rbac/default-role-manager/role_manager_test.go b/rbac/default-role-manager/role_manager_test.go
index 930f1db..daedfef 100644
--- a/rbac/default-role-manager/role_manager_test.go
+++ b/rbac/default-role-manager/role_manager_test.go
@@ -138,6 +138,27 @@ func TestRole(t *testing.T) {
 	testPrintRoles(t, rm, "g1", []string{})
 	testPrintRoles(t, rm, "g2", []string{})
 	testPrintRoles(t, rm, "g3", []string{})
+
+
+	rm = NewRoleManager(3)
+	rm.AddMatchingFunc("keyMatch", util.KeyMatch)
+
+	_ = rm.AddLink("u1", "g1")
+	_ = rm.AddLink("u1", "*")
+	_ = rm.AddLink("u2", "g2")
+
+	// Current role inheritance tree after deleting the links:
+	//          g1   g2
+	//            \ /  \
+	//             *    u2
+	//             |
+	//             u1
+	testRole(t, rm, "u1", "g1", true)
+	testRole(t, rm, "u1", "g2", true)
+	testRole(t, rm, "u2", "g2", true)
+	testRole(t, rm, "u2", "g1", false)
+	testPrintRoles(t, rm, "u1", []string{"*", "u1", "u2", "g1", "g2"})
+	testPrintUsers(t, rm, "*", []string{"u1"})
 }
 
 func TestDomainRole(t *testing.T) {
Repository: casbin/casbin
Pull request #1038 (2022-06-23 12:53:42)

fix(RoleManager): max hierarchy level
fix: #1037 

Signed-off-by: Andreas Bichinger <andreas.bichinger@gmail.com>

casbin-bot:  @tangyang9464 @closetool @sagilio please review
hsluoyz: @zzzkl plz review
zzzkl: The fix works for me. Thx

Patch:
diff --git a/rbac/default-role-manager/role_manager.go b/rbac/default-role-manager/role_manager.go
index f9d2549..4e3edf7 100644
--- a/rbac/default-role-manager/role_manager.go
+++ b/rbac/default-role-manager/role_manager.go
@@ -331,7 +331,7 @@ func (rm *RoleManagerImpl) HasLink(name1 string, name2 string, domains ...string
 }
 
 func (rm *RoleManagerImpl) hasLinkHelper(targetName string, roles map[string]*Role, level int) bool {
-	if level <= 0 || len(roles) == 0 {
+	if level < 0 || len(roles) == 0 {
 		return false
 	}
 


Test patch:
diff --git a/rbac/default-role-manager/role_manager_test.go b/rbac/default-role-manager/role_manager_test.go
index daedfef..1f68b17 100644
--- a/rbac/default-role-manager/role_manager_test.go
+++ b/rbac/default-role-manager/role_manager_test.go
@@ -361,3 +361,29 @@ func TestTemporaryRoles(t *testing.T) {
 	testPrintUsers(t, rm, "user", []string{"u\\d+", "u1"})
 	testPrintRoles(t, rm, "u1", []string{"user", "manager"})
 }
+
+func TestMaxHierarchyLevel(t *testing.T) {
+	rm := NewRoleManager(1)
+	_ = rm.AddLink("level0", "level1")
+	_ = rm.AddLink("level1", "level2")
+	_ = rm.AddLink("level2", "level3")
+
+	testRole(t, rm, "level0", "level0", true)
+	testRole(t, rm, "level0", "level1", true)
+	testRole(t, rm, "level0", "level2", false)
+	testRole(t, rm, "level0", "level3", false)
+	testRole(t, rm, "level1", "level2", true)
+	testRole(t, rm, "level1", "level3", false)
+
+	rm = NewRoleManager(2)
+	_ = rm.AddLink("level0", "level1")
+	_ = rm.AddLink("level1", "level2")
+	_ = rm.AddLink("level2", "level3")
+
+	testRole(t, rm, "level0", "level0", true)
+	testRole(t, rm, "level0", "level1", true)
+	testRole(t, rm, "level0", "level2", true)
+	testRole(t, rm, "level0", "level3", false)
+	testRole(t, rm, "level1", "level2", true)
+	testRole(t, rm, "level1", "level3", true)
+}
Repository: casbin/casbin
Pull request #1080 (2022-08-18 13:13:48)

fix: GetImplicitPermissionsForUser doesn't support multiple domains for consistency
fix https://github.com/casbin/casbin/issues/1053#issuecomment-1187538374

casbin-bot:  @tangyang9464 please review
hsluoyz: @tangyang9464 

Patch:
diff --git a/rbac_api.go b/rbac_api.go
index 4f6c253..9c5b72e 100644
--- a/rbac_api.go
+++ b/rbac_api.go
@@ -304,19 +304,19 @@ func (e *Enforcer) GetNamedImplicitPermissionsForUser(ptype string, user string,
 			if matched {
 				permission = append(permission, deepCopyPolicy(rule))
 			}
+		} else if len(domain) > 1 {
+			return nil, errors.ERR_DOMAIN_PARAMETER
 		} 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
-				}
+			d := domain[0]
+			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)
 			}
 		}
 	}


Test patch:
diff --git a/rbac_api_test.go b/rbac_api_test.go
index 0d7101d..236dba4 100644
--- a/rbac_api_test.go
+++ b/rbac_api_test.go
@@ -358,7 +358,15 @@ func TestImplicitPermissionAPI(t *testing.T) {
 	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")
+
+	_, err := e.GetImplicitPermissionsForUser("admin", "domain1", "domain2")
+	if err == nil {
+		t.Error("GetImplicitPermissionsForUser should not support multiple domains")
+	}
+
+	testGetImplicitPermissions(t, e, "alice",
+		[][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}, {"admin", "domain2", "data3", "read"}},
+		"domain2")
 
 	e, _ = NewEnforcer("examples/rbac_with_multiple_policy_model.conf", "examples/rbac_with_multiple_policy_policy.csv")