Skip to main content

Papéis — API

CRUD de papéis customizados por tenant (tenant_custom), incluindo clonagem de um papel visível e gestão granular das permissões de um papel. Também expõe leitura dos papéis de sistema no mesmo formato (system, imutáveis por esta rota).

Funcionamento​

  • Criar (POST /tenants/:tenantId/roles): cria um papel tenant_custom sem nenhuma permissão (permission_mask: "0"). O nome deve ser único entre os papéis não excluídos daquele tenant.
  • Clonar (POST /tenants/:tenantId/roles/:roleId/clones): copia as permissões de um papel visível no tenant (de sistema ou tenant_custom do próprio tenant) para um novo papel tenant_custom, com nome/descrição novos. O ator precisa possuir, ele mesmo, toda permissão que está sendo clonada (ver RN-04); senão a clonagem é negada mesmo que ele tenha role:write.
  • Listar (GET /tenants/:tenantId/roles) e buscar por lote (POST /tenants/:tenantId/roles/lookups): listam papéis visíveis no tenant (sistema + tenant_custom do tenant), com filtro por tipo, busca textual (nome/nome de exibição) e paginação. O lookup em lote aceita até 1000 role_ids e devolve só os que existem e são visíveis; lista vazia devolve [] sem consultar o banco.
  • Buscar um (GET /tenants/:tenantId/roles/:roleId): 404 se o papel não existe, foi excluído, ou é tenant_custom de outro tenant — a visibilidade nunca revela a existência de um papel de outro tenant.
  • Atualizar (PATCH /tenants/:tenantId/roles/:roleId) e retirar (DELETE .../roles/:roleId): exigem concorrência otimista — expected_version no corpo (update) ou header If-Match (retire) casando com a versão atual do papel. Um papel system nunca pode ser alterado ou retirado por aqui (IDENTITY_SYSTEM_ROLE_IMMUTABLE).
  • Adicionar (POST .../roles/:roleId/permissions), remover uma (DELETE .../roles/:roleId/permissions/:permissionId) e substituir todas (PUT .../roles/:roleId/permissions) as permissões de um papel tenant_custom: todas passam pelo mesmo caminho de escrita (ReplaceIdentityRolePermissionsService), incrementam a versão do papel e disparam o outbox de auditoria. Adicionar uma permissão já presente, ou remover uma ausente, é rejeitado como mutação sem efeito (IDENTITY_ROLE_PERMISSION_MUTATION_EMPTY).

Qualquer mutação de permissões de um papel incrementa a authorization_version de todo usuário com atribuição ativa desse papel — na próxima leitura, a claim desses usuários é recalculada.

Endpoints​

MétodoRotaDescrição
POST/v1/tenants/:tenantId/rolesCria um papel customizado
POST/v1/tenants/:tenantId/roles/:roleId/clonesClona um papel visível para um novo papel customizado
POST/v1/tenants/:tenantId/roles/lookupsResolve papéis visíveis por uma lista de IDs
GET/v1/tenants/:tenantId/rolesLista papéis visíveis no tenant
GET/v1/tenants/:tenantId/roles/:roleIdLê um papel visível no tenant
PATCH/v1/tenants/:tenantId/roles/:roleIdAtualiza nome/descrição de um papel customizado
DELETE/v1/tenants/:tenantId/roles/:roleIdRetira (soft-delete) um papel customizado
POST/v1/tenants/:tenantId/roles/:roleId/permissionsAdiciona permissões a um papel customizado
DELETE/v1/tenants/:tenantId/roles/:roleId/permissions/:permissionIdRemove uma permissão de um papel customizado
PUT/v1/tenants/:tenantId/roles/:roleId/permissionsSubstitui todas as permissões de um papel customizado

Versão: v1

Swagger: Identity — Roles · Rota (Dev): http://localhost:3000/v1/tenants/{tenantId}/roles

Permissões​

RotaGuardsPermissão exigida (escopo tenant)
POST /roles, PATCH /:roleId, DELETE /:roleId, POST /:roleId/clonesIdentityOAuthAccessTokenGuard, AuthorizationGuardrole:write
GET /roles, GET /:roleId, POST /lookupsidemrole:read
POST /:roleId/permissions, DELETE /:roleId/permissions/:permissionId, PUT /:roleId/permissionsidemrole:manage e permission:manage (match: all)

