# Роли доступа и матрица прав 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) | Backend проверяет роль перед каждым действием. Без роли — `403 forbidden_role` с сообщением о требуемой роли. Frontend дополнительно скрывает кнопки, для которых нет роли. > **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 (полный доступ) - `ordinis:client:restricted` - `ordinis:schema:reviewer` - `ordinis:schema:publisher` - `ordinis:record:reviewer` Или **composite role** `ordinis:admin`, которая агрегирует все четыре. ## Настройка в Keycloak ### Создание ролей 1. Открой **Keycloak Admin Console** → realm `nstart` → **Realm 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` (опционально, но удобно) 3. **Create role** → `ordinis:admin`. 4. На вкладке роли → **Action** → **Add 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.