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.

https://casbin.org/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")
	}
}