Além da permissão RBAC da rota, toda mutação de papel/permissão passa por IdentityRolePermissionAssignmentPolicy.assertActorCanAssignPermissionsInTenant (ou assertActorCanAssignRole, no fluxo de atribuição — ver Atribuição de papéis): o ator só pode conceder a um papel uma permissão que ele mesmo possui no tenant (bit a bit), exceto se for platformAdmin. Isso impede escalonamento de privilégio mesmo por quem tem role:manage.

Headers​

HeaderObrigatórioDescrição
AuthorizationSimBearer <access_token>
If-MatchSim, em DELETE /:roleIdVersão atual do papel (inteiro ≥ 1); ausente/inválido → IDENTITY_ROLE_VERSION_REQUIRED

Path parameters​

NomeTipoObrigatórioDescrição
tenantIduuidSimTenant dono do papel (ou tenant a partir do qual um papel system é visível)
roleIduuidSim, nas rotas de itemPapel alvo
permissionIduuidSim, em DELETE .../permissions/:permissionIdPermissão a remover

Query parameters​

GET /roles:

NomeTipoObrigatórioDefaultDescrição
pageintegerNão1Página (mínimo 1)
limitintegerNão20Itens por página (1 a 100)
searchstringNão—Busca por nome ou nome de exibição (até 128 caracteres)
include_systembooleanNãotrueInclui papéis de sistema na listagem
role_idsuuid[]Não—Restringe a listagem a estes IDs
typesystem | tenant_customNão—Filtra por tipo de papel

Body​

POST /roles e PATCH /:roleId — MaintainIdentityRoleRequest:

json
{ "name": "radiology-reviewer", "display_name": "Radiology reviewer", "description": "Reviews radiology exams" }
CampoTipoObrigatórioValidação
namestringSimaté 128 caracteres, não vazio
display_namestringSimaté 128 caracteres, não vazio
descriptionstringNãoaté 500 caracteres
expected_versionintegerSim, só em PATCHinteiro ≥ 1

POST /:roleId/permissions — AddIdentityRolePermissionsRequest:

json
{ "expected_version": 3, "permission_ids": ["b7c5...", "a91f..."] }
CampoTipoObrigatórioValidação
expected_versionintegerSiminteiro ≥ 1
permission_idsuuid[]Sim1 a 100 itens, sem duplicatas; cada um deve existir no catálogo (senão IDENTITY_PERMISSION_NOT_FOUND)

PUT /:roleId/permissions — ReplaceIdentityRolePermissionsRequest:

json
{ "expected_version": 3, "permissions": ["exam:read", "report:read"] }
CampoTipoObrigatórioValidação
expected_versionintegerSiminteiro ≥ 1
permissionsstring[]Simsem duplicatas; cada nome deve pertencer ao catálogo atribuível (IsIn sobre PermissionCatalog.assignable())

POST /lookups — IdentityRoleLookupRequest:

json
{ "role_ids": ["b7c5...", "a91f..."] }
CampoTipoObrigatórioValidação
role_idsuuid[]Simaté 1000 itens; acima disso → IDENTITY_ROLE_LOOKUP_LIMIT_INVALID (400)

Response​

2xx — IdentityRoleResponse:

json
{
"data": {
"id": "b7c5...uuid",
"description": "Reviews radiology exams",
"name": "radiology-reviewer",
"display_name": "Radiology reviewer",
"type": "tenant_custom",
"tenant_id": "1f2a...uuid",
"permission_mask": "17",
"source_role_id": null,
"permissions": ["exam:read", "report:read"],
"permission_count": 2,
"permission_details": [ { "name": "exam:read", "resource": "exam", "action": "read", "bit_position": 0, "description": "Read exams" } ],
"is_active": true,
"created_at": "2026-09-01T12:00:00.000Z",
"updated_at": "2026-09-01T12:00:00.000Z",
"version": 1
}
}

Mapa de campos​

CampoTipoValores possíveisDefaultDescrição
typestringsystem, tenant_custom—system se tenant_id não foi informado na criação (só ocorre no seed do catálogo)
tenant_iduuid | null—null para papéis systemTenant dono do papel customizado
permission_maskstring (bigint)soma de 2^bit_position das permissões"0" na criaçãoBitmask decimal persistido
permissions / permission_detailsstring[] / object[]nomes do catálogo atribuível[] na criaçãoPermissões do papel, decodificadas do bitmask
source_role_iduuid | null—nullPapel de origem, quando este papel foi criado por clonagem
versioninteger≥ 11Usado para concorrência otimista em PATCH/DELETE/mutações de permissão

