# Casbin — легко о сложном в авторизации

от автора

Введение

Всем привет!

Сегодня мы поговорим о том, как сделать вашу систему авторизации надежной, гибкой и простой в управлении с помощью библиотеки Casbin. Если вы когда-нибудь задумывались о том, как настроить права доступа в своем приложении, но вас пугала сложность этого процесса, то эта статья для вас. Мы разберем основные понятия и покажем, что настройка авторизации может быть не такой уж и сложной задачей.

Casbin — это мощная и легко расширяемая библиотека для управления доступом, которая поддерживает различные модели контроля доступа. Вот несколько примеров:

  • RBAC (Role-Based Access Control) — модель, основанная на ролях, где права доступа назначаются ролям, а не конкретным пользователям.

  • ABAC (Attribute-Based Access Control) — модель, основанная на атрибутах, где решения принимаются на основе атрибутов пользователя, ресурса, действия и контекста.

  • ACL (Access Control List) — список управления доступом, где каждому ресурсу сопоставляется список пользователей и их прав.

Сегодня мы сосредоточим наше внимание на RBAC, одной из самых популярных и понятных моделей.

Что такое RBAC

RBAC (Role-Based Access Control), или управление доступом на основе ролей, — это модель контроля доступа, в которой права доступа назначаются не конкретным пользователям, а ролям. Пользователи получают права, принимая на себя определенные роли. Эта модель значительно упрощает управление правами доступа в системе, особенно когда пользователей много и их права часто меняются.

Визуальное представление RBAC (я не разобрался как в md редакторе на забре вставить диаграмму с локальной машины xD, потому можете посмотреть диаграму на моем google drive)
https://drive.google.com/file/d/1kWD5Xds3e3jZh3fkqECBNuqvqLtRP7xu/view?usp=sharing

На диаграмме показаны:

  1. Роли:

    • Роль представляет собой набор прав доступа.

    • Например, роль user, которая может выполнять CRUD с своими ресурсами и читать ресурсы других пользователей.

  2. Пользователи:

    • Пользователь — это любой субъект (человек или процесс), которому нужно иметь доступ к ресурсам.

    • Пользователям назначаются одна или несколько ролей. Например, пользователь Bob может иметь роль user.

  3. Права:

    • Права определяют, какие действия могут выполняться над какими ресурсами.

    • Например, право read может разрешать чтение данных своих или чужих постов.

  4. Связи:

    • Связи между ролями и правами определяют, какие права принадлежат каким ролям.

    • Связи между пользователями и ролями определяют, какие роли назначены каким пользователям.

Создание RBAC модели

Создание модели RBAC с группировками

Casbin использует файл модели для определения структуры контроля доступа. Давайте создадим файл модели 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: Определяет формат запроса (кто, что, какое действие).

  • policy_definition: Определяет формат политики.

  • role_definition: Определяет формат ролей и группировок (группы g и g2).

  • policy_effect: Определяет эффект политики (разрешить или запретить).

  • matchers: Определяет логику сопоставления запроса с политиками, включая дополнительные группировки.

По сути мы видим, что у нас есть r (role_definition) — кортеж, который мы будем передавать с запроса, и есть p (policy_definition) — политика, о которой мы поговорим в следующем пункте.

Мы объявляем g и g2 (role_definition) — это особенность RBAC. По сути своей это всего лишь группировки, пример так же будет в блоке про политики.

g отвечает за роли «пользователя». Например у нас есть роль owner и у него может быть несколько видов доступных для него действий — read, edit, delete.

g2 — это группировка для ресурсов. Допустим у пользователя Bob может быть несколько ресурсов, которые будут храниться в группе Bob-Resources.

Раздел policy_effect определяет, как Casbin должен обрабатывать результат сопоставления политики. Он отвечает за то, что происходит, когда один или несколько правил политики применяются к запросу на доступ. Проще говоря, он решает, разрешить или запретить действие, основываясь на правилах, которые были найдены. Таким образом, policy_effect определяет, что если хотя бы одно правило позволяет доступ, то запрос будет одобрен.

Ну и сам алгоритм проверки — matchers. Их мы можем настраивать как нашей душе угодно, но в примере приведен пример для RBAC модели.

Примечание Так же стоит заметить порядок сравнений в matchers. Так сделано для того, чтобы увеличить скорость. В случае если у нас произойдет ошибка во время проверки на действие нам не придется выполнять лишнии функции.

Настройка политик доступа

