Skip to main content

Atribuição de papéis — API

Conceder, listar, atualizar a expiração e revogar a atribuição de um papel a um usuário — num tenant inteiro ou numa unidade específica — mais as rotas de leitura de permissões efetivas e o diretório que junta todas as atribuições visíveis de um usuário numa única resposta.

Funcionamento​

  • Conceder (POST .../role-assignments): grava um vínculo userId ↔ roleId no escopo informado (tenant-wide, via tenants/:tenantId/users/:userId/role-assignments, ou de unidade, via units/:unitId/users/:userId/role-assignments), com expires_at opcional. Se já existir uma atribuição ativa e não expirada para o mesmo usuário/papel/escopo, a chamada é idempotente e devolve a atribuição existente sem criar uma nova. Se existir uma atribuição expirada ou inativa para o mesmo trio, ela é retirada e uma nova identidade de atribuição é criada (o id muda) — não há reativação de um registro antigo.
  • Listar: quatro variações leem o mesmo tipo de registro em recortes diferentes — por unidade e usuário, por unidade inteira, por tenant e usuário, por tenant inteiro — todas paginadas e filtráveis por role_id/search/user_id.
  • Diretório (GET /users/:userId/role-assignments): junta, numa única leitura sem paginação, toda atribuição ativa do usuário — tenant-wide e por unidade — em todo tenant/unidade que o ator autenticado (não o usuário-alvo) tem permissão de enxergar. Nunca revela uma atribuição de um tenant ou unidade fora do escopo do ator: essas linhas são omitidas, não mascaradas.
  • Atualizar expiração (PATCH .../role-assignments/:assignmentId): exige uma data no futuro; datas passadas ou inválidas são rejeitadas antes de tocar o banco.
  • Revogar (DELETE .../role-assignments/:assignmentId): soft-delete lógico (isActive: false); não apaga o registro.
  • Permissões efetivas (GET .../effective-permissions e .../effective-mask): calculam a união das permissões de todo papel ativo do usuário naquele escopo — a primeira devolve os nomes de permissão, a segunda o bitmask decimal equivalente.

Toda concessão/atualização/revogação passa antes por IdentityRolePermissionAssignmentPolicy.resolveActiveScope (o tenant/unidade alvo precisa estar ativo) e depois por assertActorCanAssignRole — o mesmo controle de não escalonamento de privilégio usado em Papéis: o ator só pode atribuir um papel se possuir, ele mesmo, cada permissão daquele papel no escopo (tenant ou unidade) da atribuição.

Endpoints​

MétodoRotaDescrição
POST/v1/units/:unitId/users/:userId/role-assignmentsConcede um papel a um usuário numa unidade
GET/v1/units/:unitId/users/:userId/role-assignmentsLista atribuições ativas do usuário naquela unidade
PATCH/v1/units/:unitId/users/:userId/role-assignments/:assignmentIdAtualiza a expiração de uma atribuição de unidade
DELETE/v1/units/:unitId/users/:userId/role-assignments/:assignmentIdRevoga uma atribuição de unidade
GET/v1/units/:unitId/users/:userId/effective-permissionsPermissões efetivas do usuário naquela unidade
GET/v1/units/:unitId/users/:userId/effective-maskBitmask efetivo do usuário naquela unidade
GET/v1/units/:unitId/role-assignmentsLista toda atribuição ativa numa unidade
POST/v1/tenants/:tenantId/users/:userId/role-assignmentsConcede um papel a um usuário no tenant (tenant-wide)
GET/v1/tenants/:tenantId/users/:userId/role-assignmentsLista atribuições ativas do usuário naquele tenant
PATCH/v1/tenants/:tenantId/users/:userId/role-assignments/:assignmentIdAtualiza a expiração de uma atribuição de tenant
DELETE/v1/tenants/:tenantId/users/:userId/role-assignments/:assignmentIdRevoga uma atribuição de tenant
GET/v1/tenants/:tenantId/users/:userId/effective-permissionsPermissões efetivas do usuário naquele tenant
GET/v1/tenants/:tenantId/users/:userId/effective-maskBitmask efetivo do usuário naquele tenant
GET/v1/tenants/:tenantId/role-assignmentsLista toda atribuição ativa num tenant
GET/v1/users/:userId/role-assignmentsDiretório: toda atribuição do usuário visível ao ator, sem paginação

