Casbin — это мощная и гибкая библиотека авторизации с открытым исходным кодом, которая позволяет разработчикам легко внедрять различные модели контроля доступа, такие как ACL, RBAC, ABAC и другие, в свои приложения. Одним из наиболее популярных применений Casbin является реализация ролевой модели доступа (Role-Based Access Control, RBAC). В этой статье мы подробно рассмотрим, как реализовать RBAC с использованием Casbin в языке программирования Go, с акцентом на подгрузку ролей и их назначение пользователям.
.conf) и политики (.csv или адаптеры баз данных) для определения ролей и разрешений.Модель RBAC в Casbin строится на нескольких ключевых компонентах, которые определяют, как пользователи, роли и разрешения взаимодействуют друг с другом.
Файл модели (обычно с расширением .conf) описывает структуру контроля доступа. Для RBAC он включает следующие основные секции:
[request_definition]: Определяет структуру запроса на доступ. Обычно это r = sub, obj, act, где sub – субъект (пользователь), obj – объект (ресурс), act – действие (например, read, write).[policy_definition]: Определяет структуру правил политики. Для RBAC это часто p = sub, obj, act, где sub в данном контексте может представлять роль.[role_definition]: Определяет связи между пользователями и ролями, а также иерархию ролей. Обычно используется g = _, _ для указания, что пользователь (первый аргумент) является членом роли (второй аргумент).[policy_effect]: Определяет, как агрегируются результаты совпадающих правил политики. Распространенное значение – e = some(where (p.eft == allow)), что означает "разрешить, если хотя бы одно правило разрешает".[matchers]: Определяет логику сопоставления запроса с правилами политики. Для RBAC это может быть m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act. Это выражение проверяет, принадлежит ли запрашивающий субъект (пользователь) к роли, указанной в политике, и совпадают ли объект и действие.Файл политики (часто .csv или хранится в базе данных через адаптер) содержит конкретные правила доступа и назначения ролей. Политика состоит из строк, соответствующих определениям в модели:
p, role_name, resource, action: Определяет, что роль role_name имеет право выполнять действие action над ресурсом resource.g, user_name, role_name: Назначает пользователю user_name роль role_name.g, role_name1, role_name2: Определяет иерархию ролей, где role_name1 наследует разрешения от role_name2.Enforcer — это центральный объект в Casbin. Он загружает модель и политику, а затем предоставляет API для выполнения проверок авторизации (Enforce()), управления политиками (добавление, удаление правил) и ролями (назначение ролей пользователям, получение ролей пользователя).
Визуализация концепции ролевого контроля доступа (RBAC).
Для начала работы с Casbin в вашем Go-проекте, необходимо установить соответствующий пакет. Выполните следующую команду в терминале:
go get github.com/casbin/casbin/v2
Если вы планируете хранить политики в базе данных, вам также потребуется установить соответствующий адаптер (например, для Gorm, Xorm).
rbac_model.conf)Этот файл определяет структуру вашей 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
В этом примере:
r.sub - субъект (пользователь), делающий запрос.p.sub - субъект в определении политики (здесь это роль).g(r.sub, p.sub) - функция Casbin, проверяющая, имеет ли пользователь r.sub роль p.sub.policy.csv)Этот файл содержит конкретные правила и назначения ролей.
p, admin, /api/data1, read
p, admin, /api/data1, write
p, editor, /api/data1, read
p, editor, /api/data2, write
p, viewer, /api/data1, read
g, alice, admin
g, bob, editor
g, charlie, viewer
g, eve, editor
Из этой политики следует:
admin может читать и писать /api/data1.editor может читать /api/data1 и писать /api/data2.alice имеет роль admin.bob и eve имеют роль editor.Теперь рассмотрим, как использовать Casbin в коде на Go для загрузки ролей, их назначения и проверки разрешений.
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
// Для использования файлового адаптера, если политики хранятся в файле
// "github.com/casbin/casbin/v2/persist/file-adapter"
// "github.com/casbin/casbin/v2/model"
)
func main() {
// Инициализация Enforcer с моделью и файлом политики
// Если модель и политика находятся в текущей директории:
enforcer, err := casbin.NewEnforcer("rbac_model.conf", "policy.csv")
if err != nil {
log.Fatalf("Не удалось создать Casbin enforcer: %v", err)
}
// Загрузка политики из хранилища (например, файла или БД).
// Если используется файловый адаптер, это происходит при создании NewEnforcer.
// Для других адаптеров может потребоваться явный вызов:
// err = enforcer.LoadPolicy()
// if err != nil {
// log.Fatalf("Не удалось загрузить политику: %v", err)
// }
// --- Проверка разрешений ---
fmt.Println("--- Проверка разрешений ---")
checkPermission(enforcer, "alice", "/api/data1", "read") // Ожидается: true
checkPermission(enforcer, "alice", "/api/data1", "write") // Ожидается: true
checkPermission(enforcer, "bob", "/api/data1", "read") // Ожидается: true
checkPermission(enforcer, "bob", "/api/data1", "write") // Ожидается: false
checkPermission(enforcer, "charlie", "/api/data2", "write") // Ожидается: false
// --- Получение ролей для пользователя ---
fmt.Println("\n--- Получение ролей пользователя ---")
rolesAlice, err := enforcer.GetRolesForUser("alice")
if err != nil {
log.Printf("Ошибка получения ролей для alice: %v", err)
} else {
fmt.Printf("Роли для alice: %v\n", rolesAlice) // Ожидается: [admin]
}
rolesBob, err := enforcer.GetRolesForUser("bob")
if err != nil {
log.Printf("Ошибка получения ролей для bob: %v", err)
} else {
fmt.Printf("Роли для bob: %v\n", rolesBob) // Ожидается: [editor]
}
rolesNonExistent, err := enforcer.GetRolesForUser("unknown_user")
if err != nil {
log.Printf("Ошибка получения ролей для unknown_user: %v", err)
} else {
fmt.Printf("Роли для unknown_user: %v\n", rolesNonExistent) // Ожидается: []
}
// --- Назначение роли пользователю (выдача роли) ---
fmt.Println("\n--- Назначение новой роли пользователю ---")
// Предположим, мы хотим дать пользователю 'dave' роль 'viewer'
// Используем AddGroupingPolicy для добавления g-правила
added, err := enforcer.AddGroupingPolicy("dave", "viewer")
if err != nil {
log.Printf("Ошибка добавления роли для dave: %v", err)
}
if added {
fmt.Println("Пользователю 'dave' успешно назначена роль 'viewer'.")
// Если вы хотите сохранить изменения в файл политики (если используется FileAdapter):
// err = enforcer.SavePolicy()
// if err != nil {
// log.Fatalf("Не удалось сохранить политику: %v", err)
// }
} else {
fmt.Println("Роль 'viewer' уже была назначена пользователю 'dave' или произошла ошибка.")
}
// Проверяем права нового пользователя
checkPermission(enforcer, "dave", "/api/data1", "read") // Ожидается: true
checkPermission(enforcer, "dave", "/api/data1", "write")// Ожидается: false
rolesDave, _ := enforcer.GetRolesForUser("dave")
fmt.Printf("Роли для dave после назначения: %v\n", rolesDave) // Ожидается: [viewer]
// --- Удаление роли у пользователя ---
fmt.Println("\n--- Удаление роли у пользователя ---")
removed, err := enforcer.RemoveGroupingPolicy("eve", "editor")
if err != nil {
log.Printf("Ошибка удаления роли у eve: %v", err)
}
if removed {
fmt.Println("Роль 'editor' успешно удалена у пользователя 'eve'.")
} else {
fmt.Println("Роль 'editor' не была найдена у пользователя 'eve' или произошла ошибка.")
}
checkPermission(enforcer, "eve", "/api/data1", "read") // Ожидается: false (если это была ее единственная роль, дающая доступ)
rolesEve, _ := enforcer.GetRolesForUser("eve")
fmt.Printf("Роли для eve после удаления: %v\n", rolesEve) // Ожидается: []
}
func checkPermission(e *casbin.Enforcer, sub, obj, act string) {
allowed, err := e.Enforce(sub, obj, act)
if err != nil {
log.Fatalf("Ошибка при проверке разрешения для %s, %s, %s: %v", sub, obj, act, err)
}
fmt.Printf("Пользователь '%s' %s выполнять '%s' для объекта '%s': %t\n",
sub,
map[bool]string{true: "МОЖЕТ", false: "НЕ МОЖЕТ"}[allowed],
act,
obj,
allowed)
}
В этом примере демонстрируется инициализация Enforcer, проверка разрешений, получение ролей для пользователя с помощью GetRolesForUser() и назначение новой роли пользователю с помощью AddGroupingPolicy() (что эквивалентно добавлению строки g, user, role в политику). Метод RemoveGroupingPolicy() используется для отзыва роли.
Casbin также поддерживает иерархию ролей. Например, если роль старший_редактор наследует все права роли редактор, вы можете определить это в файле политики:
g, senior_editor, editor
Тогда, если пользователь frank имеет роль senior_editor, он автоматически получит все разрешения, назначенные роли editor. Casbin обрабатывает это наследование автоматически при проверке прав.
// Добавление правила наследования (если его нет в файле)
// enforcer.AddRoleForUserInDomain("senior_editor", "editor", "") // или AddGroupingPolicy("senior_editor", "editor")
// Назначение роли пользователю
// enforcer.AddGroupingPolicy("frank", "senior_editor")
// Frank теперь будет иметь права 'editor'
// checkPermission(enforcer, "frank", "/api/data1", "read") // Ожидается: true
Для лучшего понимания структуры RBAC в Casbin и различных аспектов ее реализации, рассмотрим следующие визуализации.
Эта ментальная карта иллюстрирует основные концепции и их взаимосвязи в RBAC-реализации с Casbin.
Эта карта показывает, как модель определяет структуру, политика предоставляет данные, а Enforcer использует их для принятия решений авторизации.
Выбор способа хранения политик Casbin (в файлах или базе данных) влияет на различные аспекты системы. Представленный ниже радарный график сравнивает эти подходы по нескольким критериям. Оценки являются качественными и могут варьироваться в зависимости от конкретной реализации.
Как видно из графика, файловые адаптеры просты в настройке, но могут быть менее масштабируемы и гибки для динамических обновлений по сравнению с адаптерами баз данных или внешними системами управления ролями.
Casbin предоставляет богатый API для управления ролями и политиками. Ниже приведена таблица с некоторыми из наиболее часто используемых функций.
| Функция | Описание | Пример использования (Go) |
|---|---|---|
casbin.NewEnforcer() |
Создает новый экземпляр Enforcer, загружая модель и политику (из файла или адаптера). | e, err := casbin.NewEnforcer("model.conf", "policy.csv") |
Enforce(sub, obj, act) |
Проверяет, имеет ли субъект sub разрешение на выполнение действия act над объектом obj. |
allowed, err := e.Enforce("alice", "/data", "read") |
LoadPolicy() |
Перезагружает все правила политики из хранилища (файла/БД). | err := e.LoadPolicy() |
SavePolicy() |
Сохраняет текущие правила политики обратно в хранилище (если адаптер поддерживает запись). | err := e.SavePolicy() |
GetRolesForUser(name string) |
Возвращает все роли, назначенные пользователю name. |
roles, err := e.GetRolesForUser("alice") |
GetUsersForRole(name string) |
Возвращает всех пользователей, которым назначена роль name. |
users, err := e.GetUsersForRole("admin") |
AddPolicy(params ...string) |
Добавляет правило авторизации (p-правило) в текущую политику. | added, err := e.AddPolicy("bob", "/data2", "write") |
RemovePolicy(params ...string) |
Удаляет правило авторизации (p-правило) из текущей политики. | removed, err := e.RemovePolicy("bob", "/data2", "write") |
AddGroupingPolicy(params ...string) |
Добавляет правило назначения роли (g-правило) в текущую политику. Эквивалентно AddRoleForUser. |
added, err := e.AddGroupingPolicy("charlie", "viewer") |
RemoveGroupingPolicy(params ...string) |
Удаляет правило назначения роли (g-правило) из текущей политики. Эквивалентно DeleteRoleForUser. |
removed, err := e.RemoveGroupingPolicy("charlie", "viewer") |
AddRoleForUser(user, role string) |
Назначает роль пользователю. | success, err := e.AddRoleForUser("dave", "editor") |
DeleteRoleForUser(user, role string) |
Удаляет роль у пользователя. | success, err := e.DeleteRoleForUser("dave", "editor") |
Для более глубокого погружения в реализацию RBAC с помощью Casbin на Go, включая настройку и управление разрешениями в реальном времени, рекомендуем посмотреть следующее видео. Оно подробно рассматривает создание масштабируемой системы управления разрешениями.
В этом видео подробно рассматривается создание системы управления разрешениями RBAC на Go.