PUT /api/v1/roles/{roleId} — Обновление роли компании
Изменение атрибутов роли компании: Name, Description. Код (Code) иммутабелен; состав permissions
меняется отдельной операцией set-role-permissions.md; архив —
archive-company-role.md. Системные роли неизменяемы (INV-R1).
Назначение
Руководитель переименовывает/уточняет описание внутренней роли своей компании. Owning-контекст —
identity (дом полей — domain/role.md). Feature group — roles.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | identity |
| API area | public |
| Feature group | roles |
| Статус | draft |
| Документ | docs/06-services/identity/api/company-user/update-company-role.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (пользователь компании) |
| Auth policy / Permissions | identity.roles.manage |
| Scope (RBAC) | организация: Active RoleAssignment со scope Platform или Organization(CompanyId) |
| Record-level | роль обязана принадлежать компании актора (role.CompanyId == CompanyId) |
| Tenant isolation | TenantId = CompanyId; INullableTenantedEntity-интерсептор + явный guard |
| Audit | update: UpdatedByUserId, Version += 1, доменное событие RoleUpdatedEvent |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | PUT |
| Route | /api/v1/roles/{roleId} |
| Success status | 200 |
| Idempotency header | не применяется (PUT идемпотентен + Version-сверка) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ActorUserId | identity claims | Guid | Да | Required | — |
CompanyId | identity claims (tenant_id) | Guid | Да | Required | Tenant isolation |
RoleId | route {roleId} | Guid | Да | != Guid.Empty | — |
Name | body | string | Да | 1..128 | — |
Description | body | string? | Нет | <= 512 | — |
Version | body | long | Да | >= 1; сверка с RoleDefinition.Version | — |
Code в bodyCode в body отсутствует намеренно — код роли иммутабелен после создания (аналогия INV-P2).
Модель ответа UpdateRoleCommandResult
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | RoleDefinition.Id | — | Id роли. |
Name | string | Да | RoleDefinition.Name | — | Новое название. |
Version | long | Да | RoleDefinition.Version | — | Новая версия. |
UpdatedAt | DateTimeOffset | Да | RoleDefinition.UpdatedAt | — | Момент сохранения. |
Предусловия и постусловия
Предусловия. Актор аутентифицирован и имеет identity.roles.manage в scope Organization(CompanyId);
роль существует, принадлежит компании актора, не системная (INV-R1) и Active; переданная Version
совпадает с текущей.
Постусловия при успехе. Обновлены Name/Description роли, версия увеличена, зафиксирован автор;
поднят доменный факт RoleUpdatedEvent. Интеграционных событий нет — переименование не меняет
эффективных прав, поэтому UserAccessChangedKafkaEvent не публикуется. Code остаётся прежним
(иммутабелен).
Постусловия при отказе. Атрибуты и версия роли не изменились.
Алгоритм
- Контекст и доступ. Операцию выполняет пользователь компании; tenant — его компания (
CompanyId); требуется правоidentity.roles.manageв scopeOrganization(CompanyId). - Проверка входа и предусловий.
- Синтаксис:
Name 1..128,Description ≤ 512; иначе —ValidationError. - Роль существует и принадлежит компании актора; системная (
CompanyId == null) или чужая →IDENTITY_ROLE_NOT_FOUND(без раскрытия существования чужих ролей). - Роль не системная (INV-R1); системная —
IDENTITY_ROLE_SYSTEM_IMMUTABLE. - Роль в статусе
Active;Archived→IDENTITY_ROLE_ARCHIVED. - Оптимистичная блокировка: переданная
Versionсовпадает с версией роли; иначе —IDENTITY_CONCURRENCY_CONFLICT.
- Синтаксис:
- Что читаем. Роль (для проверок выше); состав прав не нужен.
- Бизнес-правила и маппинг. Обновляются
NameиDescription(кодCodeиммутабелен); версия роли увеличивается, фиксируется автор изменения. Поднимается доменный фактRoleUpdatedEvent. - Что меняется. Обновляется роль. Конкурентный конфликт →
IDENTITY_CONCURRENCY_CONFLICT. - Согласованность. Интеграционных событий нет: переименование не меняет эффективных прав
(
UserAccessChangedKafkaEventпубликуется только при grant/revoke и изменении состава прав, role.md). Роль и доменный фактRoleUpdatedEventфиксируются в одном согласованном изменении. - Результат. Идентификатор роли, новое название, новая версия, момент сохранения (
200).
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
ValidationError | 400 | Длины Name/Description | Список ошибок | no retry |
IDENTITY_ROLE_NOT_FOUND | 404 | Роль не существует / чужой компании / системная под видом своей | «Роль не найдена» | no retry |
IDENTITY_ROLE_SYSTEM_IMMUTABLE | 409 | IsSystem = true (INV-R1) | «Системная роль неизменяема» | no retry |
IDENTITY_ROLE_ARCHIVED | 409 | Status = Archived | «Роль архивирована» | no retry |
IDENTITY_CONCURRENCY_CONFLICT | 409 | Версия роли изменилась параллельно | «Данные изменены параллельно» | retry после перечитывания |
FORBIDDEN | 403 | Нет identity.roles.manage | «Недостаточно прав» | no retry |
Совместимость и наблюдаемость
- Additive-изменения формы допустимы; появление возможности менять
Code— breaking (отдельный ADR). - Logs:
CompanyId,RoleId,ActorUserId; metrics:identity_role_update_total{result}; traces: OTel; dashboardidentity-overview; runbook — не применяется.
Acceptance Criteria
- Given роль компании
Activeи актуальнаяVersion, WhenPUTс новымиName/Description, Then200; атрибуты обновлены, версия увеличена;RoleUpdatedEventзафиксирован;UserAccessChangedKafkaEventне публикуется (права не изменились). - Given попытка передать
Codeв теле, WhenPUT, Then поле игнорируется —Codeостаётся прежним. - Given роль
IsSystem = true, WhenPUT, ThenIDENTITY_ROLE_SYSTEM_IMMUTABLE(409) — INV-R1. - Given роль другой компании или
CompanyId = null, WhenPUT, ThenIDENTITY_ROLE_NOT_FOUND(404) — существование чужих ролей не раскрывается. - Given переданная
Versionне совпадает с текущей, WhenPUT, ThenIDENTITY_CONCURRENCY_CONFLICT(409); роль не изменена. - Given роль
Archived, WhenPUT, ThenIDENTITY_ROLE_ARCHIVED(409). - Given
Nameпустой или длиннее 128 символов, WhenPUT, ThenValidationError(400). - Given актор без
identity.roles.manage, WhenPUT, ThenFORBIDDEN(403).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (2): GET /api/v1/roles — Список ролей компании, Identity Company-User API — реестр методов и общие правила области
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность: domain/role.md (INV-R1).
- Состав прав: set-role-permissions.md; архив: archive-company-role.md.