Перейти к основному содержимому

RBAC

Канонический DDL находится в ../database-schema.md. SQL ниже является концептуальным примером RBAC-модели и не используется для миграций.

Зачем нужно

Документ описывает роли, permissions, назначения ролей и правила проверки прав доступа.

1. Роли

1.1. Три уровня ролей

Глобальные системные — неизменяемы, поставляются с системой:

РольorderНазначение
super_admin0Полный доступ ко всему
admin1Управление платформой (без super-возможностей)
user2Базовый пользователь

Глобальные кастомные — создаёт super_admin/admin через UI. Действуют на уровне всей платформы. Например: moderator, support, editor.

Организационные — создаёт владелец организации. Действуют только внутри этой организации. Они используют тот же централизованный формат permissions, но scope и membership ограничивают проверку конкретной организацией.

1.2. Наследование

Роли поддерживают иерархию через parentId. Дочерняя роль наследует все права родительской + свои собственные. Поле parent_id всегда означает «роль, от которой наследуем».

user (order=2)
└── admin (order=1) inherits user
└── super_admin (order=0) inherits admin

Наследование применяется при генерации JWT: getAllInheritedPermissions(roleId) обходит цепочку parentId вверх и собирает permissions текущей роли и всех родителей.

Кастомные роли тоже могут иметь parentId — например, moderator наследует от user.

1.3. Entity

Три отдельных таблицы — каждый уровень ролей в своей:

Глобальные роли (roles):

CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL UNIQUE,
display_name JSONB NOT NULL,
description JSONB,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
parent_id UUID REFERENCES roles(id),
"order" INT NOT NULL DEFAULT 100,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Организационные и командные роли — см. organizations.md (organization_roles, team_roles).


2. Права доступа

2.1. Формат

Формат наследуется из ../../../platform/permissions-model.md:

<domain>.<resource>.<action>[.<scope>]

Каждый permission — гранулярное право на конкретное действие. Identity не вводит отдельный формат resource.action: все permissions, включая организационные и platform permissions, проходят через единый каталог packages/permissions/catalog.ts.

2.2. Seed-набор identity/platform permissions

Этот список — стартовый seed-набор для bootstrap identity/admin UI. Он не является закрытым числом permissions: каталог расширяется доменными permissions-matrix.md.

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

PermissionОтображение (ru)
identity.users.readПросмотр пользователей
identity.users.manageРедактирование пользователей
identity.users.deleteAnonymization пользователя (GDPR data erasure, см. users.md)
identity.users.blockБан/разбан пользователей

Организации:

PermissionОтображение (ru)
identity.organizations.create.ownСоздание своей организации
identity.organizations.read.ownПросмотр своих организаций
identity.organizations.readПросмотр организаций
identity.organizations.read.organizationПросмотр организации в organization scope
identity.organizations.manageРедактирование организаций
identity.organizations.manage.organizationРедактирование организации в organization scope
identity.organizations.archiveАрхивирование организаций на global/admin уровне
identity.organizations.archive.organizationАрхивирование организации в organization scope
identity.organizations.transfer-ownership.organizationПередача владения организацией
identity.organizations.hard-delete.globalФизическое удаление never-used организаций (break-glass)
identity.organization-references.manageУправление справочником организаций
identity.organization-members.manageУправление участниками организации на global/admin уровне
identity.organization-members.approve.organizationПодтверждение и отклонение заявок на вступление
identity.organization-members.suspend.organizationПриостановка членства
identity.organization-roles.manageУправление ролями организации на global/admin уровне
identity.organization-ownership-claims.reviewРассмотрение заявок на владение
identity.organization-merges.manageРучная склейка организаций
identity.organization-members.read.organizationПросмотр участников внутри организации
identity.organization-members.manage.organizationУправление участниками внутри организации
identity.organization-members.assign.organizationНазначение ролей участников внутри организации
identity.organization-roles.read.organizationПросмотр ролей внутри организации
identity.organization-roles.manage.organizationУправление ролями внутри организации
identity.organization-contacts.manage.organizationУправление контактами организации
identity.organization-invitations.create.organizationОтправка приглашений организации
identity.organization-invitations.manage.organizationУправление приглашениями организации
identity.teams.read.organizationПросмотр команд организации
identity.teams.manage.organizationУправление командами организации
identity.organization-students.read.organizationПросмотр учеников организации
identity.organization-students.create.organizationСоздание учеников организации
identity.organization-students.manage.organizationРедактирование учеников организации
identity.organization-students.archive.organizationАрхивирование учеников организации
identity.organization-permission-grants.manage.organizationУправление organization-local permission grants

