ADR-011. Разграничение доступа по модели ABAC (claims → функции → группы)

ADR Версия: 0.1 proposed

Полномочия описываются как claims (пара ключ+значение), собираемые в человекочитаемые функции, а функции — в группы, назначаемые пользователю. Backend проверяет claims на каждой команде/запросе, UI скрывает недоступные разделы и действия. Модель адаптирована из rtfm (ADR-SRV-023) без периметра — в домене ICD орг-структуры для скоупинга нет.

⟨/⟩ Исходник

1. Контекст

До сих пор ICD работал без доступа: команды выполняет кто угодно, поля CreatedByUserId / AssignedToUserId пишутся заглушкой (Guid.Empty), а решение «кто ввёл» vs «кто взял в работу» зафиксировано, но не защищено (ADR-004 «Кто ввёл и кто взял в работу»). Видение предполагает две роли — хранитель (ввод, проверка и корректировка, экспорт) и администратор (конфигурация обработки), — но состав ролей и прав оставлен открытым вопросом (OQ-V-1 в видение Сервиса). Требования уже опираются на «доступность действий по правам»: набор операций проверки (FR-005 «Верификация») и удаление/переходы в очереди (FR-009 «Очередь обработки»).

Нужна модель разграничения, которая:

  1. Гранулярна — контроль на уровне отдельной команды/запроса и отдельного UI-действия, а не только грубых ролей.
  2. Настраивается администратором человекочитаемо, без участия разработчика после первичного посева.
  3. Едина для backend и UI — фронт потребляет подмножество тех же прав.

В rtfm та же задача решена как ABAC с двумя осями: функциональной (что можно делать) и периметром (к каким объектам — в терминах сетей/организаций). У ICD второй оси нет: сервис оцифровки карт не мультитенантен, признака для скоупинга данных в домене не существует. Поэтому переносим функциональную ось ABAC и исключаем периметр.

2. Решение

Принимается ABAC на функциональных claims (плоский, без периметра).

claim = пара (ключ, значение). Ключи — контролируемый реестр:

Функция — человекочитаемая единица полномочий (напр. «Хранитель», «Администратор», «Только просмотр»); её фактические права — набор claim-значений. Группа набирается из функций; пользователю назначаются группы. Функции сеет разработчик (посев в инфраструктуре), группы и назначения ведёт администратор.

Проверки на двух уровнях:

Сессия. Вход по логину/паролю выписывает JWT с уникальным jti. Контекст пользователя (его claims) собирается из БД (группы → функции → claim-значения) и кэшируется по jti на срок жизни токена. Выход/отзыв — через список инвалидированных токенов + вытеснение контекста из кэша.

Dev-суперпользователь (break-glass). Один сид-пользователь проходит любые проверки (профиль отдаёт */*). Только для разработки, на проде убирается. Единый источник — backend.

Периметр — вне охвата. Фильтрация данных по орг-структуре не вводится: скоупить нечем. Оставлено точкой расширения — добавляется отдельным решением при появлении разграничения (по образцу rtfm ADR-SRV-024), не ломая claim-инфраструктуру.

3. Обоснование и рассмотренные варианты

Функции и группы сохраняем даже для небольшого приложения ради двух вещей: администратор управляет доступом без разработчика, и модель консистентна с rtfm (общий тулинг и подход в проектах организации) — перенос ADR и кода дешевле, чем изобретать упрощение.

4. Следствия

Положительные:

Компромиссы:

Вне охвата (точки расширения):

Связанные артефакты

Документы