Files
SnapOtter/apps/docs/ru/guide/scim.md
T
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
2026-07-27 15:37:30 +08:00

304 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: "Настройка провижининга SCIM 2.0 для синхронизации пользователей и групп из вашего поставщика идентификации в SnapOtter. Охватывает Okta, Azure AD / Entra ID и пользовательские интеграции."
i18n_source_hash: 06ee702b386e
i18n_provenance: human
i18n_output_hash: 9a5a2bfad94c
i18n_hash_version: 2
---
# Провижининг SCIM {#scim-provisioning}
SnapOtter реализует SCIM 2.0 (System for Cross-domain Identity Management) для автоматического провижининга пользователей и групп. Ваш поставщик идентификации может создавать, обновлять, деактивировать и повторно активировать учётные записи пользователей, а также автоматически синхронизировать членство в группах.
::: tip Функция уровня Enterprise
Провижининг SCIM требует лицензии **enterprise** с функцией `scim`. Он недоступен в плане team. Без этой функции все конечные точки SCIM (кроме discovery) возвращают 403.
:::
## Предварительные требования {#prerequisites}
- Работающий экземпляр SnapOtter, доступный по публичному URL
- Ключ лицензии enterprise с функцией `scim`
- Встроенная учетная запись SnapOtter `admin` с полным действующим набором разрешений. Делегированная пользовательская роль или ключ API администратора, у которого отсутствуют какие-либо разрешения администратора, не могут создать или отозвать глобальный токен SCIM.
- Административный доступ к настройкам провижининга вашего поставщика идентификации
## Быстрый старт {#quick-start}
1. Сгенерируйте bearer-токен SCIM:
```bash
curl -X POST https://photos.example.com/api/v1/enterprise/scim/token \
-H "Cookie: snapotter-session=YOUR_SESSION" \
-H "Content-Type: application/json"
```
Ответ содержит токен. Сохраните его немедленно; повторно его получить нельзя.
```json
{
"token": "so_scim_v2_a1b2c3d4e5f6...",
"message": "Save this token - it cannot be retrieved again"
}
```
2. В вашем поставщике идентификации настройте провижининг SCIM со следующими параметрами:
- **Base URL**: `https://photos.example.com/api/v1/scim/v2`
- **Authentication**: Bearer-токен (вставьте токен из шага 1)
## Аутентификация {#authentication}
Конечные точки SCIM используют выделенный Bearer-токен, отдельный от пользовательских сессий и ключей API.
### Генерация токена {#generating-a-token}
`POST /api/v1/enterprise/scim/token` генерирует новый токен SCIM. Поскольку токен может подготавливать и изменять пользователей в экземпляре, для этой конечной точки требуется встроенная роль `admin` с полным набором эффективных разрешений администратора. Удерживать `users:manage` в пользовательской роли недостаточно.
Токен возвращается в открытом виде ровно один раз. SnapOtter хранит только scrypt-хеш. Если вы потеряете токен, отзовите его и сгенерируйте новый.
Одновременно активен только один токен SCIM. Генерация нового токена заменяет предыдущий.
::: warning Перевыпуск токена после обновления
Устаревшие неверсионные токены SCIM отклоняются. После обновления до версии, которая выдает токены `so_scim_v2_...`, создайте новый токен и обновите поставщика удостоверений, прежде чем возобновить подготовку.
:::
### Отзыв токена {#revoking-a-token}
`DELETE /api/v1/enterprise/scim/token` отзывает текущий токен SCIM. Он имеет те же полностью встроенные требования администратора, что и генерация токенов.
### Ограничение частоты запросов {#rate-limiting}
Конечные точки SCIM ограничены 1000 запросами в минуту на токен. Превышение этого лимита возвращает HTTP 429.
## Поддерживаемые ресурсы {#supported-resources}
| Ресурс SCIM | Концепция SnapOtter | Создание | Чтение | Обновление | Удаление |
|---|---|---|---|---|---|
| User | Учётная запись пользователя | Да | Да | Да | Мягкое удаление |
| Group | Team | Да | Да | Да | Да |
::: warning
Группы SCIM сопоставляются с **командами** SnapOtter, а не с ролями. SCIM не может задать роль пользователя. Всем пользователям, созданным через SCIM, назначается роль `user`. Чтобы изменить роль пользователя, используйте админ-интерфейс SnapOtter.
:::
## Операции с пользователями {#user-operations}
### Создание пользователя {#create-user}
`POST /api/v1/scim/v2/Users`
Создаёт новую учётную запись пользователя с `authProvider`, установленным в `scim`, и ролью `user`. Пользователь назначается в команду Default. Если `active` равно `false`, роль вместо этого устанавливается в `disabled`.
Обязательные атрибуты: `userName`. Необязательные: `externalId`, `emails`, `active` (по умолчанию `true`).
### Список и фильтрация пользователей {#list-and-filter-users}
`GET /api/v1/scim/v2/Users`
Возвращает постраничный список пользователей. Поддерживает параметры запроса `startIndex` и `count` (максимум 200 результатов на страницу).
Фильтрация поддерживает только `eq` (равно) по следующим атрибутам:
- `userName eq "jane"`
- `externalId eq "ext-12345"`
Другие операторы фильтрации и атрибуты возвращают HTTP 400.
### Получение пользователя {#get-user}
`GET /api/v1/scim/v2/Users/:id`
Возвращает одного пользователя по его идентификатору пользователя SnapOtter.
### Замена пользователя {#replace-user}
`PUT /api/v1/scim/v2/Users/:id`
Заменяет атрибуты пользователя. Поддерживает `userName`, `externalId`, `emails` и `active`. Изменения имени пользователя проверяются на конфликты (409, если новое имя пользователя уже занято другим пользователем).
### Частичное обновление пользователя {#patch-user}
`PATCH /api/v1/scim/v2/Users/:id`
Частичное обновление с использованием SCIM PatchOp. Поддерживаемые операции:
| Операция | Пути |
|---|---|
| `replace` | `active`, `userName`, `externalId`, `emails`, `emails[type eq "work"].value`, `name.formatted`, `displayName` |
| `add` | То же, что и `replace` |
| `remove` | `externalId`, `emails` |
Пути `name.formatted` и `displayName` принимаются для совместимости, но не имеют постоянного эффекта (SnapOtter не хранит отдельное отображаемое имя).
Операции `replace` без значения (где значение является объектом без `path`) также поддерживаются, с ключами `userName`, `externalId`, `emails` и `active`.
### Деактивация пользователя (мягкое удаление) {#deactivate-user-soft-delete}
`DELETE /api/v1/scim/v2/Users/:id`
SnapOtter не удаляет пользователей окончательно через SCIM. Вместо этого DELETE выполняет мягкую деактивацию:
1. Роль пользователя меняется с текущего значения (например, `editor`) на `disabled:editor`, сохраняя исходную роль.
2. Пароль пользователя очищается.
3. Все активные сессии отзываются.
4. Все ключи API отзываются.
Пользователь больше не может входить в систему или использовать какие-либо ключи API. Его данные (файлы, история) сохраняются.
### Повторная активация пользователя {#reactivate-user}
Чтобы повторно активировать ранее деактивированного пользователя, отправьте запрос `PUT` или `PATCH` с `active: true`. SnapOtter восстанавливает исходную роль, существовавшую до деактивации (например, `disabled:editor` снова становится `editor`). Если исходную роль определить не удаётся, она возвращается к `user`.
::: details Пример: деактивация и повторная активация через PATCH
```json
// Deactivate
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{ "op": "replace", "path": "active", "value": false }
]
}
// Reactivate
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{ "op": "replace", "path": "active", "value": true }
]
}
```
:::
## Операции с группами {#group-operations}
Группы SCIM сопоставляются с командами SnapOtter. Создание группы создаёт команду. Членство в группе определяет, к какой команде принадлежит пользователь.
### Создание группы {#create-group}
`POST /api/v1/scim/v2/Groups`
Обязательно: `displayName`. Необязательно: `members` (массив `{ value: userId }`).
### Список и фильтрация групп {#list-and-filter-groups}
`GET /api/v1/scim/v2/Groups`
Фильтрация поддерживает только `displayName eq "..."`. Постраничный вывод с `startIndex` и `count` (максимум 200 результатов на страницу).
### Получение группы {#get-group}
`GET /api/v1/scim/v2/Groups/:id`
### Замена группы {#replace-group}
`PUT /api/v1/scim/v2/Groups/:id`
Заменяет имя группы и полный список членства. Существующие члены, отсутствующие в новом списке, перемещаются в команду Default.
### Частичное обновление группы {#patch-group}
`PATCH /api/v1/scim/v2/Groups/:id`
Поддерживает следующие операции:
| Операция | Путь | Эффект |
|---|---|---|
| `add` | `members` | Добавляет пользователей в команду |
| `remove` | `members[value eq "userId"]` | Перемещает пользователя в команду Default |
| `replace` | `displayName` | Переименовывает команду |
| `replace` | `members` | Заменяет всех членов (удалённые члены перемещаются в команду Default) |
### Удаление группы {#delete-group}
`DELETE /api/v1/scim/v2/Groups/:id`
Удаляет команду. Все члены удалённой команды перемещаются в команду Default. Пользователи не деактивируются и не удаляются.
## Настройка IdP {#idp-setup}
### Okta {#okta}
1. В консоли администратора Okta откройте своё приложение SnapOtter (или создайте новое).
2. Перейдите на вкладку **Provisioning** и нажмите **Configure API Integration**.
3. Отметьте **Enable API Integration** и введите:
- **Base URL**: `https://photos.example.com/api/v1/scim/v2`
- **API Token**: bearer-токен SCIM, сгенерированный выше
4. Нажмите **Test API Credentials**, затем **Save**.
5. В разделе **Provisioning > To App** включите:
- **Create Users**
- **Update User Attributes**
- **Deactivate Users**
6. В разделе **Push Groups** настройте, какие группы Okta синхронизировать как команды SnapOtter.
### Azure AD / Entra ID {#azure-ad-entra-id}
1. В портале Azure перейдите к вашему корпоративному приложению SnapOtter.
2. Перейдите в **Provisioning** и установите **Provisioning Mode** в **Automatic**.
3. В разделе **Admin Credentials** введите:
- **Tenant URL**: `https://photos.example.com/api/v1/scim/v2`
- **Secret Token**: bearer-токен SCIM, сгенерированный выше
4. Нажмите **Test Connection**, затем **Save**.
5. В разделе **Mappings** настройте сопоставления атрибутов пользователей и групп. Значения по умолчанию обычно работают, но убедитесь, что `userName` сопоставляется с `userPrincipalName` или `mail` по вашему усмотрению.
6. Установите **Provisioning Status** в **On** и сохраните.
Azure выполняет провижининг пользователей и групп по фиксированному циклу синхронизации (обычно каждые 40 минут).
## Конечные точки discovery {#discovery-endpoints}
Эти три конечные точки доступны без аутентификации и описывают возможности сервера SCIM:
| Конечная точка | Описание |
|---|---|
| `GET /api/v1/scim/v2/ServiceProviderConfig` | Возможности сервера и поддерживаемые функции |
| `GET /api/v1/scim/v2/Schemas` | Определения схем User и Group |
| `GET /api/v1/scim/v2/ResourceTypes` | Доступные типы ресурсов (User, Group) |
`ServiceProviderConfig` объявляет следующие возможности:
| Функция | Поддерживается |
|---|---|
| Patch | Да |
| Bulk | Нет |
| Filter | Да (максимум 200 результатов, только оператор `eq`) |
| Change password | Нет |
| Sort | Нет |
| ETag | Нет |
## Ограничения {#limitations}
- **Фильтрация**: Поддерживается только оператор `eq`. Сложные фильтры, операторы `and`/`or`, `co` (содержит) и `sw` (начинается с) не реализованы.
- **Массовые операции**: Не поддерживаются.
- **Sort и ETag**: Не поддерживаются.
- **Роли**: SCIM не может назначать роли SnapOtter. Все провизионированные пользователи получают роль `user`.
- **MAX_USERS**: Лимит переменной окружения `MAX_USERS` не применяется при создании пользователей через SCIM. Если вам нужно ограничить количество пользователей, управляйте назначениями в вашем IdP.
- **Один токен**: Одновременно может быть активен только один токен SCIM. Если нескольким IdP нужен доступ к SCIM, они должны использовать общий токен.
- **Группы соответствуют командам**: Группы SCIM соответствуют командам, а не ролям или группам разрешений.
## Устранение неполадок {#troubleshooting}
### 403 "SCIM provisioning requires an enterprise license with the scim feature" {#_403-scim-provisioning-requires-an-enterprise-license-with-the-scim-feature}
Ваша лицензия не включает функцию `scim` или лицензия не настроена. SCIM требует лицензию плана enterprise. Убедитесь, что `SNAPOTTER_LICENSE_KEY` установлена и лицензия включает функцию `scim`.
### 401 "Bearer token required" {#_401-bearer-token-required}
Запрос SCIM не содержал заголовка `Authorization: Bearer <token>`. Проверьте конфигурацию провижининга вашего IdP.
### 401 "Invalid token" {#_401-invalid-token}
Токен имеет неверный формат, использует устаревший неверсионный формат или не соответствует сохраненному хешу. Создайте текущий токен `so_scim_v2_...` и обновите его в настройках подготовки IdP.
### 401 "SCIM not configured" {#_401-scim-not-configured}
Токен SCIM ещё не был сгенерирован. Используйте конечную точку `POST /api/v1/enterprise/scim/token` для его создания.
### 409 "User already exists" / "userName already taken" {#_409-user-already-exists-username-already-taken}
Пользователь с таким же именем пользователя уже существует. Это может произойти, когда IdP повторяет неудавшуюся операцию создания. Проверьте наличие дубликатов имён пользователей в панели администратора SnapOtter.
### 429 "SCIM rate limit exceeded" {#_429-scim-rate-limit-exceeded}
IdP отправляет более 1000 запросов в минуту. Обычно это происходит во время большой первоначальной синхронизации. Большинство IdP автоматически повторяют запросы после сброса окна ограничения частоты. Если проблема сохраняется, проверьте интервал синхронизации провижининга вашего IdP.
### Пользователи деповизионированы, но не удалены из интерфейса {#users-deprovisioned-but-not-removed-from-the-ui}
DELETE в SCIM выполняет мягкую деактивацию. Деактивированные пользователи по-прежнему отображаются в списке пользователей администратора со статусом «отключён». Это сделано намеренно, чтобы их данные сохранялись. Их роль отображается как `disabled:<original-role>`.