role-based access control (RBAC) for multi domain/tenant implementation in Golang (casbin wrapper)
gate is go package for RBAC with multi tenant/domain, this one is implement base on casbin, some feature that useful for multi domain was added.
- casbin model use to set casbin policy pattern (gate fix this one for easy to use)
- casbin policy use to store policy rule in casbin adapter (ex. domain has user and role and role in domain has permission) you can see example below
//permission is contain module name and action name
//policy type p -> role writer in domain1 has write permission in module data1
p, writer, domain1, data1, write
//policy type p -> role visitor3 in domain3 has all permission in module data3
p, visitor3, domain3, data3, *
//policy type g -> alice is admin role in domain1 (notice : admin role has all permission in this domain)
g, alice, admin, domain1
//policy type g -> alice is visitor3 role in domain3
g, alice, visitor3, domain3
if you need more information please follow the casbin document casbin document
- support single domain and multi domain
- assign , revoke permission to role
- assign , revoke role to user
- check permission
- check admin role
- list all role
- list all permission in role
- list all user with role
- list role by user
- list permission by user
- count module usage in domain (some application need to know module usage for limitation)
- assign role to user with module usage license limitation in domain (coming soon)
- assing multiple role into one user (not test yet)
- auto update when package dependency is update (we use dependabot to auto pull request for new package version)
- code quality scanning (we use sonarcloud to detect code smell , bug , vulnerability , code coverage, etc.)
go get -u github.com/panapol-p/gate
you can use another adapter to store casbin policy (mongo,postgresql,mysql,aws s3 or etc.) by follow casbin adapter casbin adapter
notice : some adapter is not support auto-save
when you update policy or rule you must save and load policy by using g.Load()
or g.Save()
notice2 : if you need to use some feature from native casbin you can called by using g.E
this one is natice casbin enforcer
notice3 : if you build aplication that support scalable mode , please don't forget to set casbin watcher to trigger event when some node has policy update, another node will know and use g.Load()
for update policy each node otherwise all of your node will be not use same policy rules
func main(){
//set adapter
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
//create new gate
g, err := NewGate(a)
}
ex. usecase -> manage user role and permission in user management
notice : you can use userID (like "1234-1234-234-345") instead of user name (like "bella"), roleID too , we try to explain by using name for easy to understand and dont forget user and role must be unique in that domain if not it will be overwrite or add more policy
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
// assign permission:write for module:resource1 to role:dep1 in domain:domain5
err = g.AssignPermissionToRole("domain5", "dep1", "resource1", "write")
// assign role:dep1 to user:bella in domain:domain5
err = g.AssignRoleToUser("domain5", "dep1", "bella")
}
ex. usecase -> manage user role and permission in user management
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
// revoke permission:write for module:resource1 to role:dep1 in domain:domain5
err = g.RevokerPermissionToRole("domain5", "dep1", "resource1", "write")
// revoke role:dep1 to user:bella in domain:domain5
err = g.RevokeRoleToUser("domain5", "dep1", "bella")
}
ex. usecase -> middleware for http request
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//user:bella has permission:write for module:resource1 in domain:domain5 or not?
h, err = g.HasPermission("domain5", "bella", "resource1", "write")
//output : true / false
}
ex. usecase -> middleware for admin http request
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//user:alice is role:admin in domain:domain1 or not?
isAdmin := g.IsAdmin("domain1", "alice")
//output : true / false
}
ex. usecase -> show all role in user management page
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//list all role in domain:domain1
r1 := g.GetRoles("domain1")
//output : domain1 has role:admin , writer , reader , visitor
//output : []string{"admin", "writer", "reader", "visitor"}
}
ex. usecase -> show all user with role in user management page
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//list all user with role in domain:domain1
u := g.GetAllUsersRole("domain1")
//output : domain1 has user:alice with role:admin and user:bob with role:reader
//output []UserRole{ {"alice", "admin"}, {"bob", "reader"}}
}
ex. usecase -> manage permission in role in user management page
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//list all permission for role:writer in domain:domain1
//notice : if you use admin it will be return all permission in this domain
p := g.GetPermissionsForRole("domain1", "writer")
//output : combine module and permission(action) with dot
//output : role:wrtiter in domain1 has read and write in module:data1
//output : []string{"data1.read", "data1.write"}
}
ex. usecase -> get role by user
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//list all role by user user:alice in domain:domain1
r := g.GetUserRole("domain2", "alice")
//output : []string{"admin"}
}
ex. usecase -> some application need to count the number of module usage
func main(){
a := fileadapter.NewAdapter("./testdata/rbac_with_domains_policy.csv")
g, err := NewGate(a)
//list all module with number of usage in domain:domain1
m := g.CountModule("domain1")
//output : domain1 use module:data2 1 license and use module:data3 2 licenses
//output : map[string]int{ "data2": 1, "data3": 2}
}
ex. usecase -> some application need to limit the number of module usage
- coming soon
Distributed under the MIT License. See license for more information.
Contributions are welcome! Feel free to check our open issues.