208 lines
14 KiB
Markdown
208 lines
14 KiB
Markdown
# Эндпоинты сервиса iams-v2
|
||
|
||
Документ описывает все HTTP-эндпоинты, которые **предоставляет** сервис `iams` (IAM: пользователи, ресурсы/проекты, права доступа). В отличие от фронтенд-модулей, здесь описан контракт самого сервиса.
|
||
|
||
## Как устроено взаимодействие
|
||
|
||
Сервер — Fiber (`internal/controller/http/server.go`). Маршруты регистрируются двумя наборами:
|
||
|
||
- **Внутренний контур** (`RegisterAPIRoutes`) — без аутентификации на уровне приложения, доступ ограничивается сетевым слоем (не публикуется через ingress). Базовые группы: `/api/v0`, `/api/admin/v0`, `/api/v1`, `/api/v2`.
|
||
- **Внешний контур** (`RegisterExternalAPIRoutes`) — регистрируется только при `AUTH_ENABLED=true`. Перед маршрутами выполняется разбор JWT (`AuthMiddleware`) и пометка контекста (`ExternalAPIMiddleware`, для админ-группы дополнительно `ExternalUserAdminAPIMiddleware`). Базовые группы: `/external/api/v0`, `/external/api/admin/v0`, `/external/api/v1`, `/external/api/v2`. Внешний контур публикует **подмножество** внутренних маршрутов, часть — только на чтение.
|
||
|
||
Глобальные middleware: `requestid`, `recover`, OTel (при `OTEL_ENABLED` и заданном `OTEL_SERVICE_NAME`), логирование.
|
||
|
||
### Аутентификация (внешний контур)
|
||
|
||
Токен передаётся заголовком `Authorization: Bearer <jwt>` (`internal/controller/http/jwt/parser.go`). **Подпись токена не проверяется** — доверие делегируется вышестоящему gateway/virtual service. Схема разбора выбирается по заголовку `Identity`:
|
||
|
||
- заголовок `Identity` отсутствует/пуст → legacy-формат (Django SimpleJWT);
|
||
- заголовок `Identity` задан → Zitadel (клейм `urn:zitadel:iam:user:metadata`, значения полей — base64).
|
||
|
||
### Формат ответов и ошибок
|
||
|
||
Успешные ответы — JSON (списки: `{ count, results }`; часть эндпоинтов возвращает объект напрямую). Ошибки — JSON `{ "error": "...", "id": "..." }` (`httpx.Response`). Доменный `Kind` маппится на HTTP-статус (`httpx/error_resolve.go`): `InvalidArgument/OutOfRange`→`400`, `Unauthenticated`→`401`, `PermissionDenied`→`403`, `NotFound`→`404`, `Aborted/AlreadyExists`→`409`, `PreconditionFailed`→`412`, `ResourceExhausted`→`429`, `Unavailable`→`503`, `Internal/Unknown/DataLoss`→`500`. Идентификатор запроса — заголовок из `requestid`.
|
||
|
||
### Пагинация и фильтры
|
||
|
||
Списки — через query `limit`/`offset` (по умолчанию `limit=1000`). Дополнительные фильтры задаются query-параметрами (напр. для `/resource`: `parent_id`, `type`, `tenant_id`, `name`, `code`, `public_id`, `target_id`, `service_accounts`; для `/users`: `username`, `search`, `is_active`, `is_staff`, `is_superuser`, `service_account_id`, `id`, `company_id`, `departments`, `positions`, `groups`).
|
||
|
||
## Служебные эндпоинты
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/health` | Health-check: проверка БД и (при включении) Kafka. `200` — ok, `503` — недоступность зависимости |
|
||
|
||
## Внутренний контур
|
||
|
||
### Users (`/api/v0/users`, `/api/admin/v0`, `/api/v1`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/users` | Список пользователей (фильтры, пагинация) |
|
||
| POST | `/api/v0/users` | Создать пользователя (если включён use case) |
|
||
| PATCH | `/api/v0/users` | Массовое обновление пользователей (legacy bulk_update) |
|
||
| GET | `/api/v0/users/:id` | Пользователь по id |
|
||
| PUT | `/api/v0/users/:id` | Полное обновление пользователя |
|
||
| PATCH | `/api/v0/users/:id` | Частичное обновление пользователя |
|
||
| DELETE | `/api/v0/users/:id` | Удалить пользователя |
|
||
| POST | `/api/admin/v0/users/activation` | Активация/деактивация пользователей компании |
|
||
| POST | `/api/v1/users-with-resources` | Пользователи с их ресурсами (тело: `tenant_id`, `id[]`) |
|
||
| GET | `/api/v1/users-grouped-by-resource/` | Пользователи, сгруппированные по ресурсу (если включён use case) |
|
||
|
||
### Permission check (`/api/v0`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| POST | `/api/v0/permissions-check` | Проверка набора прав пользователя (`user_id`, `checks[]`) |
|
||
|
||
### Groups (`/api/v0/groups`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/groups` | Список групп |
|
||
| POST | `/api/v0/groups` | Создать группу |
|
||
| POST | `/api/v0/groups/search` | Поиск групп (пагинация, сортировка, фильтры) |
|
||
| GET | `/api/v0/groups/:id` | Группа по id |
|
||
| PUT | `/api/v0/groups/:id` | Обновить группу |
|
||
| PATCH | `/api/v0/groups/:id` | Частично обновить группу |
|
||
| DELETE | `/api/v0/groups/:id` | Удалить группу |
|
||
|
||
### Positions (`/api/v0/positions`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/positions` | Список должностей |
|
||
| POST | `/api/v0/positions` | Создать должность |
|
||
| GET | `/api/v0/positions/:id` | Должность по id |
|
||
| PUT | `/api/v0/positions/:id` | Обновить должность |
|
||
| PATCH | `/api/v0/positions/:id` | Частично обновить должность |
|
||
| DELETE | `/api/v0/positions/:id` | Удалить должность |
|
||
|
||
### Departments (`/api/v0/departments`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/departments` | Список подразделений |
|
||
| POST | `/api/v0/departments` | Создать подразделение |
|
||
| GET | `/api/v0/departments/:id` | Подразделение по id |
|
||
| PUT | `/api/v0/departments/:id` | Обновить подразделение |
|
||
| PATCH | `/api/v0/departments/:id` | Частично обновить подразделение |
|
||
| DELETE | `/api/v0/departments/:id` | Удалить подразделение |
|
||
|
||
### Django permissions (`/api/v0/permissions`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/permissions` | Список permission'ов |
|
||
| POST | `/api/v0/permissions` | Создать permission |
|
||
| POST | `/api/v0/permissions/search` | Поиск permission'ов |
|
||
| GET | `/api/v0/permissions/:id` | Permission по id |
|
||
| DELETE | `/api/v0/permissions/:id` | Удалить permission |
|
||
|
||
### Resource types v0 (`/api/v0/resource-types`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/resource-types` | Список типов ресурсов |
|
||
| POST | `/api/v0/resource-types` | Создать тип |
|
||
| GET | `/api/v0/resource-types/:id` | Тип по id |
|
||
| PUT | `/api/v0/resource-types/:id` | Обновить тип |
|
||
| PATCH | `/api/v0/resource-types/:id` | Частично обновить тип |
|
||
| DELETE | `/api/v0/resource-types/:id` | Удалить тип |
|
||
|
||
### Resources v0 (`/api/v0/resources`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/resources` | Список ресурсов (legacy v0) |
|
||
| POST | `/api/v0/resources` | Создать ресурс |
|
||
| GET | `/api/v0/resources/:id` | Ресурс по id |
|
||
| PUT | `/api/v0/resources/:id` | Обновить ресурс |
|
||
| PATCH | `/api/v0/resources/:id` | Частично обновить ресурс |
|
||
| DELETE | `/api/v0/resources/:id` | Удалить ресурс |
|
||
|
||
### Projects v0 (`/api/v0/projects`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/projects` | Список проектов |
|
||
| POST | `/api/v0/projects` | Создать проект |
|
||
| GET | `/api/v0/projects/:publicID` | Проект по public id |
|
||
| PUT | `/api/v0/projects/:publicID` | Обновить проект |
|
||
| PATCH | `/api/v0/projects/:publicID` | Частично обновить проект |
|
||
| DELETE | `/api/v0/projects/:publicID` | Удалить проект |
|
||
|
||
### Widgets v0 (`/api/v0/widgets`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v0/widgets` | Список виджетов |
|
||
| POST | `/api/v0/widgets` | Создать виджет |
|
||
| GET | `/api/v0/widgets/:publicID` | Виджет по public id |
|
||
| PUT | `/api/v0/widgets/:publicID` | Обновить виджет |
|
||
| PATCH | `/api/v0/widgets/:publicID` | Частично обновить виджет |
|
||
| DELETE | `/api/v0/widgets/:publicID` | Удалить виджет |
|
||
|
||
### Resources v1 (`/api/v1/resource`, `/api/v1/targets`, `/api/v1/service-accounts`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v1/resource` | Список ресурсов (v1) |
|
||
| POST | `/api/v1/resource` | Создать ресурс |
|
||
| GET | `/api/v1/resource/:publicID` | Ресурс по public id |
|
||
| PUT | `/api/v1/resource/:publicID` | Обновить ресурс |
|
||
| PATCH | `/api/v1/resource/:publicID` | Частично обновить ресурс |
|
||
| DELETE | `/api/v1/resource/:publicID` | Удалить ресурс |
|
||
| GET | `/api/v1/targets/:target_id/resource` | Ресурс по target id (только внутренний) |
|
||
| GET | `/api/v1/resources-grouped-by-sa` | Ресурсы, сгруппированные по сервисному аккаунту (если включено) |
|
||
| GET | `/api/v1/service-accounts` | Список сервисных аккаунтов |
|
||
|
||
### Widgets v2 (`/api/v2/resource`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v2/resource` | Список виджетов (v2) |
|
||
| POST | `/api/v2/resource` | Создать виджет |
|
||
| GET | `/api/v2/resource/:publicID` | Виджет по public id |
|
||
| PUT | `/api/v2/resource/:publicID` | Обновить виджет |
|
||
| PATCH | `/api/v2/resource/:publicID` | Частично обновить виджет |
|
||
| DELETE | `/api/v2/resource/:publicID` | Удалить виджет |
|
||
|
||
### Permissions v1 (`/api/v1`)
|
||
|
||
| Метод | Путь | Назначение |
|
||
| --- | --- | --- |
|
||
| GET | `/api/v1/resource_permission` | Список прав на ресурсы |
|
||
| POST | `/api/v1/resource_permission` | Создать права (пакет `items[]`) |
|
||
| GET | `/api/v1/resource_permission/:id` | Право по id |
|
||
| DELETE | `/api/v1/resource_permission/:id` | Удалить право |
|
||
| PATCH | `/api/v1/permissions-bulk` | Массовое изменение прав (`tenant_id`, `permissions`) |
|
||
| GET | `/api/v1/company_resource_permission` | Список прав компании на ресурсы |
|
||
| POST | `/api/v1/company_resource_permission` | Создать права компании (пакет `items[]`) |
|
||
| GET | `/api/v1/company_resource_permission/:id` | Право компании по id |
|
||
| DELETE | `/api/v1/company_resource_permission/:id` | Удалить право компании |
|
||
|
||
## Внешний контур (`/external/api/*`, только при `AUTH_ENABLED=true`)
|
||
|
||
Публикуется подмножество внутренних маршрутов; часть — только на чтение. Пути идентичны внутренним, но с префиксом `/external/api`.
|
||
|
||
| Группа | Маршруты | Отличия от внутреннего контура |
|
||
| --- | --- | --- |
|
||
| `/external/api/v0/users` | `GET /`, `GET /:id`, `PATCH /` | Только чтение + массовый PATCH (bulk) |
|
||
| `/external/api/admin/v0/users` | `POST /activation`, полный CRUD `/users` | Полный доступ (админ-группа) |
|
||
| `/external/api/admin/v0/positions` | `/positions` (CRUD) | Как внутренний |
|
||
| `/external/api/admin/v0/departments` | `/departments` (CRUD) | Как внутренний |
|
||
| `/external/api/admin/v0/groups` | `/groups` (CRUD + search) | Как внутренний |
|
||
| `/external/api/admin/v0/permissions` | `/permissions` (django) | Как внутренний |
|
||
| `/external/api/v0/resource-types` | `/resource-types` (CRUD) | Как внутренний |
|
||
| `/external/api/v0/resources` | `/resources` (CRUD, v0) | Как внутренний |
|
||
| `/external/api/v0/projects` | `GET /`, `GET /:publicID` | Только чтение |
|
||
| `/external/api/v0/widgets` | `/widgets` (CRUD) | Как внутренний |
|
||
| `/external/api/v1/resource` | `GET /`, `GET /:publicID` | Только чтение |
|
||
| `/external/api/v2/resource` | `/resource` (виджеты v2, CRUD) | Как внутренний |
|
||
| `/external/api/v1/resource_permission` | CRUD-подмножество | Как внутренний |
|
||
| `/external/api/v1/permissions-bulk` | `PATCH /` | Как внутренний |
|
||
| `/external/api/v1/company_resource_permission` | CRUD-подмножество | Как внутренний |
|
||
|
||
> Часть маршрутов регистрируется условно — только если соответствующий хендлер/use case подключён при инициализации сервера (проверки `Has*` в хендлерах). При отсутствии сервиса группа не регистрируется.
|