Versão: v1

Swagger: Identity — Role assignments · Identity — Tenant role assignments · Rota (Dev): http://localhost:3000/v1/units/{unitId}/users/{userId}/role-assignments

Permissões​

RotaGuardsPermissão exigida
POST, PATCH, DELETE .../role-assignments (unit ou tenant)IdentityOAuthAccessTokenGuard, AuthorizationGuardrole:manage no escopo (unit ou tenant)
GET .../role-assignments (por usuário, por unidade/tenant inteiro)idemrole:read no escopo
GET .../effective-permissions, .../effective-maskidempermission:read e user:read (match: all) no escopo
GET /users/:userId/role-assignments (diretório)idemrole:read, match: any, escopo organization-list (filter: policy) — o ator precisa enxergar pelo menos um tenant/unidade pela política de listagem

Headers​

HeaderObrigatórioDescrição
AuthorizationSimBearer <access_token>

Path parameters​

NomeTipoObrigatórioDescrição
tenantId / unitIduuidSim, conforme a varianteEscopo da atribuição
userIduuidSimUsuário-alvo
assignmentIduuidSim, nas rotas de itemAtribuição alvo

Query parameters​

Toda rota de listagem (IdentityRoleAssignmentListQueryRequest):

NomeTipoObrigatórioDefaultDescrição
pageintegerNão1Página (mínimo 1)
limitintegerNão20Itens por página (1 a 100)
searchstringNão—Busca textual (até 128 caracteres)
role_iduuidNão—Filtra por papel
user_iduuidNão—Filtra por usuário (só nas listagens por unidade/tenant inteiro)

O diretório (GET /users/:userId/role-assignments) não aceita query — devolve tudo de uma vez.

Body​

POST .../role-assignments — AssignIdentityUserRoleRequest:

json
{ "role_id": "0198f1a2-...", "expires_at": "2027-01-01T00:00:00.000Z" }
CampoTipoObrigatórioValidação
role_iduuid v7Sim@IsUUID('7')
expires_atstring (ISO 8601)Nãoformato estrito; se ausente, a atribuição não expira

PATCH .../role-assignments/:assignmentId — UpdateIdentityUserRoleExpirationRequest:

json
{ "expires_at": "2027-06-01T00:00:00.000Z" }
CampoTipoObrigatórioValidação
expires_atstring (ISO 8601)Simformato estrito e deve ser uma data futura, senão IDENTITY_USER_ROLE_EXPIRATION_INVALID

Response​

IdentityUserRoleAssignmentResponse (base de toda rota de atribuição):

json
{
"id": "3c9e...uuid",
"role_id": "0198f1a2-...",
"user_id": "77aa...uuid",
"tenant_id": "1f2a...uuid",
"unit_id": null,
"granted_by": "16ab...uuid",
"granted_at": "2026-09-01T12:00:00.000Z",
"expires_at": null,
"is_active": true,
"is_effective": true,
"is_expired": false,
"created_at": "2026-09-01T12:00:00.000Z",
"updated_at": "2026-09-01T12:00:00.000Z"
}

O diretório e as listagens por unidade/tenant inteiro estendem esse formato com role_name, role_display_name e role_type (IdentityUserRoleAssignmentWithRoleResponse), para a UI nunca precisar de uma segunda chamada ao catálogo de papéis.

Mapa de campos​

