Skip to content

Casbin

Официальный репозиторий: casbin/casbin: An authorization library that supports access control models like ACL, RBAC, ABAC in Golang (github.com)

Официальная документация: 概述 | Casbin

TIP

Эта статья считается вводной статьёй о Casbin. Для более детального изучения обращайтесь к официальной документации.

Введение

В системе бэкенд-разработчики должны отвечать за управление правами доступа к API, что требует значительной работы. Если для каждого проекта писать свою систему с нуля, это займёт много времени. Крупные компании с большими ресурсами склонны разрабатывать собственные фреймворки управления правами, но большинство малых и средних компаний не могут позволить себе такие затраты на разработку, поэтому открытые фреймворки управления правами становятся их первым выбором. Casbin — это как раз такая открытая и эффективная библиотека контроля доступа, разработанная на Go и поддерживающая другие основные языки.

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

  1. ACL (Access Control List, список контроля доступа)
  2. ACL с суперпользователем
  3. ACL без пользователей: Особенно полезно для систем без аутентификации или входа пользователя.
  4. ACL без ресурсов: Некоторые сценарии могут быть направлены только на типы ресурсов, а не на отдельные ресурсы, такие как права write-article, read-log и т.д. Они не контролируют доступ к конкретным статьям или журналам.
  5. RBAC (Control of Access Based on Role)
  6. RBAC с ролями ресурсов: Пользователи и ресурсы могут одновременно иметь роли (или группы).
  7. RBAC с доменами/арендаторами: Пользователи могут устанавливать разные наборы ролей для разных доменов/арендаторов.
  8. ABAC (Control of Access Based on Attribute): Поддерживает использование синтаксического сахара, такого как resource.Owner, для получения атрибутов элементов.
  9. RESTful: Поддерживает пути, такие как /res/*, /res/:id и HTTP-методы, такие как GET, POST, PUT, DELETE.
  10. Приоритет отказа: Поддерживает разрешения и запреты, запрет имеет приоритет над разрешением.
  11. Приоритет: Правила политик упорядочены по порядку для определения приоритета, аналогично правилам брандмауэра.

Принцип работы

В Casbin модели контроля доступа абстрагируются в конфигурационные файлы на основе PERM, где PERM означает Policy (политика), Effect (эффект), Request (запрос), Matcher (сопоставление). При изменении механизма авторизации в проекте нужно только просто изменить конфигурационный файл. Содержимое нормального конфигурационного файла Model следующее:

bash
[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

Это простейшая модель контроля доступа ACL.

Политика

В конфигурационном файле раздел определения политики:

[policy_definition]
p = sub, obj, act

p означает policy, не может быть заменён другими символами. sub означает subject — это субъект политики, obj означает object — это объект политики, act означает action — это действие.

p = sub, obj, act

Также может быть четвёртое поле eft, если опущено, по умолчанию eft равно allow.

p = sub, obj, act, eft

Эта строка просто описывает, как должна записываться политика, а не конкретное определение политики. Ниже приведён пример конкретной политики:

p, jojo, cake, eat

p представляет, что это определение правила политики, jojo — субъект политики, cake — объект политики, eat — действие, полный смысл: субъект jojo может выполнять действие eat над объектом cake. Конкретные правила политики не появляются в файле модели; для хранения политик используются специальные файлы политик или базы данных.

Запрос

В конфигурационном файле раздел определения запроса:

[request_definition]
r = sub, obj, act

r означает request, не может быть заменён другими символами. sub означает subject — это субъект запроса, obj означает object — это объект запроса, act означает action — это действие запроса. Обычно определение запроса совпадает с определением политики по именам полей. Часть запроса не отвечает Casbin; это решает разработчик, что является субъектом запроса, что является объектом запроса. Casbin только отвечает за контроль доступа на основе переданных полей.

Сопоставление

В конфигурационном файле раздел определения сопоставления:

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

m означает matcher, не может быть заменён другими символами. После него следует соответствующее правило сопоставления. Вышеупомянутое — простое булево выражение, означающее, что если все переданные поля запроса совпадают со всеми полями правила политики, то сопоставление успешно. Конечно, это также может быть подстановочный знак или регулярное выражение с большей выразительностью.

Кроме того, matcher поддерживает синтаксис in, например:

[matchers]
m = r.sub in ("root","admin")

Или:

[matchers]
m = r.sub.Name in (r.obj.Admins)
go
e.Enforce(Sub{Name: "alice"}, Obj{Name: "a book", Admins: []interface{}{"alice", "bob"}})

При сопоставлении Casbin не выполняет проверку типов, а использует interface для проверки == на равенство.

Эффект

Раздел определения эффекта выполняет логическую комбинацию и проверку результатов сопоставления. В конфигурационном файле раздел определения эффекта:

[policy_effect]
e = some(where (p.eft == allow))

e означает effect, не может быть заменён другими символами. Квантор some проверяет, существует ли хотя бы одно правило политики, удовлетворяющее сопоставителю. Квантор any проверяет, удовлетворяют ли все правила политики сопоставителю.

some(where (p.eft == allow))

Это правило означает: если среди результатов сопоставления есть хотя бы один результат allow, то окончательный результат — allow.

e = !some(where (p.eft == deny))

Это правило означает: если среди результатов сопоставления нет результата deny, то окончательный результат — allow.

e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

Это правило означает: если среди результатов сопоставления есть хотя бы один allow и нет deny, то окончательный результат — allow.

Хотя Casbin разработал вышеуказанный синтаксис для эффектов политик, текущее выполнение использует только жёстко закодированные эффекты политик. Они считают, что такая гибкость не особо необходима. На данный момент вы должны использовать встроенные эффекты политик, нельзя создавать собственные. Встроенные поддерживаемые эффекты политик следующие:

Определение эффекта политикиЗначениеПример
some(where (p.eft == allow))allow-overrideACL, RBAC и т.д.
!some(where (p.eft == deny))deny-overrideПереопределение отказа
some(where (p.eft == allow)) && !some(where (p.eft == deny))allow-and-denyРазрешение и отказ
priority(p.eft) || denypriorityПриоритет
subjectPriority(p.eft)Приоритет на основе ролиПриоритет субъекта

TIP

  1. Все четыре вышеуказанных определения могут быть определены несколько раз. Синтаксис: type+number, например r2, p2, e2, m2.

  2. Файлы моделей могут иметь комментарии, использующие символ #.

Пример

Ниже приведён пример, демонстрирующий процесс работы файла модели. Сначала определим простой файл модели ACL:

[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

Файл политики:

p, alice, data1, read
p, bob, data2, write

Процесс абстрагирования субъекта, объекта, действия определяется бизнес-логикой. Здесь это не важно, поэтому опущено. Ниже показан простейший способ передачи запроса:

alice, data1, read
bob, data1, read
alice, data2, write
bob, data2, write

В файле политики определено: alice имеет право на чтение data1, bob имеет право на запись data2. Тогда в переданных запросах:

alice, data1, read

означает, что alice хочет выполнить чтение data1.

bob, data1, read

означает, что bob хочет выполнить чтение data1. Остальное аналогично. Тогда окончательный результат:

true
false
false
true

Это простейший пример ACL. На официальном сайте Casbin можно редактировать и тестировать примеры онлайн. Перейдите на Casbin editor для тестирования.

RBAC

RBAC (Role-Based Access Control) — контроль доступа на основе ролей. По сравнению с моделью ACL добавляется раздел [role_definition]. Ниже приведена простая модель RBAC:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Определение роли:

[role_definition]
g = _, _

g означает group, не может быть заменён другими символами. Поддерживает создание нескольких с помощью type+number. _ — это заполнитель, указывающий количество входных параметров. Обычно в политике g имеет следующий формат:

g, alice, data2_admin
g, mike, data1_admin
g, data1_admin data2_admin

alice — это субъект, data2_admin — это роль. Строго говоря, Casbin рассматривает их как строки. Как интерпретировать их значение и использование, зависит от разработчика.

g, alice, data2_admin

означает, что alice имеет роль data2_admin.

g, mike, data1_admin

означает, что mike имеет роль data1_admin.

g, data1_admin data2_admin

означает, что роль data1_admin имеет роль data2_admin. Это отношение наследования между ролями.

Модель ролей ресурсов

Модель ролей ресурсов добавляет g2 как определение роли ресурса. Определение модели:

[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 = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act

Пример определения политики:

p, alice, data1, read
p, bob, data2, write
p, data_group_admin, data_group, write

g, alice, data_group_admin
g2, data1, data_group
g2, data2, data_group

g2 определяет группу ролей ресурсов, назначая ресурсы разным ролям, одновременно определяя отношения пользователей между ролями пользователей и ролями ресурсов.

p, data_group_admin, data_group, write

Эта политика определяет, что пользователь с ролью data_group_admin может выполнять операцию записи над ресурсом с ролью data_group.

Модель многоарендаторных доменов

[request_definition]
r = sub, dom, obj, act

[policy_definition]
p = sub, dom, obj, act

[role_definition]
g = _, _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act

Модель многоарендаторных доменов по сравнению с традиционной моделью RBAC добавляет поле dom для указания домена, к которому принадлежит субъект. Пример политики:

p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write

g, alice, admin, domain1
g, bob, admin, domain2

Например:

p, admin, domain1, data1, read

определяет, что субъект admin, принадлежащий домену domain1, имеет право на чтение data1.

g, alice, admin, domain1

определяет, что alice принадлежит domain1 и имеет роль admin.

ABAC

ABAC (Attribute-Based Access Control) — контроль доступа на основе атрибутов. Модель ABAC использует атрибуты субъектов, объектов и действий для определения правил доступа.

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.obj == r.sub.Age

В модели ABAC сопоставление может использовать атрибуты объектов. Например, r.obj == r.sub.Age означает, что доступ разрешён, если возраст субъекта совпадает с объектом.

Пример использования в коде:

go
e := casbin.NewEnforcer("model.conf", "policy.csv")

sub := User{Age: 18}
obj := 18

allowed, _ := e.Enforce(sub, obj, "read")

Использование в Go

Установка

bash
go get github.com/casbin/casbin/v2

Базовое использование

go
package main

import (
    "github.com/casbin/casbin/v2"
)

func main() {
    // Создание энфорсера с файлом модели и файлом политики
    e, _ := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")
    
    // Проверка прав доступа
    sub := "alice"
    obj := "data1"
    act := "read"
    
    allowed, _ := e.Enforce(sub, obj, act)
    
    if allowed {
        println("Доступ разрешён")
    } else {
        println("Доступ запрещён")
    }
}

Использование с базой данных

Casbin поддерживает различные базы данных для хранения политик:

go
import (
    "github.com/casbin/casbin/v2"
    gormadapter "github.com/casbin/gorm-adapter/v3"
)

// Использование с GORM
a, _ := gormadapter.NewAdapter("mysql", "mysql:mysql@tcp(127.0.0.1:3306)/casbin")
e, _ := casbin.NewEnforcer("path/to/model.conf", a)

Добавление политик

go
// Добавление одной политики
e.AddPolicy("alice", "data1", "read")

// Добавление нескольких политик
e.AddPolicies([][]string{
    {"bob", "data2", "write"},
    {"charlie", "data3", "read"},
})

// Добавление роли
e.AddGroupingPolicy("alice", "admin")

Удаление политик

go
// Удаление политики
e.RemovePolicy("alice", "data1", "read")

// Удаление политик по фильтру
e.RemoveFilteredPolicy(0, "alice")

// Удаление роли
e.RemoveGroupingPolicy("alice", "admin")

Получение политик

go
// Получение всех политик
policies := e.GetPolicy()

// Получение политик для субъекта
userPolicies := e.GetPolicyForUser("alice")

// Получение ролей для субъекта
roles := e.GetRolesForUser("alice")

// Получение пользователей для роли
users := e.GetUsersForRole("admin")

Интеграция с веб-фреймворками

Интеграция с Gin

go
package main

import (
    "github.com/casbin/casbin/v2"
    "github.com/gin-gonic/gin"
)

func CasbinMiddleware(e *casbin.Enforcer) gin.HandlerFunc {
    return func(c *gin.Context) {
        sub := c.GetString("user") // Получение пользователя из контекста
        obj := c.Request.URL.Path
        act := c.Request.Method
        
        allowed, _ := e.Enforce(sub, obj, act)
        
        if !allowed {
            c.AbortWithStatus(403)
            return
        }
        
        c.Next()
    }
}

func main() {
    e, _ := casbin.NewEnforcer("model.conf", "policy.csv")
    
    r := gin.Default()
    r.Use(CasbinMiddleware(e))
    
    r.GET("/data1", func(c *gin.Context) {
        c.String(200, "Доступ к data1")
    })
    
    r.Run()
}

Заключение

Casbin — это мощный и гибкий фреймворк контроля доступа, который поддерживает множество моделей доступа и может быть легко интегрирован в различные проекты. Благодаря своей простоте и эффективности, Casbin стал популярным выбором для управления правами доступа в Go-приложениях.

Golang by www.golangdev.cn edit