Repository: casbin/casbin
Pull request #746 (2021-03-30 08:35:38)

feat: Add GetDomainsForUser() API
Fix: https://github.com/casbin/casbin/issues/744

hsluoyz: @thoraj please review.
hsluoyz: @thoraj can you just see if the test case output is expected for the model and policy? If that's good to go, we will port the exactly the same logic to .NET

![image](https://user-images.githubusercontent.com/3787410/113015021-b52f1c80-91af-11eb-83c3-45cff5c6f9c6.png)


Patch:
diff --git a/examples/rbac_with_domains_policy2.csv b/examples/rbac_with_domains_policy2.csv
new file mode 100644
index 0000000..baa06f0
--- /dev/null
+++ b/examples/rbac_with_domains_policy2.csv
@@ -0,0 +1,9 @@
+p, admin, domain1, data1, read
+p, admin, domain1, data1, write
+p, admin, domain2, data2, read
+p, admin, domain2, data2, write
+p, user, domain3, data2, read
+g, alice, admin, domain1
+g, alice, admin, domain2
+g, bob, admin, domain2
+g, bob, user, domain3
diff --git a/rbac/default-role-manager/role_manager.go b/rbac/default-role-manager/role_manager.go
index 4ef1966..5815c84 100644
--- a/rbac/default-role-manager/role_manager.go
+++ b/rbac/default-role-manager/role_manager.go
@@ -300,6 +300,28 @@ func (rm *RoleManager) PrintRoles() error {
 	return nil
 }
 
+// GetDomains gets domains that a user has
+func (rm *RoleManager) GetDomains(name string) ([]string, error) {
+	var domains []string
+	for domain := range rm.domains {
+		if rm.hasAnyRole(name, domain)  {
+			domains = append(domains, domain)
+		}
+	}
+	domains = util.RemoveDuplicateElement(domains)
+	return domains, nil
+}
+
+func (rm *RoleManager) hasAnyRole(name string, domain string) bool {
+	patternDomain := rm.getPatternDomain(domain)
+	for _, domain := range patternDomain {
+		if rm.roles.hasRole(domain, name, rm.matchingFunc) {
+			return true
+		}
+	}
+	return false
+}
+
 // Roles represents all roles in a domain
 type Roles struct {
 	sync.Map
diff --git a/rbac/role_manager.go b/rbac/role_manager.go
index c626cbc..008544c 100644
--- a/rbac/role_manager.go
+++ b/rbac/role_manager.go
@@ -35,6 +35,8 @@ type RoleManager interface {
 	// GetUsers gets the users that inherits a role.
 	// domain is a prefix to the users (can be used for other purposes).
 	GetUsers(name string, domain ...string) ([]string, error)
+	// GetDomains gets domains that a user has
+	GetDomains(name string) ([]string, error)
 	// PrintRoles prints all the roles to log.
 	PrintRoles() error
 	// SetLogger sets role manager's logger.
diff --git a/rbac_api.go b/rbac_api.go
index 485843c..1bfa990 100644
--- a/rbac_api.go
+++ b/rbac_api.go
@@ -251,3 +251,16 @@ func (e *Enforcer) GetImplicitUsersForPermission(permission ...string) ([]string
 
 	return res, nil
 }
+
+// GetDomainsForUser gets all domains
+func (e *Enforcer) GetDomainsForUser(user string) ([]string, error) {
+	var domains []string
+	for _, rm := range e.rmMap {
+		domain, err := rm.GetDomains(user)
+		if err != nil {
+			return nil, err
+		}
+		domains = append(domains, domain...)
+	}
+	return domains, nil
+}


Test patch:
diff --git a/model_test.go b/model_test.go
index a079bfa..9802c83 100644
--- a/model_test.go
+++ b/model_test.go
@@ -347,6 +347,9 @@ func (rm *testCustomRoleManager) GetRoles(name string, domain ...string) ([]stri
 func (rm *testCustomRoleManager) GetUsers(name string, domain ...string) ([]string, error) {
 	return []string{}, nil
 }
+func (rm *testCustomRoleManager) GetDomains(name string) ([]string, error) {
+	return []string{}, nil
+}
 func (rm *testCustomRoleManager) PrintRoles() error           { return nil }
 func (rm *testCustomRoleManager) SetLogger(logger log.Logger) {}
 
diff --git a/rbac_api_with_domains_test.go b/rbac_api_with_domains_test.go
index cbecc5a..ad31bef 100644
--- a/rbac_api_with_domains_test.go
+++ b/rbac_api_with_domains_test.go
@@ -15,6 +15,7 @@
 package casbin
 
 import (
+	"sort"
 	"testing"
 
 	"github.com/casbin/casbin/v2/util"
@@ -189,3 +190,23 @@ func TestPermissionAPIInDomain(t *testing.T) {
 	testGetPermissionsInDomain(t, e, "admin", "domain2", [][]string{{"admin", "domain2", "data2", "read"}, {"admin", "domain2", "data2", "write"}})
 	testGetPermissionsInDomain(t, e, "non_exist", "domain2", [][]string{})
 }
+
+func testGetDomainsForUser(t *testing.T, e *Enforcer, res []string, user string) {
+	t.Helper()
+	myRes, _ := e.GetDomainsForUser(user)
+
+	sort.Strings(myRes)
+	sort.Strings(res)
+
+	if !util.ArrayEquals(res, myRes) {
+		t.Error("domains for user: ", user, ": ", myRes, ",  supposed to be ", res)
+	}
+}
+
+func TestGetDomainsForUser(t *testing.T) {
+	e, _ := NewEnforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy2.csv")
+
+	testGetDomainsForUser(t, e, []string{"domain1", "domain2"}, "alice")
+	testGetDomainsForUser(t, e, []string{"domain2", "domain3"}, "bob")
+	testGetDomainsForUser(t, e, []string{"domain3"}, "user")
+}