Перейти до основного вмісту

ADR-005: Перевизначення дозволів

  • Статус: прийнято
  • Дата: 2026-04-16
  • Автор: CTO Agent (KALA-148)

Контекст

Кастомні ролі покривають стабільну політику tenant, але продакшн-системам завжди потрібні винятки:

  • тимчасово видати користувачу доступ до ролі рівнем вище;
  • revoke-нути ризикований дозвіл у користувача з широкою роллю;
  • додати один відділ під проект;
  • прибрати один відділ, який інакше дає shared-роль.

Створювати нову роль під кожен виняток — це role explosion. Повна per-user permission-таблиця додала б більше механіки, ніж зараз вимагає продукт.

Рішення

Тримаємо override-и прямо на User:

extraPermissions      String[]
revokedPermissions String[]
extraDepartmentIds String[]
revokedDepartmentIds String[]

Ефективні дозволи обчислюються так:

effectivePerm = role.permissions + extraPermissions - revokedPermissions

Ефективні відділи — той самий add-then-subtract патерн:

effectiveDept = role.departmentIds + primary department + extraDepartmentIds - revokedDepartmentIds

Revoke застосовується останнім, тому перемагає і базову роль, і extra.

Розглянуті альтернативи

Per-user permission join-таблиця

Поки відхилено. Легше історично запитувати, але додає write-шляхи, джойни та складність UI, а дозволи мають достатню cardinality, щоб це виправдати.

Кастомна роль під кожен виняток

Відхилено. Створює шум у списку ролей і приховує те, що зміна — це індивідуальний виняток.

Тільки deny-overrides

Відхилено. Продукт потребує і тимчасових grant-ів, і extra department access.

Наслідки

Позитивні

  • Винятки явно видимі на user-записі.
  • Ефективний доступ детермінований і легко пояснюється.
  • Grant/revoke-API можуть писати audit-записи зі зміненим ключем.
  • Revoke дає простий deny-механізм без повноцінної policy-системи.

Негативні

  • Масиви менш нормалізовані, ніж join-рядки.
  • Перейменування permission-ключа потребує array migration.
  • Забагато overrides на одного користувача — це smell, краще створити кастомну роль.

Нотатки про реалізацію

  • POST /users/:id/permissions/grant додає permissions або extra departments.
  • POST /users/:id/permissions/revoke додає permission- або department-revokes.
  • DELETE-маршрути прибирають grant- чи revoke-записи.
  • GET /users/:id/effective-access повертає роль, overrides та обчислений доступ — для дебагу.
  • Кожна override-мутація пише audit-log-запис.