1dba9476db
operator/rbac.md — новая страница с полной матрицей RBAC: - Scope ACL (ordinis:client:public/internal/restricted) - Workflow роли (schema:reviewer, schema:publisher, record:reviewer) - Action matrix (endpoint → required scope + role) - Типичные профили (consumer, maker, reviewer, publisher, admin) - Composite role 'ordinis:admin' для one-shot assignment - Keycloak setup инструкция (Admin Console шаги) - Диагностика типичных 403 ошибок toc.yaml — link в раздел Руководство оператора. operator/index.md, reviewer/index.md — cross-references к rbac.md. Покрывает Phase 1d enforcement (MR !162) — пользователи теперь знают какие роли назначать в Keycloak realm 'nstart'.
145 lines
8.3 KiB
Markdown
145 lines
8.3 KiB
Markdown
# Роли доступа и матрица прав
|
||
|
||
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.
|