Erros​

Classe de erroerrorCodeStatusQuando ocorre
(validação de payload/UUID)BAD_REQUEST / INVALID_UUID400corpo, tenantId/roleId malformados
—IDENTITY_ROLE_LOOKUP_LIMIT_INVALID400role_ids do lookup acima de 1000 itens
—IDENTITY_ROLE_PERMISSION_MUTATION_EMPTY400adicionar/remover uma permissão sem mudar o conjunto final
—IDENTITY_PERMISSION_NOT_FOUND400permission_ids de POST .../permissions não existe no catálogo
—IDENTITY_ROLE_VERSION_REQUIRED400DELETE :roleId sem If-Match válido
UnauthenticatedErrorIDENTITY_INVALID_OAUTH_ACCESS_TOKEN401access token ausente/inválido
ForbiddenActionIDENTITY_SYSTEM_ROLE_IMMUTABLE403tentativa de alterar/retirar/remover permissões de um papel system
ForbiddenActionIDENTITY_ROLE_PERMISSION_ESCALATION_FORBIDDEN403ator tenta conceder/clonar uma permissão que ele mesmo não possui no tenant
ForbiddenAction (guard)—403ator sem role:write/role:read/role:manage+permission:manage no tenant
IdentityRoleNotFoundErrorIDENTITY_ROLE_NOT_FOUND404papel inexistente, excluído, ou não visível no tenant informado
AlreadyExistsErrorIDENTITY_ROLE_NAME_ALREADY_EXISTS409outro papel não excluído já usa este name no mesmo escopo (tenant ou global)
IdentityRoleVersionConflictErrorIDENTITY_ROLE_VERSION_CONFLICT409expected_version/If-Match não bate com a versão atual

Regras de negócio​

IDRegraComportamento esperado
RN-01Papel system é imutável por esta APIPATCH, DELETE e as três rotas de permissões de papel sempre negam com IDENTITY_SYSTEM_ROLE_IMMUTABLE para type: system
RN-02Nome de papel é único por escopo (tenant, ou global entre os system)criar/clonar/atualizar com nome já em uso → 409 IDENTITY_ROLE_NAME_ALREADY_EXISTS
RN-03Toda mutação de papel usa concorrência otimistaexpected_version (body) ou If-Match (header) deve casar com version atual, senão 409
RN-04Sem escalonamento de privilégio: o ator só concede o que já possuiadicionar/substituir/clonar permissões exige que o ator tenha, ele mesmo, cada permissão envolvida no tenant — platformAdmin está isento
RN-05Mutação de permissão precisa ter efeitoadicionar uma permissão já presente ou remover uma ausente é rejeitado (IDENTITY_ROLE_PERMISSION_MUTATION_EMPTY)
RN-06Mudança de permissões de um papel invalida a claim de todo usuário atribuído a eleauthorization_version incrementado para cada usuário com atribuição ativa daquele papel
RN-07Lookup em lote é limitado e idempotenteaté 1000 role_ids; lista vazia não consulta o banco; IDs duplicados são deduplicados e ordenados

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPD / HIPAALeast privilege / não escalonamentoRN-04 impede que um administrador de papéis conceda permissão que não possui
HIPAA / ANVISATrilha de mudança de controle de acessotoda criação/atualização/retirada/clonagem/mutação de permissão grava um evento no outbox de auditoria com autor, IP e antes/depois

Variáveis de ambiente​

Nenhuma variável de ambiente é consumida diretamente por estas rotas.

Tempo médio de resposta​

A confirmar — responsável: time de Identity; data: 24/09/2026. Sem medição publicada.

Requisitos não funcionais​

RequisitoDefinição
IdempotênciaNão para POST (cada chamada tenta criar um novo papel); mutações de permissão via PUT/add/remove são idempotentes no resultado final, mas rejeitam uma chamada repetida sem efeito
PaginaçãoSim, em GET /roles (limit 1–100, default 20)
Rate limitNão identificado
CacheNão identificado
AuditoriaSim — todo evento de escrita grava no outbox (identity.role.created/updated/retired/cloned/permissions_replaced)

Relacionado​