Плагины:

PermissionОтображение (ru)
platform.plugins.readПросмотр плагинов
platform.plugins.installУстановка плагинов
platform.plugins.manageУправление плагинами (настройка/включение/выключение)

RBAC:

PermissionОтображение (ru)
identity.roles.readПросмотр ролей
identity.roles.manageСоздание и редактирование ролей
identity.roles.assignНазначение и снятие ролей с пользователей
identity.permissions.readПросмотр permissions

Настройки платформы:

PermissionОтображение (ru)
platform.settings.readПросмотр настроек платформы
platform.settings.manageИзменение настроек платформы
platform.branding.manageРедактирование брендинга
platform.navigation.manageРедактирование навигации

Уведомления:

PermissionОтображение (ru)
platform.notifications.readПросмотр правил уведомлений
platform.notifications.manageРедактирование правил уведомлений

Семья:

PermissionОтображение (ru)
identity.families.readПросмотр семейной группы
identity.families.manageУправление семейной группой

OAuth-клиенты:

PermissionОтображение (ru)
identity.oauth_clients.readПросмотр OAuth-клиентов
identity.oauth_clients.manageУправление OAuth-клиентами

Аудит:

PermissionОтображение (ru)
identity.audit.readПросмотр журнала аудита

2.3. Организационные permissions

Организационные роли используют те же permission keys из централизованного catalog, но применяют их только внутри конкретной организации через scope = organization или более узкий domain-specific scope.

Набор permissions для организационных ролей определяется в organizations.md.

2.4. Человекочитаемость

Каждый permission хранит display_name (JSONB с локалями) для отображения в UI:

CREATE TABLE permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL UNIQUE, -- "identity.users.read"
display_name JSONB NOT NULL, -- { "ru": "Просмотр списка пользователей" }
description JSONB,
resource VARCHAR(100) NOT NULL, -- "users"
action VARCHAR(100) NOT NULL, -- "list"
is_system BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE(name)
);

2.5. Wildcard

Wildcard levels:

  1. Global wildcard * applies only to global/platform permissions. It does not automatically grant organization-local owner role.
  2. Organization wildcard * applies only inside one organization.
  3. Team wildcard * applies only inside one team.

Global super_admin access to organization data is break-glass/admin access: it requires explicit global permission, is always audited, and does not mutate organization membership. Wildcard values are not stored in permissions; they are resolved dynamically when generating effective permissions.


3. Назначение ролей

3.1. Таблица user_role_assignments

Только для глобальных ролей. Организационные и командные назначения — через organization_memberships и organization_team_memberships (см. organizations.md).

CREATE TABLE user_role_assignments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
assigned_by UUID REFERENCES users(id),
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(user_id, role_id)
);

3.2. Правила

  • Пользователь может иметь несколько глобальных ролей
  • Организационные и командные роли назначаются через membership (см. organizations.md)
  • Нельзя назначить роль с большими привилегиями (меньший order), чем у назначающего
  • expires_at — опциональный срок действия роли; истёкшие назначения исключаются на уровне запроса (WHERE expires_at IS NULL OR expires_at > now()), cron-очистка не требуется. При истечении mid-session: текущий access token сохраняет старые permissions до естественного истечения (макс. 15 мин), при refresh token генерируется новый access token без истёкшей роли. Принудительный revoke не выполняется — задержка до 15 мин приемлема
  • При регистрации: первый пользователь → super_admin, остальные → user
  • Аудит: назначение и снятие ролей логируются в audit_logs — действие identity.role.assigned / identity.role.revoked с assigned_by, role_id, user_id. Это security-значимое действие, аудит обязателен

4. Проверка прав

4.1. Серверная часть (API gateway)

Проверка из JWT-payload (синхронная, без обращения к БД):

  • JwtAuthGuard — аутентификация (есть токен?)
  • RolesGuard — проверка роли по имени: @RequireRoles('admin')hasRole(name: string) сравнивает с полем name из JWT, без иерархии уровней
  • PermissionsGuard — проверка permissions (@RequirePermissions('identity.users.read'))
  • super_admin и wildcard * — bypass только в allowed wildcard level; organization-local owner/team lead checks still use local membership or explicit audited global permission.

4.2. Фронтенд

PermissionProvider оборачивает приложение и предоставляет контекст прав через хук usePermissions:

  • Читает roles и permissions из auth store (JWT-payload)
  • hasPermission(permission: string) — проверяет наличие строки в массиве permissions из JWT
  • hasRole(name: string) — проверяет точное совпадение имени роли с одним из значений в roles из JWT (сравнение по имени, не по уровню)
  • isSuperAdmin — wildcard check

Компоненты: AdminRoute, PermissionRoute — защита маршрутов.

4.3. Задержка обновления

Права из JWT — кэш на 15 минут (TTL access token). Изменение ролей вступает в силу при следующем refresh token.


5. Организационные и командные роли

5.1. Организационные роли

Организационные роли независимы от глобальных назначений, но используют тот же permission catalog:

  • Создаются владельцем организации
  • Действуют только внутри организации
  • Не влияют на глобальные права пользователя
  • Хранятся в organization_roles (см. organizations.md §4.1–4.2)

Шаблонные роли (создаются автоматически при создании организации, is_system: true):

РольorderПраваНазначение
owner0["*"]Полный доступ, создатель организации
admin1управление членами, командами, ролями, настройкамиАдминистратор
member2базовый доступОбычный участник

Владелец/admin может создавать кастомные роли с произвольным набором permissions.

5.2. Командные роли

Командные роли действуют только внутри конкретной команды организации:

  • Создаются lead/admin команды
  • Не влияют ни на глобальные, ни на организационные права
  • Хранятся в team_roles (см. organizations.md §4.3–4.4)

Шаблонные роли (создаются автоматически при создании команды):

РольorderПраваНазначение
lead0Добавление/удаление участников, редактирование командыРуководитель
member1Просмотр и участиеУчастник

5.3. Три уровня — без пересечений

Глобальные роли (roles)
└── Действуют на всю платформу
└── Назначаются через user_role_assignments

Организационные роли (organization_roles)
└── Действуют внутри конкретной организации
└── Назначаются через organization_memberships.role_id

Командные роли (team_roles)
└── Действуют внутри конкретной команды
└── Назначаются через organization_team_memberships.role_id

Каждый уровень имеет свою таблицу, свой набор permissions, свои шаблонные роли. Глобальный super_admin имеет доступ ко всему (wildcard *), но это реализуется через глобальную проверку, а не через организационные/командные permissions.


6. API-эндпоинты

HTTPПутьAuthНазначение
GET/api/v2/identity/admin/rolesJWT + identity.roles.readСписок глобальных ролей
POST/api/v2/identity/admin/rolesJWT + identity.roles.manageСоздать роль
PATCH/api/v2/identity/admin/roles/:idJWT + identity.roles.manageРедактировать роль
DELETE/api/v2/identity/admin/roles/:idJWT + identity.roles.manageУдалить роль
GET/api/v2/identity/admin/roles/:id/permissionsJWT + identity.roles.readИтоговые permissions (с наследованием)
GET/api/v2/identity/admin/permissionsJWT + identity.permissions.readСписок permissions
POST/api/v2/identity/admin/role-assignmentsJWT + identity.roles.assignНазначить роль пользователю
DELETE/api/v2/identity/admin/role-assignments/:idJWT + identity.roles.assignСнять роль

7. UX/UI

7.1. Admin: RolesManagementPage

Страница /:lang/admin/roles — управление ролями и правами.

Список ролей:

  • RoleTreeSimple — иерархическое дерево ролей (parent-child)
  • Каждая роль: название + количество пользователей + бейдж уровня
  • Системные роли (super_admin, admin, user) — не удаляемые, помечены бейджем "Системная"

RoleModal (создание/редактирование):

  • Поля: название, описание, parent role (select из дерева)
  • PermissionBuilder — визуальный конструктор: категории permissions (users, organizations, plugins и др.) с чекбоксами
  • Наследование: отображает permissions от parent role (disabled чекбоксы, помечены "Наследовано")

7.2. UserDetailPanel — таб "Роли"

В UserDetailPanel (slide-in панель на странице управления пользователями):

  • UserDetailRolesTab: список назначенных ролей
  • RolesSelector — select для добавления роли
  • Кнопка "Удалить" для каждой назначенной роли (кроме дефолтной user)
  • Предупреждение при назначении super_admin

7.3. Визуальное отображение прав

В UI права влияют на:

  • Sidebar: пункты меню фильтруются по requiredPermissions — пользователь видит только разрешённые
  • Страницы: PermissionRoute проверяет permission до рендера; при отсутствии — AccessDeniedBlock (403)
  • Элементы интерфейса: кнопки, действия, секции скрываются через usePermissions().hasPermission(name)
  • Admin sidebar: группы и пункты фильтруются по permissions текущего пользователя