Files
mdm-ordinis/docs/operator/rbac.md
T
2026-05-13 16:41:03 +00:00

12 KiB
Raw Blame History

Роли доступа и матрица прав

Ordinis использует Keycloak realm roles (claim realm_access.roles в JWT) для двух уровней контроля доступа:

  1. Scope ACL — что пользователь может читать (видимость записей и словарей)
  2. Workflow roles — какие действия пользователь может выполнять (approve, publish, reject)

Все роли используют colon-separated namespace ordinis:<домен>:<действие>.

Полная матрица ролей

Scope ACL (видимость данных)

Realm role Видит Подразумевает
ordinis:client:public только PUBLIC записи
ordinis:client:internal PUBLIC + INTERNAL автоматически включает public
ordinis:client:restricted PUBLIC + INTERNAL + RESTRICTED автоматически включает оба нижних

Без любой из этих ролей пользователь считается anonymous и видит только PUBLIC записи (fallback).

Альтернативные dash-формы (ordinis-public, ordinis-internal, ordinis-restricted) также распознаются для совместимости. Канонические — colon-форма.

Workflow роли (право на действия)

Realm role Право
ordinis:schema:reviewer Approve / Запросить правки / Отклонить schema draft
ordinis:schema:publisher Publish approved schema draft → live
ordinis:record:reviewer Approve / Reject record draft (Approval Workflow v2)
ordinis:admin Super-role — автоматически разрешает любой workflow action. Convenience для small teams: assign one role вместо трёх.

Backend проверяет роль перед каждым действием. Без роли — 403 forbidden_role с сообщением о требуемой роли. Frontend дополнительно скрывает кнопки, для которых нет роли.

JWT claim path: на staging/prod backend читает роли из resource_access.ordinis.roles (client-scoped roles в Keycloak ordinis client). Default fallback — realm_access.roles. Настраивается через ORDINIS_AUTH_ROLES_CLAIM env var.

