From 998a563c0f6624f48654d9beaa399546948b93f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:27:03 +0000 Subject: [PATCH 1/5] Initial plan From 5ab7b5fc9c21231003a6b36e705699e426ed096b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:34:27 +0000 Subject: [PATCH 2/5] Fix domain token renaming bug by making domain detection dynamic Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- enforcer.go | 12 +++++++- model_test.go | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/enforcer.go b/enforcer.go index 498680c07..5fed5f2b0 100644 --- a/enforcer.go +++ b/enforcer.go @@ -518,7 +518,17 @@ func (e *Enforcer) initRmMap() { assertion.CondRM = defaultrolemanager.NewConditionalDomainManager(10) e.condRmMap[ptype] = assertion.CondRM } - matchFun := "keyMatch(r_dom, p_dom)" + // Dynamically detect the domain token name from the model definition + // Domain is typically the second parameter (index 1) in request/policy definitions + rDomainToken := "r_dom" + pDomainToken := "p_dom" + if len(e.model["r"]) > 0 && len(e.model["r"]["r"].Tokens) > 1 { + rDomainToken = e.model["r"]["r"].Tokens[1] + } + if len(e.model["p"]) > 0 && len(e.model["p"]["p"].Tokens) > 1 { + pDomainToken = e.model["p"]["p"].Tokens[1] + } + matchFun := fmt.Sprintf("keyMatch(%s, %s)", rDomainToken, pDomainToken) if strings.Contains(e.model["m"]["m"].Value, matchFun) { e.AddNamedDomainMatchingFunc(ptype, "g", util.KeyMatch) } diff --git a/model_test.go b/model_test.go index 79126bcfc..3545b24bd 100644 --- a/model_test.go +++ b/model_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/casbin/casbin/v3/log" + "github.com/casbin/casbin/v3/model" fileadapter "github.com/casbin/casbin/v3/persist/file-adapter" "github.com/casbin/casbin/v3/rbac" "github.com/casbin/casbin/v3/util" @@ -241,6 +242,87 @@ func TestRBACModelWithDomainsAtRuntimeMockAdapter(t *testing.T) { testDomainEnforce(t, e, "bob", "domain2", "data2", "read", false) } +func TestRBACModelWithDomainTokenRename(t *testing.T) { + // Test that renaming the domain token from "dom" to another name (e.g., "dom1") + // still works correctly. This is a regression test for the issue where the + // hardcoded "r_dom" and "p_dom" strings prevented proper domain matching. + + // Test with standard "dom" token + modelText1 := ` +[request_definition] +r = sub, dom, obj, act + +[policy_definition] +p = sub, dom, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.dom) && keyMatch(r.dom, p.dom) && r.obj == p.obj && r.act == p.act +` + m1, _ := model.NewModelFromString(modelText1) + e1, _ := NewEnforcer(m1) + _, _ = e1.AddPolicy("admin", "domain1", "data1", "read") + _, _ = e1.AddGroupingPolicy("alice", "admin", "domain*") + + testDomainEnforce(t, e1, "alice", "domain1", "data1", "read", true) + testDomainEnforce(t, e1, "alice", "domain2", "data1", "read", false) + + // Test with renamed "dom1" token + modelText2 := ` +[request_definition] +r = sub, dom1, obj, act + +[policy_definition] +p = sub, dom1, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.dom1) && keyMatch(r.dom1, p.dom1) && r.obj == p.obj && r.act == p.act +` + m2, _ := model.NewModelFromString(modelText2) + e2, _ := NewEnforcer(m2) + _, _ = e2.AddPolicy("admin", "domain1", "data1", "read") + _, _ = e2.AddGroupingPolicy("alice", "admin", "domain*") + + testDomainEnforce(t, e2, "alice", "domain1", "data1", "read", true) + testDomainEnforce(t, e2, "alice", "domain2", "data1", "read", false) + + // Test with renamed "tenant" token + modelText3 := ` +[request_definition] +r = sub, tenant, obj, act + +[policy_definition] +p = sub, tenant, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.tenant) && keyMatch(r.tenant, p.tenant) && r.obj == p.obj && r.act == p.act +` + m3, _ := model.NewModelFromString(modelText3) + e3, _ := NewEnforcer(m3) + _, _ = e3.AddPolicy("admin", "domain1", "data1", "read") + _, _ = e3.AddGroupingPolicy("alice", "admin", "domain*") + + testDomainEnforce(t, e3, "alice", "domain1", "data1", "read", true) + testDomainEnforce(t, e3, "alice", "domain2", "data1", "read", false) +} + func TestRBACModelWithDeny(t *testing.T) { e, _ := NewEnforcer("examples/rbac_with_deny_model.conf", "examples/rbac_with_deny_policy.csv") From c81c4ae1d2223b6faf74fea1fff7c6cc7a7bfd30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:36:50 +0000 Subject: [PATCH 3/5] Format code with gofmt Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- model_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/model_test.go b/model_test.go index 3545b24bd..be00f4d9c 100644 --- a/model_test.go +++ b/model_test.go @@ -246,7 +246,7 @@ func TestRBACModelWithDomainTokenRename(t *testing.T) { // Test that renaming the domain token from "dom" to another name (e.g., "dom1") // still works correctly. This is a regression test for the issue where the // hardcoded "r_dom" and "p_dom" strings prevented proper domain matching. - + // Test with standard "dom" token modelText1 := ` [request_definition] @@ -268,7 +268,7 @@ m = g(r.sub, p.sub, r.dom) && keyMatch(r.dom, p.dom) && r.obj == p.obj && r.act e1, _ := NewEnforcer(m1) _, _ = e1.AddPolicy("admin", "domain1", "data1", "read") _, _ = e1.AddGroupingPolicy("alice", "admin", "domain*") - + testDomainEnforce(t, e1, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e1, "alice", "domain2", "data1", "read", false) @@ -293,7 +293,7 @@ m = g(r.sub, p.sub, r.dom1) && keyMatch(r.dom1, p.dom1) && r.obj == p.obj && r.a e2, _ := NewEnforcer(m2) _, _ = e2.AddPolicy("admin", "domain1", "data1", "read") _, _ = e2.AddGroupingPolicy("alice", "admin", "domain*") - + testDomainEnforce(t, e2, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e2, "alice", "domain2", "data1", "read", false) @@ -318,7 +318,7 @@ m = g(r.sub, p.sub, r.tenant) && keyMatch(r.tenant, p.tenant) && r.obj == p.obj e3, _ := NewEnforcer(m3) _, _ = e3.AddPolicy("admin", "domain1", "data1", "read") _, _ = e3.AddGroupingPolicy("alice", "admin", "domain*") - + testDomainEnforce(t, e3, "alice", "domain1", "data1", "read", true) testDomainEnforce(t, e3, "alice", "domain2", "data1", "read", false) } From 93bd7c8a4531bf483d9d2d3c6f1378b13c671e2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 13:38:27 +0000 Subject: [PATCH 4/5] Improve domain token detection with better safety checks Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- enforcer.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/enforcer.go b/enforcer.go index 5fed5f2b0..0a326ecca 100644 --- a/enforcer.go +++ b/enforcer.go @@ -518,19 +518,23 @@ func (e *Enforcer) initRmMap() { assertion.CondRM = defaultrolemanager.NewConditionalDomainManager(10) e.condRmMap[ptype] = assertion.CondRM } - // Dynamically detect the domain token name from the model definition - // Domain is typically the second parameter (index 1) in request/policy definitions - rDomainToken := "r_dom" - pDomainToken := "p_dom" - if len(e.model["r"]) > 0 && len(e.model["r"]["r"].Tokens) > 1 { - rDomainToken = e.model["r"]["r"].Tokens[1] + // Dynamically detect the domain token name from the model definition. + // In RBAC with domains, the domain is typically the second parameter (index 1) + // in both request and policy definitions (e.g., r = sub, dom, obj, act). + // We extract the actual token names to support arbitrary domain parameter names. + var rDomainToken, pDomainToken string + if rAssertion, ok := e.model["r"]["r"]; ok && len(rAssertion.Tokens) > 1 { + rDomainToken = rAssertion.Tokens[1] } - if len(e.model["p"]) > 0 && len(e.model["p"]["p"].Tokens) > 1 { - pDomainToken = e.model["p"]["p"].Tokens[1] + if pAssertion, ok := e.model["p"]["p"]; ok && len(pAssertion.Tokens) > 1 { + pDomainToken = pAssertion.Tokens[1] } - matchFun := fmt.Sprintf("keyMatch(%s, %s)", rDomainToken, pDomainToken) - if strings.Contains(e.model["m"]["m"].Value, matchFun) { - e.AddNamedDomainMatchingFunc(ptype, "g", util.KeyMatch) + // Only register domain matching function if both tokens are found + if rDomainToken != "" && pDomainToken != "" { + matchFun := fmt.Sprintf("keyMatch(%s, %s)", rDomainToken, pDomainToken) + if strings.Contains(e.model["m"]["m"].Value, matchFun) { + e.AddNamedDomainMatchingFunc(ptype, "g", util.KeyMatch) + } } } } From 5e673359fdf8b07a533f2c900623d77e01fa1a34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 16:33:06 +0000 Subject: [PATCH 5/5] Refactor domain token detection to fix nestif linter error Extract domain matching logic into separate helper functions to reduce nested if complexity from 7 to acceptable levels. This maintains the same functionality while improving code readability. Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- enforcer.go | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/enforcer.go b/enforcer.go index 0a326ecca..f9bab13c5 100644 --- a/enforcer.go +++ b/enforcer.go @@ -496,6 +496,35 @@ func (e *Enforcer) SavePolicy() error { return nil } +// getDomainTokens extracts domain token names from request and policy definitions. +// Returns empty strings if tokens cannot be found. +func (e *Enforcer) getDomainTokens() (rDomainToken, pDomainToken string) { + if rAssertion, ok := e.model["r"]["r"]; ok && len(rAssertion.Tokens) > 1 { + rDomainToken = rAssertion.Tokens[1] + } + if pAssertion, ok := e.model["p"]["p"]; ok && len(pAssertion.Tokens) > 1 { + pDomainToken = pAssertion.Tokens[1] + } + return rDomainToken, pDomainToken +} + +// registerDomainMatchingFunc registers domain matching function if the matcher uses keyMatch for domains. +func (e *Enforcer) registerDomainMatchingFunc(ptype string) { + // Dynamically detect the domain token name from the model definition. + // In RBAC with domains, the domain is typically the second parameter (index 1) + // in both request and policy definitions (e.g., r = sub, dom, obj, act). + // We extract the actual token names to support arbitrary domain parameter names. + rDomainToken, pDomainToken := e.getDomainTokens() + if rDomainToken == "" || pDomainToken == "" { + return + } + + matchFun := fmt.Sprintf("keyMatch(%s, %s)", rDomainToken, pDomainToken) + if strings.Contains(e.model["m"]["m"].Value, matchFun) { + e.AddNamedDomainMatchingFunc(ptype, "g", util.KeyMatch) + } +} + func (e *Enforcer) initRmMap() { for ptype, assertion := range e.model["g"] { if rm, ok := e.rmMap[ptype]; ok { @@ -518,24 +547,7 @@ func (e *Enforcer) initRmMap() { assertion.CondRM = defaultrolemanager.NewConditionalDomainManager(10) e.condRmMap[ptype] = assertion.CondRM } - // Dynamically detect the domain token name from the model definition. - // In RBAC with domains, the domain is typically the second parameter (index 1) - // in both request and policy definitions (e.g., r = sub, dom, obj, act). - // We extract the actual token names to support arbitrary domain parameter names. - var rDomainToken, pDomainToken string - if rAssertion, ok := e.model["r"]["r"]; ok && len(rAssertion.Tokens) > 1 { - rDomainToken = rAssertion.Tokens[1] - } - if pAssertion, ok := e.model["p"]["p"]; ok && len(pAssertion.Tokens) > 1 { - pDomainToken = pAssertion.Tokens[1] - } - // Only register domain matching function if both tokens are found - if rDomainToken != "" && pDomainToken != "" { - matchFun := fmt.Sprintf("keyMatch(%s, %s)", rDomainToken, pDomainToken) - if strings.Contains(e.model["m"]["m"].Value, matchFun) { - e.AddNamedDomainMatchingFunc(ptype, "g", util.KeyMatch) - } - } + e.registerDomainMatchingFunc(ptype) } } }