Casbin is an authorization library that supports access control models like ACL, RBAC, ABAC for muti-language.
Before developing Casbin, it is necessary to understand the underlying basic model and how to configure a permission management model that suits your needs.
First, the casbin model are base on PERM (Policy, Request, Effect, Matchers) with following settings:
- sub (subject): The entity accessing the resource, such as a member, user, or device. It represents who is making the request.
- obj (object): The resource being requested, such as a feature, page, or folder. It represents what is being requested.
- act (action): The method or behavior of the request, such as publish, edit, view, disable, etc. It represents how the request is made.
- eft (effect): The result of the policy after defining the sub, obj, and act. It can be set as true, false, or null (indicating allow).
And further, it can be combined with roles to manage roles or multi-tenancy.
Next, we will explain the Casbin model. During the learning process, you can practice and operate in the Casbin official editor.
Request (request_definition)
Setting request model:
r = sub, obj, act
The example of a request that can be sent is as follows:
alice, data1, write
Policy (policy_definition)
After obtaining the request, it is matched against the Policy. The definition is as follows:
p = sub, obj, act, eft
The actual configured Policy is as follows:
p, alice, data1, read
p, bob, data2, write
p, alice, data1, write, deny
p, alice, data1, write, allow
Matchers
According to the Request and Policy, logic matching is performed. For example, the following setting uses exact matching rules, but regular expressions can also be used for writing:
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
Effect (policy_effect)
You can configure expressions to check policies and return results. By default, only the following four policy effects are supported:
Policy Effect | 意義 |
---|---|
some(where (p.left == allow)) | allow-override |
!some(where (p.left == decy)) | deny-override |
some(where (p.left == allow) && !some(where(p.left == deny))) | allow-and-deny |
policy(p.left) |
Regarding the example above, if the input Request is
alice, data1, write
The configured policy is as follows, where Alice is granted both deny and allow permissions for write in data1.
p, alice, data1, read
p, bob, data2, write
p, alice, data1, write, deny
p, alice, data1, write, allow
If the effect is set to the following rule, then as long as one allow is matched, the request will be granted.
e = some(where (p.eft == allow))
If you want to check if deny exists and set it to false, you can configure the following rule.
some(where (p.left == allow) && !some(where(p.left == deny)))
Role Definiction
After understanding PREM, you can further configure the definition of Roles.
g = _, _ is used for accessing users and roles. g = _, _, _ is used for accessing users, roles, and merchants, suitable for multi-tenant models.
Setting the model:
[roledefiniction]
g = _, _
The actual data is as follows: set Alice’s role as data2_admin, and then define the policy for roles.
p, data1_reader, data1, read
p, data2_admin, data2, write
p, data2_admin, data2, read
g, bob, data1_reader
g, alice, data2_admin
Example
Following are step-by-step and reference from casbin get started doc:
Install casbin
go get github.com/casbin/casbin/v2
Create model (model.conf)
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
Create policy (policy.csv)
p, adam, data1, read
Checking permissions by e.Enforce
sub := "adam"
obj := "data1"
act := "read"
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// handle err
}
if ok == true {
// permit alice to read data1
} else {
// deny the request, show an error
}
or, using e.BatchEnforce
to bash check policy
results, err := e.BatchEnforce([][]interface{}{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"jack", "data3", "read"}})
Get the role from casbin
roles, err := e.GetRolesForUser("alice")
Adapter Example
Install casbin for permission control
go get github.com/casbin/casbin/v2
Gorm adapter to save the policy to DB
go get github.com/casbin/gorm-adapter/v3
Example:
package main
import (
casbin "github.com/casbin/casbin/v2"
gormadapter github.com/casbin/gorm-adapter/v3
...
)
func main() {
...
//Casbin
var InfoDb utils.InfoDb //gorm.DB
db, _ := InfoDb.InitDB()
a, err := gormadapter.NewAdapterByDB(db)
if err != nil {
panic("error: adapter:" + err.Error())
}
e, err := casbin.NewEnforcer("config/casbin_lib/model.conf", a)
if err != nil {
panic("error: model:" + err.Error())
}
// Load the policy from DB.
e.LoadPolicy()
added, err := e.AddPolicy("adam", "data1", "read")
fmt.Println(added, err)
sub := "adam" // the user that wants to access a resource.
obj := "data1" // the resource that is going to be accessed.
act := "write" // the operation that the user performs on the resource.
//Modify the policy
e.AddPolicy(sub, obj, act)
// Check the permission.
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// handle err
fmt.Println(err, "error happened")
}
if ok {
// permit alice to read data1
fmt.Println("permit alice to read data1")
} else {
// deny the request, show an error
fmt.Println("deny the request, show an error")
}
//Save the policy to database
e.SavePolicy()
// Modify the policy.
// e.RemovePolicy(...)
// Check the permission.
ok, err = e.Enforce(sub, obj, act)
if err != nil {
// handle err
fmt.Println(err, "error happened")
}
if ok {
// permit alice to read data1
fmt.Println("permit alice to read data1")
} else {
// deny the request, show an error
fmt.Println("deny the request, show an error")
}
}