Maker side (создание / submit / withdraw draft'а) специальной ролью не gated — любой authenticated user с required scope может создать draft. Backend защищает invariant maker-checker через 403 self_approve_forbidden (один и тот же user не может approve свой draft).

Полная матрица действий

Действие Endpoint Scope требуется Workflow роль требуется
Просмотр PUBLIC dict GET /api/v1/dictionaries public+
Просмотр INTERNAL/RESTRICTED dict GET /api/v1/dictionaries соответствующий scope
Создание dict POST /api/v1/dictionaries соответствующий scope
Редактирование dict schema (через draft) POST /api/v1/dictionaries/{name}/drafts соответствующий scope — (maker)
Submit schema draft на ревью POST .../drafts/{id}/review соответствующий scope — (maker)
Approve / Request changes / Reject schema draft POST .../drafts/{id}/decision соответствующий scope ordinis:schema:reviewer
Publish approved schema draft POST .../drafts/{id}/publish соответствующий scope ordinis:schema:publisher
Withdraw собственный schema draft POST .../drafts/{id}/withdraw соответствующий scope — (maker self)
Создание record draft POST .../records/.../drafts соответствующий scope — (maker)
Approve record draft POST /api/v1/drafts/{id}/approve соответствующий scope ordinis:record:reviewer
Reject record draft POST /api/v1/drafts/{id}/reject соответствующий scope ordinis:record:reviewer
Просмотр webhook subscriptions GET /api/v1/admin/webhooks/subscriptions internal или restricted
Просмотр audit log GET /api/v1/admin/audit соответствующий scope

Типичные профили пользователей

Только чтение (consumer, integrator)

  • ordinis:client:public

Видит публичные справочники, не может ничего менять.

Maker (контент-редактор)

  • ordinis:client:internal (видит данные своей области)

Создаёт и редактирует справочники, записи, schema drafts, submit'ит их на ревью. Не может approve / publish свои собственные действия (maker-checker).

Reviewer

  • ordinis:client:restricted
  • ordinis:schema:reviewer
  • ordinis:record:reviewer

Может всё что maker, плюс approve / reject чужих drafts. Publish — отдельной ролью.

Publisher

  • ordinis:client:restricted
  • ordinis:schema:reviewer (опционально)
  • ordinis:schema:publisher

Финальный gatekeeper для production публикации схем. Обычно более узкая группа чем reviewers.

Admin (полный доступ)

Простой путь — single role ordinis:admin (super-role): backend и frontend оба recognize её как override для любого workflow check'а. Достаточно одного назначения.

Канонический путь (composite в Keycloak)ordinis:admin как composite, агрегирующий:

  • ordinis:client:restricted
  • ordinis:schema:reviewer
  • ordinis:schema:publisher
  • ordinis:record:reviewer

Composite-роль в Keycloak автоматически expands в JWT — token содержит все включённые роли. Это работает и без super-role override.

Настройка в Keycloak

Создание ролей

  1. Открой Keycloak Admin Console → realm nstartRealm roles.
  2. Нажми Create role и создай по очереди:
    • ordinis:client:public
    • ordinis:client:internal
    • ordinis:client:restricted
    • ordinis:schema:reviewer
    • ordinis:schema:publisher
    • ordinis:record:reviewer

Composite role ordinis:admin (опционально, но удобно)

  1. Create roleordinis:admin.
  2. На вкладке роли → ActionAdd associated roles → выбери все 6 ролей выше → Assign.

После этого можно назначать одну роль ordinis:admin users'ам, и они автоматически получают все права.

Заметка про token mapper: composite role в Keycloak разворачивается в JWT только если есть соответствующий mapper. У client ordinis в realm nstart должен быть mapper типа User Realm Role или User Client Role с Multivalued: ON и Add to access token: ON. Без mapper'а composite роль будет на user'е, но в JWT попадёт только сама ordinis:admin, а её components (ordinis:schema:reviewer и т.д.) — нет.

Backend handles это двумя путями:

  1. Mapper настроен (рекомендуется): JWT содержит all 6 expanded roles → WorkflowRoles.hasRole(specific) находит её напрямую.
  2. Mapper не настроен (default Keycloak install): JWT содержит только ordinis:adminWorkflowRoles.hasRole(role) всё равно проходит через super-role override (roles.contains(ADMIN)).

Super-role override — fallback. Mapper — proper solution. Если admin user'ов больше 5 — настройте mapper, чтобы scope ACL и audit log видели правильные конкретные роли.

Per-dictionary минимальная роль ревьюера (Phase 2)

В DictionaryEditorDialog есть поле «Минимальная роль ревьюера (опционально)» (approvalMinRole). Если задано (e.g. ordinis:restricted) — backend требует чтобы reviewer держал эту роль поверх стандартной ordinis:record:reviewer / ordinis:schema:reviewer / ordinis:schema:publisher.

Use case: критичный справочник (PII, госуд. реестр) — approvalMinRole = ordinis:restricted гарантирует что approve может только user с restricted-scope ролью, даже если у него есть reviewer на других dict'ах. ordinis:admin super-role проходит всегда.

Поле сохраняется в DB как plain string. Backend (DraftService.requireDictMinRoleIfSet, SchemaDraftService.requireDictMinRoleIfSet) делает workflowRoles.requireRole(approvalMinRole) перед состоянием перехода.

Назначение пользователю

  1. Users → найди user'аRole mapping.
  2. Assign role → отметь нужные роли (или ordinis:admin для админов).

Применение

Пользователю нужно logout / login в Ordinis UI, чтобы получить новый JWT с обновлённым realm_access.roles claim'ом.

Диагностика

«Не могу нажать Approve — кнопка скрыта»

UI скрывает кнопку если у user'а нет нужной workflow роли. Проверить:

  1. F12 → Application → Session Storage → oidc.user:....
  2. В JSON найти profile.realm_access.roles.
  3. Должна быть ordinis:schema:reviewer (для schema) или ordinis:record:reviewer (для record).

Если роли нет — назначить в Keycloak и сделать logout/login.

«403 forbidden_role» от backend

Backend вернул 403 с кодом forbidden_role. Сообщение содержит требуемую роль. Назначь её в Keycloak и refresh JWT.

«403 self_approve_forbidden»

Пользователь пытается approve свой собственный draft. Это invariant maker-checker — backend защищает от self-approve. Нужен другой reviewer.

«404 / пустая страница на /webhooks»

Webhook subscriptions требуют internal или restricted scope. Назначь ordinis:client:internal минимум.

История

  • Phase 1a/1b/1c (до 2026-05-12) — backend гейтил только maker-checker, любой authenticated мог review.
  • Phase 1d (2026-05-13) — backend проверяет workflow роли; frontend скрывает кнопки. Migration require: создать роли в Keycloak и assign actual reviewer-users до или одновременно с deploy.
  • Phase 2 (2026-05-13, vечером) — per-dict approvalMinRole enforced. Поле существовало с миграции 0019, но backend его игнорировал (UI hint прямо говорил "(Phase 2 — enforcement TBD)"). Теперь required gate в approve/reject/decide/publish.