From 594897111fe22e41083b4e5d18bddc49976d4661 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 02:47:21 +0530 Subject: [PATCH 1/8] feat(casbin): add support to build upon multi role links Signed-off-by: Kevin --- enforcer.go | 3 +++ model/policy.go | 10 ++++++++++ rbac_api.go | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/enforcer.go b/enforcer.go index fd3f43a8d..33d6e9e50 100644 --- a/enforcer.go +++ b/enforcer.go @@ -175,6 +175,9 @@ func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) er func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) error { e.adapter = adapter + // Global g* asserter + m.AddDef("g", "*", "_, _") + e.model = m m.SetLogger(e.logger) e.model.PrintModel() diff --git a/model/policy.go b/model/policy.go index 875da0901..55851982b 100644 --- a/model/policy.go +++ b/model/policy.go @@ -228,6 +228,16 @@ func (model Model) AddPolicy(sec string, ptype string, rule []string) error { assertion.Policy = append(assertion.Policy, rule) assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1 + if sec == "g" { + assertion, err := model.GetAssertion(sec, "*") + if err != nil { + return err + } else { + assertion.Policy = append(assertion.Policy, rule) + assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec]["*"].Policy) - 1 + } + } + hasPriority := false if _, ok := assertion.FieldIndexMap[constant.PriorityIndex]; ok { hasPriority = true diff --git a/rbac_api.go b/rbac_api.go index fe2a6e7a0..194152f31 100644 --- a/rbac_api.go +++ b/rbac_api.go @@ -324,7 +324,7 @@ func (e *Enforcer) GetImplicitUsersForRole(name string, domain ...string) ([]str // GetPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. // But GetImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. func (e *Enforcer) GetImplicitPermissionsForUser(user string, domain ...string) ([][]string, error) { - return e.GetNamedImplicitPermissionsForUser("p", "g", user, domain...) + return e.GetNamedImplicitPermissionsForUser("p", "*", user, domain...) } // GetNamedImplicitPermissionsForUser gets implicit permissions for a user or role by named policy. From 752ac2f5d23fd2f0978fb883bd7660a888ee84dd Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 03:01:01 +0530 Subject: [PATCH 2/8] feat(casbing): move g* definition inside of load model Signed-off-by: Kevin --- enforcer.go | 3 --- model/model.go | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/enforcer.go b/enforcer.go index 33d6e9e50..fd3f43a8d 100644 --- a/enforcer.go +++ b/enforcer.go @@ -175,9 +175,6 @@ func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) er func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) error { e.adapter = adapter - // Global g* asserter - m.AddDef("g", "*", "_, _") - e.model = m m.SetLogger(e.logger) e.model.PrintModel() diff --git a/model/model.go b/model/model.go index bffb9deeb..99adc8322 100644 --- a/model/model.go +++ b/model/model.go @@ -195,6 +195,11 @@ func (model Model) loadModelFromConfig(cfg config.ConfigInterface) error { for s := range sectionNameMap { loadSection(model, cfg, s) } + + if model.hasSection("g") { + model.AddDef("g", "*", "_, _") + } + ms := make([]string, 0) for _, rs := range requiredSections { if !model.hasSection(rs) { From 7dd8ccb0803aadc44d484d8f83f23a699dee030d Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 03:22:13 +0530 Subject: [PATCH 3/8] fix(casbin): yank off from save policy Signed-off-by: Kevin --- enforcer.go | 9 +++++++++ model/model.go | 4 ---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/enforcer.go b/enforcer.go index fd3f43a8d..b7a564291 100644 --- a/enforcer.go +++ b/enforcer.go @@ -219,6 +219,10 @@ func (e *Enforcer) initialize() { e.autoNotifyWatcher = true e.autoNotifyDispatcher = true e.initRmMap() + + if e.model["g"] != nil { + e.model.AddDef("g", "*", "_, _") + } } // LoadModel reloads the model from the model CONF file. @@ -477,6 +481,11 @@ func (e *Enforcer) SavePolicy() error { if e.IsFiltered() { return errors.New("cannot save a filtered policy") } + + if e.model["g"] != nil && e.model["g"]["*"] != nil { + delete(e.model["g"], "*") + } + if err := e.adapter.SavePolicy(e.model); err != nil { return err } diff --git a/model/model.go b/model/model.go index 99adc8322..ff6ee3d9f 100644 --- a/model/model.go +++ b/model/model.go @@ -196,10 +196,6 @@ func (model Model) loadModelFromConfig(cfg config.ConfigInterface) error { loadSection(model, cfg, s) } - if model.hasSection("g") { - model.AddDef("g", "*", "_, _") - } - ms := make([]string, 0) for _, rs := range requiredSections { if !model.hasSection(rs) { From 68cdb1002672a0bf99e42f1122774297526a3b0d Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 03:35:03 +0530 Subject: [PATCH 4/8] fix(casbin): fix shadowed declaration Signed-off-by: Kevin --- model/policy.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/model/policy.go b/model/policy.go index 55851982b..5e5f98d1f 100644 --- a/model/policy.go +++ b/model/policy.go @@ -229,12 +229,12 @@ func (model Model) AddPolicy(sec string, ptype string, rule []string) error { assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1 if sec == "g" { - assertion, err := model.GetAssertion(sec, "*") + assert, err := model.GetAssertion(sec, "*") if err != nil { return err } else { - assertion.Policy = append(assertion.Policy, rule) - assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec]["*"].Policy) - 1 + assert.Policy = append(assert.Policy, rule) + assert.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec]["*"].Policy) - 1 } } From c8e17983f75b9ed4e3e5fe8e00d0c805bf71fcdf Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 14:32:18 +0530 Subject: [PATCH 5/8] fix(casbin): init rm map after adding def Signed-off-by: Kevin --- enforcer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/enforcer.go b/enforcer.go index b7a564291..2ab0d6651 100644 --- a/enforcer.go +++ b/enforcer.go @@ -218,11 +218,12 @@ func (e *Enforcer) initialize() { e.autoBuildRoleLinks = true e.autoNotifyWatcher = true e.autoNotifyDispatcher = true - e.initRmMap() if e.model["g"] != nil { e.model.AddDef("g", "*", "_, _") } + + e.initRmMap() } // LoadModel reloads the model from the model CONF file. From 771164ef0adfcc380625415b30e0901689416ab7 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 14:33:30 +0530 Subject: [PATCH 6/8] feat(casbin): only add definition once more than one g Signed-off-by: Kevin --- enforcer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enforcer.go b/enforcer.go index 2ab0d6651..eaa93f3c1 100644 --- a/enforcer.go +++ b/enforcer.go @@ -219,7 +219,7 @@ func (e *Enforcer) initialize() { e.autoNotifyWatcher = true e.autoNotifyDispatcher = true - if e.model["g"] != nil { + if e.model["g"] != nil && len(e.model["g"]) > 1 { e.model.AddDef("g", "*", "_, _") } From d7230dafb445bca658aad2ff7430c8a41de1acaa Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 16:42:16 +0530 Subject: [PATCH 7/8] feat(casbin): added tests Signed-off-by: Kevin --- model/policy.go | 2 +- rbac_api.go | 6 ++++++ rbac_api_test.go | 13 +++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/model/policy.go b/model/policy.go index 5e5f98d1f..4915d41be 100644 --- a/model/policy.go +++ b/model/policy.go @@ -228,7 +228,7 @@ func (model Model) AddPolicy(sec string, ptype string, rule []string) error { assertion.Policy = append(assertion.Policy, rule) assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1 - if sec == "g" { + if sec == "g" && len(model["g"]) > 1 { assert, err := model.GetAssertion(sec, "*") if err != nil { return err diff --git a/rbac_api.go b/rbac_api.go index 194152f31..8fa59d5ec 100644 --- a/rbac_api.go +++ b/rbac_api.go @@ -239,6 +239,7 @@ func (e *Enforcer) GetImplicitRolesForUser(name string, domain ...string) ([]str res = append(res, roles...) } + util.ArrayRemoveDuplicates(&res) return res, nil } @@ -311,6 +312,7 @@ func (e *Enforcer) GetImplicitUsersForRole(name string, domain ...string) ([]str } } + util.ArrayRemoveDuplicates(&res) return res, nil } @@ -324,6 +326,10 @@ func (e *Enforcer) GetImplicitUsersForRole(name string, domain ...string) ([]str // GetPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. // But GetImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. func (e *Enforcer) GetImplicitPermissionsForUser(user string, domain ...string) ([][]string, error) { + return e.GetNamedImplicitPermissionsForUser("p", "g", user, domain...) +} + +func (e *Enforcer) GetImplicitPermissionsForUserFromAllRoles(user string, domain ...string) ([][]string, error) { return e.GetNamedImplicitPermissionsForUser("p", "*", user, domain...) } diff --git a/rbac_api_test.go b/rbac_api_test.go index ccf69490f..f840c343a 100644 --- a/rbac_api_test.go +++ b/rbac_api_test.go @@ -15,6 +15,7 @@ package casbin import ( + "fmt" "log" "sort" "testing" @@ -283,6 +284,7 @@ func TestPermissionAPI(t *testing.T) { e, _ = NewEnforcer("examples/rbac_with_multiple_policy_model.conf", "examples/rbac_with_multiple_policy_policy.csv") testGetNamedPermissionsForUser(t, e, "p", "user", [][]string{{"user", "/data", "GET"}}) testGetNamedPermissionsForUser(t, e, "p2", "user", [][]string{{"user", "view"}}) + testGetImplicitPermissionsForUserFromAllRoles(t, e, "alice", [][]string{{"user", "/data", "GET"}, {"admin", "/data", "POST"}}) } func testGetImplicitRoles(t *testing.T, e *Enforcer, name string, res []string) { @@ -334,6 +336,17 @@ func testGetImplicitPermissions(t *testing.T, e *Enforcer, name string, res [][] } } +func testGetImplicitPermissionsForUserFromAllRoles(t *testing.T, e *Enforcer, name string, res [][]string, domain ...string) { + t.Helper() + myRes, _ := e.GetImplicitPermissionsForUserFromAllRoles(name, domain...) + fmt.Println(e.GetNamedImplicitRolesForUser("*", name)) + t.Log("Implicit permissions for ", name, "from all roles", ": ", myRes) + + if !util.Set2DEquals(res, myRes) { + t.Error("Implicit permissions for ", name, ": ", myRes, ", supposed to be ", res) + } +} + func testGetImplicitPermissionsWithDomain(t *testing.T, e *Enforcer, name string, domain string, res [][]string) { t.Helper() myRes, _ := e.GetImplicitPermissionsForUser(name, domain) From 0258be4f89879170defff733a0a7859f9fea36cf Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 10 Apr 2025 17:27:49 +0530 Subject: [PATCH 8/8] feat(casbin): hide * in model.ToText() Signed-off-by: Kevin --- model/model.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/model/model.go b/model/model.go index ff6ee3d9f..d51021ac3 100644 --- a/model/model.go +++ b/model/model.go @@ -393,6 +393,9 @@ func (model Model) ToText() string { if _, ok := model["g"]; ok { s.WriteString("[role_definition]\n") for ptype := range model["g"] { + if ptype == "*" { + continue + } s.WriteString(fmt.Sprintf("%s = %s\n", ptype, model["g"][ptype].Value)) } }