Роли и разрешения
AgentCore использует гибридную RBAC-модель. Legacy enum Role остаётся для совместимости, а SystemRole хранит редактируемые наборы разрешений и department scope. Переопределения на уровне пользователя разруливают исключения без отдельной таблицы-джоина разрешений.
Системные роли
Четыре встроенные роли сидируются как строки SystemRole с isSystem: true. Системные роли можно назначать пользователям и обновлять через миграции или seed-данные, но UI продукта не должен позволять тенантам их удалять.
| Роль | Department scope | Ключевые разрешения |
|---|---|---|
admin | все отделы | все известные разрешения |
dept_head | свой отдел и подотделы | пользователи отдела, namespaces, персоны, approvals, знания, шаблоны, профили сотрудников |
approver | свой отдел и подотделы | approvals, эскалации, просмотр всех approvals, чтение знаний, генерация документов |
employee | свой отдел и подотделы | чтение персоны, чтение знаний, генерация документов, чтение плагинов, свой профиль |
Legacy enum-значения всё ещё есть в User.role. Если roleId задан — основным источником разрешений служит User.systemRole. Если не задан, бэкенд откатывается на legacy enum mapping.
Кастомные роли
Кастомные роли — это строки в system_roles с isSystem: false.
Поля:
| Поле | Назначение |
|---|---|
name | Отображаемое имя. |
slug | Стабильный lowercase-ключ. Генерируется из name, если не указан. |
isSystem | Защищает встроенные роли от удаления. |
allDepartments | Если true, даёт межотдельскую видимость. |
departmentIds | Явный department scope, когда allDepartments: false. |
permissions | Массив permission-ключей или * для всех разрешений. |
Маршруты кастомных ролей:
GET /api/v1/permissions
GET /api/v1/roles
GET /api/v1/roles/:id
POST /api/v1/roles
PATCH /api/v1/roles/:id
DELETE /api/v1/roles/:id
Просмотр ролей требует canViewRoles. Создание, обновление и удаление — canManageRoles. Удаление системных ролей отклоняется.
Каталог разрешений
Разрешения — строковые ключи, сгруппированные для отображения в UI:
| Группа | Ключи |
|---|---|
| Пользователи | canViewAllUsers, canCreateUsers, canManageUsers, canManageDepartmentUsers |
| Роли | canViewRoles, canManageRoles |
| Отделы | canManageDepartments, canArchiveDepartments |
| Namespaces и персоны | canManageNamespaces, canEditPersona, canViewPersona |
| Approvals | canApprove, canEscalate, canViewAllApprovals |
| Знания | canViewKnowledge, canUploadDocuments, canDeleteDocuments |
| Генерация документов | canGenerateDocuments, canManageTemplates |
| Плагины | canViewPlugins, canManagePlugins |
| Администрирование | canViewAudit, canManageEmployeeProfiles, canEditSettings, canEditSelfProfile |
| Система | * |
API валидирует каждую кастомную роль и override по этому каталогу.
Матрица разрешений
Заполненная встроенная матрица:
| Разрешение | Admin | Dept head | Approver | Employee |
|---|---|---|---|---|
canCreateUsers | да | нет | нет | нет |
canManageUsers | да | нет | нет | нет |
canManageDepartmentUsers | да | да | нет | нет |
canViewAllUsers | да | да | да | нет |
canViewRoles | да | нет | нет | нет |
canManageRoles | да | нет | нет | нет |
canManageDepartments | да | нет | нет | нет |
canArchiveDepartments | да | нет | нет | нет |
canManageNamespaces | да | да | нет | нет |
canEditPersona | да | да | нет | нет |
canViewPersona | да | да | да | да |
canApprove | да | да | да | нет |
canEscalate | да | да | да | нет |
canViewAllApprovals | да | нет | да | нет |
canUploadDocuments | да | да | нет | нет |
canDeleteDocuments | да | да | нет | нет |
canViewKnowledge | да | да | да | да |
canManageTemplates | да | да | нет | нет |
canGenerateDocuments | да | да | да | да |
canManagePlugins | да | нет | нет | нет |
canViewPlugins | да | да | да | да |
canViewAudit | да | нет | нет | нет |
canManageEmployeeProfiles | да | да | нет | нет |
canEditSelfProfile | да | да | да | да |
canEditSettings | да | нет | нет | нет |
Кастомные роли могут выдавать любое подмножество этих разрешений.
User overrides
У каждого пользователя четыре массива override-ов:
| Поле | Эффект |
|---|---|
extraPermissions | Добавляет permission-ключи после оценки роли. |
revokedPermissions | Убирает permission-ключи после применения extraPermissions. |
extraDepartmentIds | Добавляет видимые отделы после оценки scope роли. |
revokedDepartmentIds | Убирает отделы после применения extraDepartmentIds. |
Маршруты override:
POST /api/v1/users/:id/permissions/grant
POST /api/v1/users/:id/permissions/revoke
DELETE /api/v1/users/:id/permissions/grant/:permission
DELETE /api/v1/users/:id/permissions/revoke/:permission
GET /api/v1/users/:id/effective-access
Grant- и revoke-запросы могут включать permission, departmentId или то и другое. Каждая мутация пишет audit-запись.
Формула эффективного доступа
Разрешения:
rolePermissions = user.systemRole?.permissions ?? legacyPermissions(user.role)
effectivePermissions = (rolePermissions + user.extraPermissions) - user.revokedPermissions
Если в extraPermissions есть *, он раскрывается во все известные permission-ключи. Revoke применяется последним, поэтому отозванное разрешение бьёт и базовую роль, и extraPermissions.
Отделы:
if role.allDepartments or legacy role is admin:
effectiveDepartments = all
else:
effectiveDepartments =
role.departmentIds
+ user.primaryDepartmentId
+ user.departmentId
+ user.extraDepartmentIds
- user.revokedDepartmentIds
Хелпер forUser() оборачивает это вычисление для кода маршрутов. Он даёт:
directWhere()для моделей с прямымdepartmentId;nestedWhere('namespace')для моделей, scope которых задан через связь;canSeeDept(id)для проверки ресурса;hasPermission(key)для RBAC-чеков.
Правила разработки
- Применяйте
requirePermission()на привилегированных маршрутах. - В новом коде используйте
forUser(request.user)вместо проверкиrole === 'admin'. - Для устойчивой политики тенанта предпочитайте кастомные роли, а override-ы — для индивидуальных исключений.
- Держите
revokedPermissionsнебольшим. Если нужно убрать разрешение у многих пользователей — сделайте другую кастомную роль. - При разборе тикетов по доступу используйте
GET /users/:id/effective-access.