# Casbin – easy about difficult things in authorization

Introduction

Hi all!

Today we will talk about how to make your authorization system reliable, flexible and easy to manage using the library Casbin. If you've ever wondered how to set up permissions in your application, but were intimidated by the complexity of the process, then this article is for you. We'll walk you through the basic concepts and show that setting up authorization may not be such a difficult task.

Casbin is a powerful and easily extensible access control library that supports various access control models. Here are some examples:

  • RBAC Role-Based Access Control is a role-based model where access rights are assigned to roles rather than to specific users.

  • ABAC Attribute-Based Access Control is an attribute-based model where decisions are made based on attributes of the user, resource, action, and context.

  • ACL (Access Control List) – an access control list, where each resource is associated with a list of users and their rights.

Today we will focus our attention on RBAC, one of the most popular and understood models.

What is RBAC

RBAC Role-Based Access Control, or role-based access control, is an access control model in which access rights are assigned not to specific users, but to roles. Users gain rights by taking on specific roles. This model greatly simplifies the management of access rights in the system, especially when there are many users and their rights change frequently.

Visual representation of RBAC (I haven't figured out how to md in the editor, insert a diagram from your local machine xD, so you can look at the diagram on my google drive)
https://drive.google.com/file/d/1kWD5Xds3e3jZh3fkqECBNuqvqLtRP7xu/view?usp=sharing

The diagram shows:

  1. Roles:

    • A role is a set of access rights.

    • For example, role userwhich can perform CRUD with your resources and read the resources of other users.

  2. Users:

    • A user is any entity (person or process) that needs to have access to resources.

    • Users are assigned one or more roles. For example, user Bob may have a role user.

  3. Rights:

    • Rights define what actions can be performed on what resources.

    • For example, right read can allow reading of data from their own or other people's posts.

  4. Contacts:

    • Associations between roles and rights determine which rights belong to which roles.

    • Associations between users and roles determine which roles are assigned to which users.

Creating an RBAC Model

Creating an RBAC Model with Groupings

Casbin uses a model file to define the access control structure. Let's create a model file rbac-model.conf:

rbac-model.conf

[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 = r.act == p.act && ; проверяем, что action совпадает как в политике, так и в кортеже, который мы получаем в request.
    g(r.sub, p.sub) && ; это внутрення функция, которая сопоставляет sub с request и сравнивает с ролями пользователя в политики. 
    (g2(p.obj, r.obj) || p.obj == "*") ; мы проверяем, что если req.obj != * (доступ ко всем ресурсам), то выполняется внутрення функция сравнения группировок ресурсов пользователя в политики.
  • request_definition: Defines the request format (who, what, what action).

  • policy_definition: Defines the policy format.

  • role_definition: Defines the format of roles and groupings (groups g and g2).

  • policy_effect: Determines the effect of the policy (allow or deny).

  • matchers: Defines the logic for matching a request to policies, including additional groupings.

Essentially we see what we have r (role_definition) – the tuple that we will transmit from the request is p (policy_definition) – a policy that we will talk about in the next paragraph.

We announce g And g2 (role_definition) – this is a feature RBAC. In essence, these are just groups; an example will also be in the block about politics.

g is responsible for the “user” roles. For example we have a role owner and he may have several types of actions available to him – read, edit, delete.

g2 is a grouping for resources. Let's say the user Bob there can be multiple resources that will be stored in a group Bob-Resources.

Chapter policy_effect defines how Casbin must process the result of the policy match. It is responsible for what happens when one or more policy rules are applied to an access request. In simple terms, it decides whether to allow or deny an action based on the rules that have been found. Thus, policy_effect specifies that if at least one rule allows access, the request will be approved.

Well, the verification algorithm itself – matchers. We can configure them as we please, but the example shows an example for RBAC models.

Note It is also worth noting the order of comparisons in matchers. This is done in order to increase speed. If we have an error while checking for an action, we will not have to perform unnecessary functions.

Setting up access policies

Now let's create a file with policies policy.csvin which we define access and grouping rights:

policy.csv

<!-- определяем пользователей, даем доступ к группе ресурса и доступ к ролям -->
p, alice, admin-res, admin
p, bob, bob-res, user
p, bob, guest-res, guest
p, clare, guest-res, guest

<!-- создаем роль crud, чтобы не дублировать имена действий -->
g, crud, create
g, crud, read
g, crud, update
g, crud, delete

<!-- выдаем ролям действия -->
g, admin, crud
g, user, crud
g, guest, read

<!-- создаем группы ресурсов -->
g2, all-resources
g2, admin-res, all-resource
g2, bob-posts
g2, bob-friends
g2, bob-groups
g2, bob-res, bob-posts
g2, bob-res, bob-groups
g2, bob-res, groups
g2, guest-res, all-resource
  • p: Define rights for roles. For example, role admin has crud rights to admin-res.

  • g: Defines role relationships. For example, alice is adminA bob is user.

  • g2: Defines resource groupings. For example, all-resources belong to the group admin-res And guest-res

As promised in the previous block, I will explain what and how.

First of all, policies are defined – they are very easy to understand, essentially in letters p we indicate that this will be policy. Next, we indicate the user name (in a real application, this could be a token or identifier), then we indicate the resources to which our user will have access, and we also indicate the actions that the user can perform with the resources to which he has access.

And so, then we see the same role_definition - g. We create a grouping of actions crud, in essence, this can be perceived as a variable – we create a variable in order to reuse it later. To avoid assigning the same actions to each user every time, we will assign a grouping to them. Now I’ll show you why it’s done this way: instead of doing it like this

p, bob, bob-res, create
p, bob, bob-res, read
p, bob, bob-res, update
p, bob, bob-res, delete

we have created a grouping and can assign it to any number of users

p, den, res, crud
p, emily, res, crud
p, frank, res, crud

The situation is exactly the same with groupings for resources, because we can have common resources, we can have private resources, and there can be a lot of them, so it’s better to divide them into groups and subgroups and issue them to specific users.

Casbin integration into the application

Installation and configuration of Casbin

Let's create a directory for the project

mkdir casbin-rbac
cd casbin-rbac

Initialize the application and install the dependency

go mod init casbinrbac
go get github.com/casbin/casbin/v2
package main

import (
    "fmt"
    
    "github.com/casbin/casbin/v2"
    "github.com/casbin/casbin/v2/persist/file-adapter"
)

func main() {
    // так же есть возможность подключить БД адаптеры, смотреть ниже
    adapter := fileadapter.NewAdapter("policy.csv")

    // enforcer это основной компонент, который отвечает за
    // 1. Загрузка модели и политик - загружает модель (rbac-model.conf) и (policy.csv)
    // 2. Принятие решений о доступе - принимает запросы на доступ 
    //      и сопоставляет их с политиками для принятия решения, разрешить или запретить доступ
    // 3. Управление ролями и пользователями - Позволяет добавлять и удалять пользователей,
    //      назначать роли и управлять правами доступа
    enforcer, err := casbin.NewEnforcer("rbac-model.conf", a)

    var (
        sub = "bob"
        obj = "bob-posts"
        act = "read"
    )

    isAuth, _ := enforcer.Enforce(sub, obj, act)
    if ok {
        fmt.Printf("sub %s successfully authorized to %s resource, action - %s", sub, obj, act)
    } else {
        fmt.Printf("sub %s not-authorized to %s resource, action - %s", sub, obj, act)
    }
}

Let's run our application and see what comes out of it:

go run main.go
`casbin` also supports other types of adapters, for example `postgres`:

We will use the official tool

go get github.com/casbin/casbin-pg-adapter
Database creation and migrations

The database must be called casbin and have a table casbin_rule

First, raise postgres image in docker:

docker run --name casbin -p 5432:5432 -e POSTGRES_PASSWORD=casbin_path -e POSTGRES_USER=casbin_user -e POSTGRES_DB=casbin -d postgres

You can use any migration tool, I'll just leave it at sql:

create extension if not exists "uuid-ossp";;

create table if not exists casbin_rule
(
    id uuid default gen_random_uuid() primary key,
    ptype text not null,
    v0 text not null,
    v1 text not null,
    v2 text,
    v3 text,
    v4 text,
    v5 text
);

insert into casbin_rule (ptype, v0, v1, v2) values
    ('p', 'alice', 'admin-res', 'admin'),
    ('p', 'bob', 'bob-res', 'user'),
    ('p', 'bob', 'guest-res', 'guest'),
    ('p', 'clare', 'guest-res', 'guest'),
    ('g', 'crud', 'create', ''),
    ('g', 'crud', 'read', ''),
    ('g', 'crud', 'update', ''),
    ('g', 'crud', 'delete', ''),
    ('g', 'admin', 'crud', ''),
    ('g', 'user', 'crud', ''),
    ('g', 'guest', 'read', ''),
    ('g2', 'all-resources', '', ''),
    ('g2', 'admin-res', 'all-resources', ''),
    ('g2', 'bob-posts', '', ''),
    ('g2', 'bob-friends', '', ''),
    ('g2', 'bob-groups', '', ''),
    ('g2', 'bob-res', 'bob-posts', ''),
    ('g2', 'bob-res', 'bob-friends', ''),
    ('g2', 'bob-res', 'bob-groups', ''),
    ('g2', 'guest-res', 'all-resources', ''),

The table entry is only needed for our example application. Complete these migrations and you are good to go.

Let's create an adapter:

package adapter

import (
	"os"

	pgadapter "github.com/casbin/casbin-pg-adapter"
	"github.com/casbin/casbin/v2/persist"
)

// Casbin.Adapter должен имплементировать интерфейс persist.Adapter,
// который должен реализовать методы для работы с политиками:

/**
type Adapter interface {
	// LoadPolicy loads all policy rules from the storage.
	LoadPolicy(model model.Model) error
	// SavePolicy saves all policy rules to the storage.
	SavePolicy(model model.Model) error

	// AddPolicy adds a policy rule to the storage.
	// This is part of the Auto-Save feature.
	AddPolicy(sec string, ptype string, rule []string) error
	// RemovePolicy removes a policy rule from the storage.
	// This is part of the Auto-Save feature.
	RemovePolicy(sec string, ptype string, rule []string) error
	// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
	// This is part of the Auto-Save feature.
	RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error
}
*/

func NewPgCasbinAdapter() (persist.Adapter, error) {
    dsn := os.Getenv("PG_DSN")
	return pgadapter.NewAdapter(dsn)
}

Of course, this is a simple example of how authorization takes place.

Managing roles and users

Casbin makes it easy to manage roles and users on your system. With it, you can add, delete, and change roles, as well as assign them to users. Let's look at the basic operations you can perform to manage roles and users.

Adding users to roles

To add a user to a role, you can use the method AddGroupingPolicy. This allows you to associate users with roles.

package main

import (
	"fmt"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	"github.com/casbin/casbin/v2/persist/file-adapter"
)

func main() {
	e, _ := casbin.NewEnforcer("rbac-model.conf", "policy.csv")

	// Добавление пользователя к роли
	e.AddGroupingPolicy("charlie", "user")
}

In this example we are adding a user charlie to the role user.

Removing users from roles

To remove a user from a role, you can use the method RemoveGroupingPolicy.

// Удаление пользователя из роли
e.RemoveGroupingPolicy("charlie", "user")

This method will remove the connection between the user charlie and role user.

Adding permissions to roles

To add rights to a role, you can use the method AddPolicy.

// Добавление прав к роли
e.AddPolicy("editor", "data2", "write")

This method adds a rule that the editor role has write permission on the data2 resource.

Removing rights from roles

To remove rights from a role, you can use the method RemovePolicy.

// Удаление прав у роли
e.RemovePolicy("editor", "data2", "write")

This method removes the rule that the editor role has write permission on the data2 resource.

Checking roles and rights

You can check which roles the user belongs to and what rights the role has.

// Получение ролей пользователя
roles, _ := e.GetRolesForUser("alice")

// Получение прав роли
permissions, _ := e.GetPermissionsForUser("admin")

These methods allow you to find out which roles are assigned to the user alice and what rights does the role have? admin.

Full example

Let's put it all together in one example.

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	"github.com/casbin/casbin/v2/persist/file-adapter"
)

func main() {
	// Загрузка модели и политик
	e, _ := casbin.NewEnforcer("rbac-model.conf", "policy.csv")

	// Добавление пользователя к роли
	e.AddGroupingPolicy("charlie", "user")

	// Удаление пользователя из роли
	e.RemoveGroupingPolicy("charlie", "user")

	// Добавление прав к роли
	e.AddPolicy("editor", "data2", "write")

	// Удаление прав у роли
	e.RemovePolicy("editor", "data2", "write")

	// Получение ролей пользователя
	roles, _ := e.GetRolesForUser("alice")

	// Получение прав роли
	permissions, _ := e.GetPermissionsForUser("admin")
}

Conclusion

This brings us to the end of a short tour of CasbinI hope that now you can simplify your work with access to resources and maybe even be able to bring this idea to your team and get some goodies for it.

In general, this is a very simple topic and quite easy to understand. Hope. that I could help someone.

Also Casbin supports other languages ​​such as:

  • php

  • nodejs

  • .net

  • python

  • rust

  • java

In the coming days I plan to shoot a training video on YouTube, where I will make a small API and I’ll show you with examples how to use a role model RBACso if anyone is interested in this, then I suggest going to my channelthere I will soon announce all the information.

Github

Telegram

Instagram

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *