iac/apps/iam/openapi.yaml

1171 lines
44 KiB
YAML
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.

openapi: 3.0.3
info:
title: iams-v2 API
version: "1.0.0"
description: |
REST API сервиса **iams** (`platform/iams-v2`) — управление пользователями,
ресурсами/проектами, оргструктурой (должности, подразделения, группы) и
правами доступа (IAM).
Сервис написан на Go (**Fiber**). Приложение собирается в
`internal/controller/http/server.go` (`NewServer`). Роутинг состоит из двух
контуров:
- **внутренний** — группы `/api/v0`, `/api/admin/v0`, `/api/v1`, `/api/v2`;
аутентификации на уровне приложения нет, доступ ограничивается сетевым
слоем (через ingress не публикуется);
- **внешний** — группы `/external/api/v0`, `/external/api/admin/v0`,
`/external/api/v1`, `/external/api/v2`; регистрируется только при
`AUTH_ENABLED=true`, публикует подмножество внутренних маршрутов (часть —
только на чтение).
Health-check доступен по `GET /api/health`.
### Аутентификация
Внешние эндпоинты требуют заголовок `Authorization: Bearer <jwt>`
(`internal/controller/http/jwt/parser.go`). **Подпись токена не проверяется** —
доверие делегируется вышестоящему gateway/virtual service. Схема разбора
выбирается по заголовку `Identity`:
1. заголовок `Identity` отсутствует/пуст → legacy-формат (Django SimpleJWT);
2. заголовок `Identity` задан → Zitadel (клейм
`urn:zitadel:iam:user:metadata`, значения полей — base64).
Внутренние эндпоинты (`/api/*`) аутентификации на уровне приложения не требуют.
### Пагинация
Списочные ответы используют `limit`/`offset` (по умолчанию `limit=1000`) и
оборачиваются в `{ 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`.
servers:
- url: /api
description: Внутренний контур (без auth)
- url: /external/api
description: Внешний контур (JWT, только при AUTH_ENABLED=true)
tags:
- name: health
- name: users
- name: permission-check
- name: groups
- name: positions
- name: departments
- name: django-permissions
- name: resource-types
- name: resources-v0
- name: projects-v0
- name: widgets-v0
- name: resources
- name: widgets
- name: resource-permissions
- name: company-resource-permissions
- name: service-accounts
paths:
/health:
get:
tags: [health]
summary: Health-check
description: Проверка БД и (при включении) Kafka. Обслуживается как `/api/health`.
security: []
responses:
"200":
description: Сервис доступен
content:
application/json:
schema:
type: object
properties:
status: { type: string, example: ok }
"503":
description: Недоступна зависимость (БД/Kafka)
content:
application/json:
schema:
type: object
properties:
status: { type: string, example: unhealthy }
error: { type: string, example: database_unavailable }
/v0/users:
get:
tags: [users]
summary: Список пользователей
parameters:
- $ref: "#/components/parameters/Limit"
- $ref: "#/components/parameters/Offset"
- { name: username, in: query, schema: { type: string } }
- { name: search, in: query, schema: { type: string } }
- { name: is_active, in: query, schema: { type: boolean } }
- { name: is_staff, in: query, schema: { type: boolean } }
- { name: is_superuser, in: query, schema: { type: boolean } }
- { name: service_account_id, in: query, schema: { type: string, format: uuid } }
- { name: id, in: query, description: "CSV из id", schema: { type: string } }
- { name: company_id, in: query, schema: { type: integer } }
- { name: departments, in: query, description: "CSV", schema: { type: string } }
- { name: positions, in: query, description: "CSV", schema: { type: string } }
- { name: groups, in: query, description: "CSV", schema: { type: string } }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
count: { type: integer }
results:
type: array
items: { $ref: "#/components/schemas/User" }
default: { $ref: "#/components/responses/Error" }
post:
tags: [users]
summary: Создать пользователя
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/UserCreateInput" }
responses:
"201":
description: Создан
content:
application/json:
schema: { $ref: "#/components/schemas/User" }
default: { $ref: "#/components/responses/Error" }
patch:
tags: [users]
summary: Массовое обновление пользователей (legacy bulk_update)
requestBody:
required: true
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/UserBulkPatchItem" }
responses:
"200":
description: OK
default: { $ref: "#/components/responses/Error" }
/v0/users/{id}:
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 } }
get:
tags: [users]
summary: Пользователь по id
responses:
"200":
description: OK
content:
application/json:
schema: { $ref: "#/components/schemas/User" }
default: { $ref: "#/components/responses/Error" }
put:
tags: [users]
summary: Полное обновление пользователя
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/UserUpdateInput" }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
patch:
tags: [users]
summary: Частичное обновление пользователя
requestBody:
required: true
content:
application/json:
schema: { type: object }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
delete:
tags: [users]
summary: Удалить пользователя
responses:
"204": { description: Удалён }
default: { $ref: "#/components/responses/Error" }
/admin/v0/users/activation:
post:
tags: [users]
summary: Активация/деактивация пользователей компании
requestBody:
required: true
content:
application/json:
schema:
type: array
items: { $ref: "#/components/schemas/CompanyUserActivationItem" }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
/v1/users-with-resources:
post:
tags: [users]
summary: Пользователи с их ресурсами
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [tenant_id, id]
properties:
tenant_id: { type: integer, format: int64 }
id:
type: array
items: { type: integer, format: int64 }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
/v1/users-grouped-by-resource/:
get:
tags: [users]
summary: Пользователи, сгруппированные по ресурсу
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
/v0/permissions-check:
post:
tags: [permission-check]
summary: Проверка прав пользователя
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/UserPermissionCheckInput" }
responses:
"200":
description: OK
content:
application/json:
schema: { $ref: "#/components/schemas/UserPermissionCheckOutput" }
default: { $ref: "#/components/responses/Error" }
/v0/groups:
get:
tags: [groups]
summary: Список групп
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
post:
tags: [groups]
summary: Создать группу
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/GroupCreateInput" }
responses:
"201": { description: Создана }
default: { $ref: "#/components/responses/Error" }
/v0/groups/search:
post:
tags: [groups]
summary: Поиск групп
requestBody:
required: true
content:
application/json:
schema: { type: object }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
/v0/groups/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [groups]
summary: Группа по id
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
put:
tags: [groups]
summary: Обновить группу
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/GroupUpdateInput" } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
patch:
tags: [groups]
summary: Частично обновить группу
requestBody:
required: true
content: { application/json: { schema: { type: object } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
delete:
tags: [groups]
summary: Удалить группу
responses:
"204": { description: Удалена }
default: { $ref: "#/components/responses/Error" }
/v0/positions:
get:
tags: [positions]
summary: Список должностей
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
post:
tags: [positions]
summary: Создать должность
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/PositionCreateInput" } } }
responses:
"201": { description: Создана }
default: { $ref: "#/components/responses/Error" }
/v0/positions/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [positions]
summary: Должность по id
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
put:
tags: [positions]
summary: Обновить должность
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/PositionUpdateInput" } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
patch:
tags: [positions]
summary: Частично обновить должность
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
delete:
tags: [positions]
summary: Удалить должность
responses:
"204": { description: Удалена }
default: { $ref: "#/components/responses/Error" }
/v0/departments:
get:
tags: [departments]
summary: Список подразделений
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
post:
tags: [departments]
summary: Создать подразделение
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/DepartmentCreateInput" } } }
responses:
"201": { description: Создано }
default: { $ref: "#/components/responses/Error" }
/v0/departments/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [departments]
summary: Подразделение по id
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
put:
tags: [departments]
summary: Обновить подразделение
requestBody:
required: true
content: { application/json: { schema: { $ref: "#/components/schemas/DepartmentUpdateInput" } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
patch:
tags: [departments]
summary: Частично обновить подразделение
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
delete:
tags: [departments]
summary: Удалить подразделение
responses:
"204": { description: Удалено }
default: { $ref: "#/components/responses/Error" }
/v0/permissions:
get:
tags: [django-permissions]
summary: Список permission'ов
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
post:
tags: [django-permissions]
summary: Создать permission
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name, codename]
properties:
name: { type: string }
codename: { type: string }
responses:
"201": { description: Создан }
default: { $ref: "#/components/responses/Error" }
/v0/permissions/search:
post:
tags: [django-permissions]
summary: Поиск permission'ов
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
/v0/permissions/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [django-permissions]
summary: Permission по id
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
delete:
tags: [django-permissions]
summary: Удалить permission
responses:
"204": { description: Удалён }
default: { $ref: "#/components/responses/Error" }
/v0/resource-types:
get:
tags: [resource-types]
summary: Список типов ресурсов
responses:
"200": { description: OK }
default: { $ref: "#/components/responses/Error" }
post:
tags: [resource-types]
summary: Создать тип
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name: { type: string }
parent_id: { type: integer, format: int64, nullable: true }
responses:
"201": { description: Создан }
default: { $ref: "#/components/responses/Error" }
/v0/resource-types/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [resource-types]
summary: Тип по id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
put:
tags: [resource-types]
summary: Обновить тип
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
patch:
tags: [resource-types]
summary: Частично обновить тип
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [resource-types]
summary: Удалить тип
responses: { "204": { description: Удалён }, default: { $ref: "#/components/responses/Error" } }
/v0/resources:
get:
tags: [resources-v0]
summary: Список ресурсов (v0)
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
count: { type: integer }
results:
type: array
items: { $ref: "#/components/schemas/ResourceV0Output" }
default: { $ref: "#/components/responses/Error" }
post:
tags: [resources-v0]
summary: Создать ресурс
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name, tenant_id]
properties:
name: { type: string }
parent_id: { type: string, nullable: true }
type_id: { type: integer, format: int64 }
tenant_id: { type: integer, format: int64, minimum: 1 }
created_by: { type: integer, format: int64 }
responses: { "201": { description: Создан }, default: { $ref: "#/components/responses/Error" } }
/v0/resources/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: string } } ]
get:
tags: [resources-v0]
summary: Ресурс по id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
put:
tags: [resources-v0]
summary: Обновить ресурс
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
patch:
tags: [resources-v0]
summary: Частично обновить ресурс
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [resources-v0]
summary: Удалить ресурс
responses: { "204": { description: Удалён }, default: { $ref: "#/components/responses/Error" } }
/v0/projects:
get:
tags: [projects-v0]
summary: Список проектов
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
post:
tags: [projects-v0]
summary: Создать проект
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "201": { description: Создан }, default: { $ref: "#/components/responses/Error" } }
/v0/projects/{publicID}:
parameters: [ { name: publicID, in: path, required: true, schema: { type: string, format: uuid } } ]
get:
tags: [projects-v0]
summary: Проект по public id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
put:
tags: [projects-v0]
summary: Обновить проект
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
patch:
tags: [projects-v0]
summary: Частично обновить проект
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [projects-v0]
summary: Удалить проект
responses: { "204": { description: Удалён }, default: { $ref: "#/components/responses/Error" } }
/v0/widgets:
get:
tags: [widgets-v0]
summary: Список виджетов
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
post:
tags: [widgets-v0]
summary: Создать виджет
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "201": { description: Создан }, default: { $ref: "#/components/responses/Error" } }
/v0/widgets/{publicID}:
parameters: [ { name: publicID, in: path, required: true, schema: { type: string, format: uuid } } ]
get:
tags: [widgets-v0]
summary: Виджет по public id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
put:
tags: [widgets-v0]
summary: Обновить виджет
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
patch:
tags: [widgets-v0]
summary: Частично обновить виджет
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [widgets-v0]
summary: Удалить виджет
responses: { "204": { description: Удалён }, default: { $ref: "#/components/responses/Error" } }
/v1/resource:
get:
tags: [resources]
summary: Список ресурсов (v1)
parameters:
- $ref: "#/components/parameters/Limit"
- $ref: "#/components/parameters/Offset"
- { name: parent_id, in: query, schema: { type: string, format: uuid } }
- { name: type, in: query, schema: { type: integer } }
- { name: tenant_id, in: query, description: "CSV", schema: { type: string } }
- { name: name, in: query, schema: { type: string } }
- { name: code, in: query, schema: { type: string } }
- { name: public_id, in: query, schema: { type: string, format: uuid } }
- { name: target_id, in: query, schema: { type: integer } }
- { name: service_accounts, in: query, description: "CSV из uuid", schema: { type: string } }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
count: { type: integer }
results:
type: array
items: { $ref: "#/components/schemas/Resource" }
default: { $ref: "#/components/responses/Error" }
post:
tags: [resources]
summary: Создать ресурс
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/ResourceCreateInput" }
responses: { "201": { description: Создан }, default: { $ref: "#/components/responses/Error" } }
/v1/resource/{publicID}:
parameters: [ { name: publicID, in: path, required: true, schema: { type: string, format: uuid } } ]
get:
tags: [resources]
summary: Ресурс по public id
responses:
"200":
description: OK
content: { application/json: { schema: { $ref: "#/components/schemas/Resource" } } }
default: { $ref: "#/components/responses/Error" }
put:
tags: [resources]
summary: Обновить ресурс
requestBody: { required: true, content: { application/json: { schema: { $ref: "#/components/schemas/ResourceUpdateInput" } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
patch:
tags: [resources]
summary: Частично обновить ресурс
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [resources]
summary: Удалить ресурс
responses: { "204": { description: Удалён }, default: { $ref: "#/components/responses/Error" } }
/v1/targets/{target_id}/resource:
parameters: [ { name: target_id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [resources]
summary: Ресурс по target id (только внутренний контур)
responses:
"200":
description: OK
content: { application/json: { schema: { $ref: "#/components/schemas/Resource" } } }
default: { $ref: "#/components/responses/Error" }
/v1/resources-grouped-by-sa:
get:
tags: [resources]
summary: Ресурсы, сгруппированные по сервисному аккаунту
description: Регистрируется, только если включён соответствующий use case.
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
/v1/service-accounts:
get:
tags: [service-accounts]
summary: Список сервисных аккаунтов
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
count: { type: integer }
results:
type: array
items: { type: string, format: uuid }
default: { $ref: "#/components/responses/Error" }
/v2/resource:
get:
tags: [widgets]
summary: Список виджетов (v2)
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
post:
tags: [widgets]
summary: Создать виджет
requestBody: { required: true, content: { application/json: { schema: { $ref: "#/components/schemas/WidgetCreateInput" } } } }
responses: { "201": { description: Создан }, default: { $ref: "#/components/responses/Error" } }
/v2/resource/{publicID}:
parameters: [ { name: publicID, in: path, required: true, schema: { type: string, format: uuid } } ]
get:
tags: [widgets]
summary: Виджет по public id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
put:
tags: [widgets]
summary: Обновить виджет
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
patch:
tags: [widgets]
summary: Частично обновить виджет
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [widgets]
summary: Удалить виджет
responses: { "204": { description: Удалён }, default: { $ref: "#/components/responses/Error" } }
/v1/resource_permission:
get:
tags: [resource-permissions]
summary: Список прав на ресурсы
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
post:
tags: [resource-permissions]
summary: Создать права (пакет)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items: { $ref: "#/components/schemas/ResourcePermissionCreateInput" }
responses: { "201": { description: Создано }, default: { $ref: "#/components/responses/Error" } }
/v1/resource_permission/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [resource-permissions]
summary: Право по id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [resource-permissions]
summary: Удалить право
responses: { "204": { description: Удалено }, default: { $ref: "#/components/responses/Error" } }
/v1/permissions-bulk:
patch:
tags: [resource-permissions]
summary: Массовое изменение прав
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [tenant_id, permissions]
properties:
tenant_id: { type: integer, format: int64, minimum: 1 }
permissions:
type: object
description: "map[resource_uuid][]permission_uuid"
additionalProperties:
type: array
items: { type: string, format: uuid }
unrestricted_permissions:
type: object
additionalProperties: true
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
/v1/company_resource_permission:
get:
tags: [company-resource-permissions]
summary: Список прав компании
parameters: [ { $ref: "#/components/parameters/Limit" }, { $ref: "#/components/parameters/Offset" } ]
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
post:
tags: [company-resource-permissions]
summary: Создать права компании (пакет)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items: { $ref: "#/components/schemas/CompanyResourcePermissionCreateInput" }
responses: { "201": { description: Создано }, default: { $ref: "#/components/responses/Error" } }
/v1/company_resource_permission/{id}:
parameters: [ { name: id, in: path, required: true, schema: { type: integer, format: int64 } } ]
get:
tags: [company-resource-permissions]
summary: Право компании по id
responses: { "200": { description: OK }, default: { $ref: "#/components/responses/Error" } }
delete:
tags: [company-resource-permissions]
summary: Удалить право компании
responses: { "204": { description: Удалено }, default: { $ref: "#/components/responses/Error" } }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
`Authorization: Bearer <jwt>`. Требуется во внешнем контуре
(`/external/api/*`). Дополнительный заголовок `Identity` переключает
разбор в режим Zitadel. Подпись не проверяется приложением.
parameters:
Limit:
name: limit
in: query
description: Размер страницы (по умолчанию 1000)
schema: { type: integer, default: 1000, minimum: 1 }
Offset:
name: offset
in: query
schema: { type: integer, default: 0, minimum: 0 }
responses:
Error:
description: Ошибка
content:
application/json:
schema: { $ref: "#/components/schemas/ApiError" }
schemas:
ApiError:
type: object
properties:
error: { type: string, description: Человекочитаемое сообщение }
id: { type: string, description: Стабильный slug доменной ошибки }
User:
type: object
properties:
id: { type: integer, format: int64 }
username: { type: string }
email: { type: string, format: email }
first_name: { type: string }
last_name: { type: string }
is_staff: { type: boolean }
is_active: { type: boolean }
is_superuser: { type: boolean }
job_title: { type: string }
companies:
type: array
items: { type: integer, format: int64 }
UserCreateInput:
type: object
required: [username, email, first_name, last_name]
properties:
username: { type: string }
email: { type: string, format: email }
first_name: { type: string }
last_name: { type: string }
is_staff: { type: boolean }
is_active: { type: boolean }
is_superuser: { type: boolean }
job_title: { type: string }
companies:
type: array
items: { type: integer, format: int64 }
departments:
type: array
items: { type: integer, format: int64 }
groups:
type: array
items: { type: integer, format: int64 }
enable_notifications: { type: boolean }
UserUpdateInput:
type: object
required: [username, email]
properties:
username: { type: string }
email: { type: string, format: email }
first_name: { type: string }
last_name: { type: string }
is_staff: { type: boolean }
is_active: { type: boolean }
is_superuser: { type: boolean }
job_title: { type: string }
companies:
type: array
items: { type: integer, format: int64 }
UserBulkPatchItem:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
username: { type: string }
email: { type: string, format: email }
first_name: { type: string }
last_name: { type: string }
is_staff: { type: boolean }
is_active: { type: boolean }
is_superuser: { type: boolean }
job_title: { type: string }
companies: { type: array, items: { type: integer, format: int64 } }
departments: { type: array, items: { type: integer, format: int64 } }
groups: { type: array, items: { type: integer, format: int64 } }
enable_notifications: { type: boolean }
CompanyUserActivationItem:
type: object
required: [user_id, company_id]
properties:
user_id: { type: integer, format: int64, minimum: 1 }
company_id: { type: integer, format: int64, minimum: 1 }
is_active: { type: boolean }
UserPermissionCheckInput:
type: object
required: [user_id, checks]
properties:
user_id: { type: integer, format: int64, minimum: 1 }
tenant_id: { type: integer, format: int64 }
checks:
type: array
minItems: 1
maxItems: 100
items:
type: object
required: [permission]
properties:
permission: { type: string }
public_resource_id: { type: string, format: uuid, nullable: true }
UserPermissionCheckOutput:
type: object
properties:
results:
type: array
items:
type: object
properties:
permission: { type: string }
public_resource_id: { type: string, format: uuid, nullable: true }
allowed: { type: boolean }
reason: { type: string }
GroupCreateInput:
type: object
required: [name]
properties:
name: { type: string }
description: { type: string }
is_public: { type: boolean }
company_id: { type: integer, format: int64, nullable: true }
permission_ids: { type: array, items: { type: integer, format: int64 } }
GroupUpdateInput:
allOf:
- $ref: "#/components/schemas/GroupCreateInput"
- type: object
required: [id]
properties:
id: { type: integer, format: int64 }
PositionCreateInput:
type: object
required: [name, company_id]
properties:
name: { type: string }
description: { type: string }
company_id: { type: integer, format: int64, minimum: 1 }
users: { type: array, items: { type: integer, format: int64 } }
groups: { type: array, items: { type: integer, format: int64 } }
PositionUpdateInput:
allOf:
- $ref: "#/components/schemas/PositionCreateInput"
- type: object
required: [id]
properties:
id: { type: integer, format: int64 }
DepartmentCreateInput:
type: object
required: [name, company_id]
properties:
name: { type: string }
description: { type: string }
company_id: { type: integer, format: int64, minimum: 1 }
legal_entity: { type: string }
contractor: { type: object, nullable: true }
users: { type: array, items: { type: integer, format: int64 } }
groups: { type: array, items: { type: integer, format: int64 } }
DepartmentUpdateInput:
allOf:
- $ref: "#/components/schemas/DepartmentCreateInput"
- type: object
required: [id]
properties:
id: { type: integer, format: int64 }
Resource:
type: object
properties:
public_id: { type: string, format: uuid }
parent_id: { type: string, format: uuid, nullable: true }
type_id: { type: integer, format: int64 }
tenant_id: { type: integer, format: int64 }
name: { type: string }
description: { type: string }
code: { type: string }
target_id: { type: integer, format: int64 }
show_in_overview: { type: boolean }
location_verbose: { type: string }
latitude: { type: number, format: double }
longitude: { type: number, format: double }
widgets: { type: object, additionalProperties: true }
attributes: { type: object, additionalProperties: true }
meta: { type: object, additionalProperties: true }
ResourceCreateInput:
type: object
required: [name, tenant_id]
properties:
parent_id: { type: string, format: uuid, nullable: true, description: "public_id родителя" }
name: { type: string }
type_id: { type: integer, format: int64, description: "0/опущено — тип «Проект»" }
tenant_id: { type: integer, format: int64, minimum: 1 }
created_by: { type: integer, format: int64 }
target_id: { type: integer, format: int64 }
code: { type: string }
ResourceUpdateInput:
type: object
required: [name, tenant_id]
properties:
parent_id: { type: string, format: uuid, nullable: true }
type_id: { type: integer, format: int64 }
tenant_id: { type: integer, format: int64, minimum: 1 }
name: { type: string }
description: { type: string }
code: { type: string }
target_id: { type: integer, format: int64 }
planning_widget_id: { type: integer, format: int64, nullable: true }
work_schedule_project_id: { type: integer, format: int64, nullable: true }
show_in_overview: { type: boolean }
location_verbose: { type: string }
latitude: { type: number, format: double, minimum: -90, maximum: 90 }
longitude: { type: number, format: double, minimum: -180, maximum: 180 }
widgets: { type: object, additionalProperties: true }
attributes: { type: object, additionalProperties: true }
meta: { type: object, additionalProperties: true }
ResourceV0Output:
type: object
properties:
id: { type: string }
public_id: { type: string }
parent_id: { type: string, nullable: true }
path: { type: string }
type_id: { type: integer, format: int64 }
tenant_id: { type: integer, format: int64 }
name: { type: string }
created_by: { type: integer, format: int64 }
created_at: { type: string }
updated_at: { type: string }
WidgetCreateInput:
type: object
required: [name, tenant_id]
properties:
parent_id: { type: string, format: uuid, nullable: true }
type_id: { type: integer, format: int64 }
tenant_id: { type: integer, format: int64, minimum: 1 }
name: { type: string }
description: { type: string }
code: { type: string }
target_id: { type: integer, format: int64 }
show_in_overview: { type: boolean }
latitude: { type: number, format: double, minimum: -90, maximum: 90 }
longitude: { type: number, format: double, minimum: -180, maximum: 180 }
coordinate_system: { type: integer, format: int64 }
widgets: { type: object, additionalProperties: true }
attributes: { type: object, additionalProperties: true }
meta: { type: object, additionalProperties: true }
ResourcePermissionCreateInput:
type: object
required: [public_resource_id, service_account]
properties:
public_resource_id: { type: string, format: uuid }
type: { type: integer, format: int64, minimum: 0 }
service_account: { type: string, format: uuid }
created_by: { type: integer, format: int64 }
CompanyResourcePermissionCreateInput:
type: object
required: [tenant_id, service_account]
properties:
tenant_id: { type: integer, format: int64, minimum: 1 }
service_account: { type: string, format: uuid }
created_by: { type: integer, format: int64 }
security:
- bearerAuth: []