Repository: casbin/casbin (go)

Issue:
[Question] How to get permissions while using resource roles
**Want to prioritize this issue? Try:**

[![issuehunt-to-marktext](https://github.com/BoostIO/issuehunt-materials/raw/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/r/casbin/casbin)

------

**What's your scenario? What do you want to achieve?**
Your answer here

**Your model:**

```ini
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _
g2 = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act
```

**Your policy:**

```
p, alice, data_group, write

g, alice, data_group_admin
g2, data1, data_group
g2, data2, data_group
```

**Your request(s):**

If I want to get all permissions for user alice and resource data1, it doesn't work  

```go
enforcer.GetFilteredPolicy(0, "alice", "data1")
expected: [["alice", "data1", "write"]...]
actual: []

enforcer.GetImplicitPermissionsForUser("alice") 
expected: [["alice", "data1", "write"]...]
actual: [["alice", "data_group", "write"]...]
```

Is there any API supporte this action?

Pull request description:
feat: add named g support to `GetNamedImplicitPermissionsForUser`
fix #1360 

This PR also fixes an unexpect logic change in #1405 

Patch:
diff --git a/examples/rbac_with_multiple_policy_model.conf b/examples/rbac_with_multiple_policy_model.conf
index 6a7fb09..d4581ac 100644
--- a/examples/rbac_with_multiple_policy_model.conf
+++ b/examples/rbac_with_multiple_policy_model.conf
@@ -13,4 +13,5 @@ m = g(r.user, p.role) && r.thing == p.thing && r.action == p.action
 m2 = g(r.user, p2.role) && r.action == p.action
 
 [role_definition]
-g = _,_
\ No newline at end of file
+g = _,_
+g2 = _,_
\ No newline at end of file
diff --git a/examples/rbac_with_multiple_policy_policy.csv b/examples/rbac_with_multiple_policy_policy.csv
index 18e9cbc..0afb588 100644
--- a/examples/rbac_with_multiple_policy_policy.csv
+++ b/examples/rbac_with_multiple_policy_policy.csv
@@ -5,4 +5,5 @@ p2, user, view
 p2, admin, create
 
 g, admin, user
-g, alice, admin
\ No newline at end of file
+g, alice, admin
+g2, alice, user
\ No newline at end of file
diff --git a/rbac_api.go b/rbac_api.go
index 3e6e2f8..fe2a6e7 100644
--- a/rbac_api.go
+++ b/rbac_api.go
@@ -229,28 +229,53 @@ func (e *Enforcer) HasPermissionForUser(user string, permission ...string) (bool
 // GetRolesForUser("alice") can only get: ["role:admin"].
 // But GetImplicitRolesForUser("alice") will get: ["role:admin", "role:user"].
 func (e *Enforcer) GetImplicitRolesForUser(name string, domain ...string) ([]string, error) {
-	res := []string{}
+	var res []string
 
-	for _, rm := range e.rmMap {
-		roleSet := make(map[string]bool)
-		roleSet[name] = true
-		q := make([]string, 0)
-		q = append(q, name)
+	for v := range e.rmMap {
+		roles, err := e.GetNamedImplicitRolesForUser(v, name, domain...)
+		if err != nil {
+			return nil, err
+		}
+		res = append(res, roles...)
+	}
 
-		for len(q) > 0 {
-			name := q[0]
-			q = q[1:]
+	return res, nil
+}
 
-			roles, err := rm.GetRoles(name, domain...)
-			if err != nil {
-				return nil, err
-			}
-			for _, r := range roles {
-				if _, ok := roleSet[r]; !ok {
-					res = append(res, r)
-					q = append(q, r)
-					roleSet[r] = true
-				}
+// GetNamedImplicitRolesForUser gets implicit roles that a user has by named role definition.
+// Compared to GetImplicitRolesForUser(), this function retrieves indirect roles besides direct roles.
+// For example:
+// g, alice, role:admin
+// g, role:admin, role:user
+// g2, alice, role:admin2
+//
+// GetImplicitRolesForUser("alice") can only get: ["role:admin", "role:user"].
+// But GetNamedImplicitRolesForUser("g2", "alice") will get: ["role:admin2"].
+func (e *Enforcer) GetNamedImplicitRolesForUser(ptype string, name string, domain ...string) ([]string, error) {
+	var res []string
+
+	rm := e.GetNamedRoleManager(ptype)
+	if rm == nil {
+		return nil, fmt.Errorf("role manager %s is not initialized", ptype)
+	}
+	roleSet := make(map[string]bool)
+	roleSet[name] = true
+	q := make([]string, 0)
+	q = append(q, name)
+
+	for len(q) > 0 {
+		name := q[0]
+		q = q[1:]
+
+		roles, err := rm.GetRoles(name, domain...)
+		if err != nil {
+			return nil, err
+		}
+		for _, r := range roles {
+			if _, ok := roleSet[r]; !ok {
+				res = append(res, r)
+				q = append(q, r)
+				roleSet[r] = true
 			}
 		}
 	}
@@ -299,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", user, domain...)
+	return e.GetNamedImplicitPermissionsForUser("p", "g", user, domain...)
 }
 
 // GetNamedImplicitPermissionsForUser gets implicit permissions for a user or role by named policy.
@@ -311,14 +336,14 @@ 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) {
+func (e *Enforcer) GetNamedImplicitPermissionsForUser(ptype string, gtype string, user string, domain ...string) ([][]string, error) {
 	permission := make([][]string, 0)
-	rm := e.GetRoleManager()
+	rm := e.GetNamedRoleManager(gtype)
 	if rm == nil {
-		return nil, fmt.Errorf("role manager is not initialized")
+		return nil, fmt.Errorf("role manager %s is not initialized", gtype)
 	}
 
-	roles, err := e.GetImplicitRolesForUser(user, domain...)
+	roles, err := e.GetNamedImplicitRolesForUser(gtype, user, domain...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/rbac_api_synced.go b/rbac_api_synced.go
index 69b6295..7b10e56 100644
--- a/rbac_api_synced.go
+++ b/rbac_api_synced.go
@@ -182,10 +182,10 @@ func (e *SyncedEnforcer) GetImplicitPermissionsForUser(user string, domain ...st
 //
 // 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 *SyncedEnforcer) GetNamedImplicitPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) {
+func (e *SyncedEnforcer) GetNamedImplicitPermissionsForUser(ptype string, gtype string, user string, domain ...string) ([][]string, error) {
 	e.m.RLock()
 	defer e.m.RUnlock()
-	return e.Enforcer.GetNamedImplicitPermissionsForUser(ptype, user, domain...)
+	return e.Enforcer.GetNamedImplicitPermissionsForUser(ptype, gtype, user, domain...)
 }
 
 // GetImplicitUsersForPermission gets implicit users for a permission.


Test patch:
diff --git a/rbac_api_test.go b/rbac_api_test.go
index 10494a5..ccf6949 100644
--- a/rbac_api_test.go
+++ b/rbac_api_test.go
@@ -344,9 +344,9 @@ func testGetImplicitPermissionsWithDomain(t *testing.T, e *Enforcer, name string
 	}
 }
 
-func testGetNamedImplicitPermissions(t *testing.T, e *Enforcer, ptype string, name string, res [][]string) {
+func testGetNamedImplicitPermissions(t *testing.T, e *Enforcer, ptype string, gtype string, name string, res [][]string) {
 	t.Helper()
-	myRes, _ := e.GetNamedImplicitPermissionsForUser(ptype, name)
+	myRes, _ := e.GetNamedImplicitPermissionsForUser(ptype, gtype, name)
 	t.Log("Named implicit permissions for ", name, ": ", myRes)
 
 	if !util.Set2DEquals(res, myRes) {
@@ -379,8 +379,11 @@ func TestImplicitPermissionAPI(t *testing.T) {
 
 	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"}})
+	testGetNamedImplicitPermissions(t, e, "p", "g", "alice", [][]string{{"user", "/data", "GET"}, {"admin", "/data", "POST"}})
+	testGetNamedImplicitPermissions(t, e, "p2", "g", "alice", [][]string{{"user", "view"}, {"admin", "create"}})
+
+	testGetNamedImplicitPermissions(t, e, "p", "g2", "alice", [][]string{{"user", "/data", "GET"}})
+	testGetNamedImplicitPermissions(t, e, "p2", "g2", "alice", [][]string{{"user", "view"}})
 }
 
 func TestImplicitPermissionAPIWithDomain(t *testing.T) {