CampoTipoValores possíveisDescrição
unit_iduuid | null—null numa atribuição tenant-wide; preenchido numa atribuição de unidade
expires_atstring (ISO) | null—null = sem expiração
is_activeboolean—false após revogação (soft-delete); nunca volta a true no mesmo registro
is_expiredbooleancalculadotrue se expires_at já passou, mesmo que is_active ainda seja true
is_effectivebooleancalculadois_active && !is_expired — é este campo que decide se a atribuição concede permissão agora
role_typestring (diretório/listagens agregadas)system, tenant_customTipo do papel atribuído

Erros​

Classe de erroerrorCodeStatusQuando ocorre
(validação de payload/UUID)BAD_REQUEST / INVALID_UUID400corpo ou identificadores malformados
—IDENTITY_USER_ROLE_EXPIRATION_INVALID400expires_at ausente/no passado (PATCH)
UnauthenticatedErrorIDENTITY_INVALID_OAUTH_ACCESS_TOKEN401access token ausente/inválido
ForbiddenActionIDENTITY_ROLE_PERMISSION_ESCALATION_FORBIDDEN403ator não possui, ele mesmo, alguma permissão do papel que está atribuindo
ForbiddenAction (guard)—403ator sem role:manage/role:read/permission:read+user:read no escopo
ForbiddenAction (guard)AUTHORIZATION_SCOPE_UNAVAILABLE403tenant/unidade alvo inexistente, inativo, ou fora do escopo do tenant informado
IdentityUserNotFoundError—404userId inexistente/inativo
IdentityRoleNotFoundErrorIDENTITY_ROLE_NOT_FOUND404role_id inexistente ou não visível no escopo
IdentityUserRoleAssignmentNotFoundErrorIDENTITY_USER_ROLE_ASSIGNMENT_NOT_FOUND404assignmentId inexistente, já revogado, ou de outro usuário/escopo

Regras de negócio​

IDRegraComportamento esperado
RN-01Uma atribuição é tenant-wide ou de unidade, nunca as duascriada pela rota correspondente; um grant de tenant aplica-se a toda unidade daquele tenant, mas isso é resolvido na leitura, não gravado por unidade
RN-02Concessão repetida é idempotente enquanto a atribuição existente está efetivamesmo trio usuário/papel/escopo com atribuição ativa e não expirada → devolve a existente, não cria duplicata
RN-03Uma atribuição expirada/revogada nunca é reaproveitadauma nova concessão para o mesmo trio, após expiração ou revogação, sempre cria uma nova identidade de atribuição
RN-04Sem escalonamento de privilégio na atribuiçãoo ator só concede um papel se possuir, ele mesmo, cada permissão daquele papel no escopo da atribuição (platformAdmin isento)
RN-05Um grant de unidade só é válido se a unidade pertencer ao tenant esperadose a persistência do vínculo unidade↔tenant divergir do cadastro atual da organização, o grant de unidade é ignorado no cálculo de efetivo
RN-06O diretório nunca revela atribuição fora do escopo do atortenants/unidades que o ator não pode ler são omitidos da resposta, não mascarados
RN-07Revogação é lógicaDELETE marca is_active: false; o registro permanece para auditoria
RN-08Toda mutação incrementa a authorization_version do usuário afetadoa claim cacheada do usuário é invalidada na próxima leitura

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPD / HIPAALeast privilege / não escalonamentoRN-04
HIPAA / ANVISATrilha de concessão/revogação de acessotodo evento (identity.user_role.assigned/revoked/expiration_updated) grava outbox 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ênciaSim, em POST (RN-02); PATCH/DELETE não são idempotentes no sentido de repetir sem novo estado, mas são seguros de repetir
PaginaçãoSim, nas listagens por unidade/tenant/usuário (1–100, default 20); o diretório não pagina
Rate limitNão identificado
CacheNão identificado
AuditoriaSim — outbox em toda concessão, atualização de expiração e revogação

Relacionado​