Files
mdm-ordinis/docs/operator/rbac.md
T
Zimin A.N. 36c06ae1da feat(auth): WorkflowRoles configurable claim path + ordinis:admin super-role
User report: token has roles в resource_access.ordinis.roles (client-scoped),
backend WorkflowRoles читал hardcoded realm_access.roles → forbidden_role
для всех reviewer actions.

Backend:
- WorkflowRoles: depends OrdinisAuthProperties → reads via rolesClaim path
  (же что ScopeContext). Default realm_access.roles, на staging/prod env
  var ORDINIS_AUTH_ROLES_CLAIM=resource_access.ordinis.roles (уже wired
  в helm helper).
- Nested claim path resolver — mirror ScopeContext.readClaimByPath
- New super-role ADMIN ('ordinis:admin') — holder автоматически проходит
  любой workflow check без явного назначения отдельных ролей. Convenience
  для small teams.

Frontend (usePermissions.ts):
- tokenRoles() читает оба claim path (realm_access.roles +
  resource_access.ordinis.roles) и объединяет — UI gate работает
  независимо от backend config
- hasRole() recognizes 'ordinis:admin' как super-role override
- ROLE_ADMIN exported

Docs:
- docs/operator/rbac.md: добавлен ordinis:admin row, JWT claim path note,
  Admin profile updated (super-role vs composite explanation)
2026-05-13 13:17:54 +03:00

9.1 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'ам, и они автоматически получают все права.

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

  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.