Теперь создадим файл с политиками policy.csv, в котором определим права доступа и группировки:

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: Определяют права для ролей.. Например, роль admin имеет права crud на admin-res.

  • g: Определяет отношения ролей. Например, alice является admin, а bob является user.

  • g2: Определяет группировки ресурсов. Например, all-resources принадлежат группе admin-res и guest-res

Как и было обещано в предыдущем блоке — буду объяснять что да как.

Первым делом определяются политики — их очень легко понять, По сути буквой p мы указываем, что это будем политика. Далее мы указываем имя пользователя (в настоящем приложении это может быть токен или идентификатор), далее мы указываем ресурсы к которым наш пользователь будем иметь доступ, ну и указываем действие, которые может выполнять пользователь с ресурсами, к которым у него есть доступ.

И так, далее мы видим тот самый role_definition - g. Мы создаем группировку действий crud, по сути это можно воспринимать как переменную — мы создаем переменную, чтобы после ее переиспользовать. Чтобы каждому пользователю не назначать каждый раз одинаковые действий мы будем назначать ему группировку. Сейчас покажу почему это сделано именно так: вместо того, чтобы делать так

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

мы создали группировку и можем назначать ее любому количеству пользователей

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

Точно такая же ситуация и с группировками для ресурсов, потому что у нас могут общие ресурсы, могут быть приватные ресурсы и их может быть очень много, поэтому лучше разбить их на группы и подгруппы и выдавать конкретным пользователям.

Интеграция Casbin в приложение

Установка и настройка Casbin

Создадим директория для проекта

mkdir casbin-rbac cd casbin-rbac 

Инициализируем приложение и установим зависимость

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)     } } 

Запустим наше приложение и посмотрим что из этого вышло:

go run main.go 
Так же `casbin` поддерживает другие виды адаптеров, например `postgres`:

Будем использовать официальный инструмент

go get github.com/casbin/casbin-pg-adapter 
Создание БД и миграции

База данны обязательно должна называться casbin и иметь таблицу сasbin_rule

Для начала подними postgres образ в docker:

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

Вы можете использовать любой инструмент для миграций, я просто оставлю 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', ''), 

Запись в таблицу нужна только для примера нашего приложения. Выполните эти миграции и можете идти дальше.

Создадим адаптер:

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) } 

Конечно же это простой пример того как проходит авторизация

Управление ролями и пользователями

Casbin позволяет легко управлять ролями и пользователями в вашей системе. С его помощью можно добавлять, удалять и изменять роли, а также назначать их пользователям. Давайте рассмотрим основные операции, которые можно выполнять для управления ролями и пользователями.

Добавление пользователей к ролям

Чтобы добавить пользователя к роли, можно использовать метод AddGroupingPolicy. Это позволяет связывать пользователей с ролями.

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") } 

В этом примере мы добавляем пользователя charlie к роли user.

Удаление пользователей из ролей

Чтобы удалить пользователя из роли, можно использовать метод RemoveGroupingPolicy.

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

Этот метод удалит связь между пользователем charlie и ролью user.

Добавление прав к ролям

Для добавления прав к роли можно использовать метод AddPolicy.

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

Этот метод добавляет правило, что роль editor имеет право write на ресурс data2.

Удаление прав у ролей

Для удаления прав у роли можно использовать метод RemovePolicy.

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

Этот метод удаляет правило, что роль editor имеет право write на ресурс data2.

Проверка ролей и прав

Вы можете проверить, к каким ролям принадлежит пользователь и какие права у роли.

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

Эти методы позволяют узнать, какие роли назначены пользователю alice и какие права имеет роль admin.

Полный пример

Давайте соберем всё вместе в одном примере.

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") } 

Заключение

Вот и подошел к концу небольшой экскурс по Casbin, надеюсь, что теперь вы сможете упростить работу с доступами к ресурсам и, может быть, даже сможете принести эту идею в свою команду и получить за это некоторые плюшки.

В целом это весьма несложная тема и с ней достаточно просто разобраться. Надеюсь. что смог кому-то помочь.

Так же Casbin поддерживает и другие языки, такие как:

  • php

  • nodejs

  • .net

  • python

  • rust

  • java

В ближайшие дни я планирую снять обучающий ролик на ютуб, там я сделаю небольшое API и покажу на примерах как использовать ролевую модель RBAC, поэтому если кому-то интересно такое, то предлагаю перейти в мой канал, там я в скором времени анонсирую всю информацию.

Github

Telegram

Instagram


ссылка на оригинал статьи https://habr.com/ru/articles/823374/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *