ADR-003: Кастомные роли и разрешения
- Статус: Принято
- Дата: 16 апреля 2026
- Автор: CTO Agent (KALA-141)
Контекст
Исходная авторизационная модель использовала фиксированный Prisma-enum Role:
admin;dept_head;approver;employee.
Этого хватало для первых API-гейтов, но не для реальных компаний. Тенантам нужны роли вроде "Legal Reviewer", "Finance Operator" или "Regional Manager" — без ожидания code deploy. Одновременно продукт ещё не требует полноценной many-to-many RBAC с permission-таблицей, иерархией ролей, ресурс-грантами и policy-языком.
Решение
Ввести SystemRole как основную модель:
model SystemRole {
id String
name String
slug String
isSystem Boolean
allDepartments Boolean
departmentIds String[]
permissions String[]
}
Встроенные роли хранятся как строки SystemRole с isSystem: true. Роли, созданные тенантом, имеют isSystem: false. User.roleId указывает на role-строку, а legacy enum остаётся в User.role для совместимости и fallback во время миграции.
Разрешения — строковые ключи из центрального каталога. Wildcard * выдаёт все известные разрешения.
Рассмотренные альтернативы
Оставить только enum
Отклонено. Любая tenant-специфичная роль потребовала бы правок в коде приложения и деплоя для изменения политики.
Many-to-many таблица разрешений
Пока отклонено. Нормализованная permission-таблица с role-permission join — мощная штука, но добавляет миграции, джоины, сложность UI и дрейф политики до того, как продукт действительно этого требует.
Policy DSL в JSON
Отклонено. Policy DSL слишком гибкий для текущего админ-UI и его сложнее аудитить, чем явные permission-ключи.
Последствия
Плюсы
- Кастомные роли можно создавать и править в runtime.
- Встроенные роли защищены через
isSystem. - Permission-проверки остаются простыми: проверка членства в массиве + расширение wildcard.
- Legacy enum fallback позволяет существующим пользователям и тестам работать во время миграции.
Минусы
- Permission-ключи должны быть стабильными — они хранятся как строки.
- Переименование permission требует миграции данных.
- Нет resource-level policy language; изоляция идёт по оси department scope.
Заметки по реализации
src/lib/permission-catalog.tsвладеет валидным списком ключей.src/lib/permissions.tsвычисляет capabilities и эффективные permission-наборы.src/routes/roles.tsобрабатывает role CRUD и защищает системные роли.- Seed-данные создают четыре встроенные строки
SystemRole.