ADR-003: Кастомні ролі та дозволи
- Статус: прийнято
- Дата: 2026-04-16
- Автор: CTO Agent (KALA-141)
Контекст
Оригінальна авторизаційна модель використовувала фіксований Prisma-enum Role:
admindept_headapproveremployee
Цього вистачало для ранніх API-гейтів, але недостатньо для реальних компаній. Тенантам потрібні ролі на кшталт "Legal Specialist", "Finance Operator" або "Regional Manager" без очікування code-деплою. Водночас продукт ще не вимагав повноцінної RBAC-системи з many-to-many таблицею дозволів, ієрархією ролей, resource-specific grants і policy language.
Рішення
Впроваджуємо SystemRole як основну rule-модель:
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 для backward compatibility та fallback під час міграції.
Дозволи — рядкові ключі з центрального каталогу. Wildcard * дає всі відомі дозволи.
Розглянуті альтернативи
Залишити тільки enum
Відхилено. Це змушувало б кожну tenant-specific роль переносити у код застосунку і вимагало б деплою для зміни політики.
Many-to-many permissions table
Поки відхилено. Нормалізована permissions-таблиця з role-permission join rows потужна, але додає міграції, джойни, складність UI і policy drift ще до того, як у продукту буде достатньо вимог, щоб це виправдати.
JSON policy language
Відхилено. Policy DSL занадто гнучкий для поточного admin UI і його важче аудитити, ніж явні permission-ключі.
Наслідки
Позитивні
- Кастомні ролі створюються і редагуються в рантаймі.
- Вбудовані ролі лишаються захищеними через
isSystem. - Перевірка дозволів проста: членство у масиві плюс розгортання wildcard.
- Legacy-enum fallback тримає існуючих користувачів і тести робочими під час міграції.
Негативні
- Permission-ключі мають лишатися стабільними — це persisted-рядки.
- Перейменування дозволу вимагає data migration.
- Нема resource-level policy language; department scope лишається основною віссю ізоляції.
Нотатки про реалізацію
src/lib/permission-catalog.tsволодіє валідним списком ключів.src/lib/permissions.tsобчислює capability-булеви та ефективні набори дозволів.src/routes/roles.tsвалідує role CRUD і захищає системні ролі.- Сід-дані створюють чотири вбудованих
SystemRole-записи.