Skip to main content

Políticas de autorização — API

CRUD de políticas de autorização por tenant e uma rota para avaliar, sob demanda, o que uma política decidiria para um ator específico. Uma política é a camada ABAC (controle por atributo) que fica em cima do RBAC: ela nunca concede uma permissão que o RBAC já não tenha concedido — só pode confirmar (allow) ou derrubar (deny) uma concessão RBAC existente, com base em condições sobre atributos da requisição.

Funcionamento​

  • Criar (POST /tenants/:tenantId/policies): a política é amarrada a uma permission_name do catálogo atribuível, um effect (allow/deny), uma priority (0 a 10.000, maior primeiro), até 50 conditions e até 100 target_role_ids (papéis aos quais a política se aplica; vazio = qualquer papel). Nome único por tenant.
  • Listar/buscar/atualizar/retirar: mesmo padrão de concorrência otimista de Papéis — PATCH exige expected_version; DELETE também (no corpo, diferente de papéis que usam If-Match). Atualizar reaplica toda validação de criação sobre o resultado final (tenant e papéis-alvo continuam ativos, nome continua único).
  • Avaliar (POST /tenants/:tenantId/policies/:policyId/evaluations): roda a política contra um actor_user_id e um escopo (tenant, unidade, ou recurso específico) sem de fato autorizar nada — é uma simulação síncrona, útil para debug/suporte ("por que este usuário foi barrado?").
  • Como a decisão é calculada (EvaluateIdentityAuthorizationPoliciesService, compartilhado entre esta avaliação pontual, o AuthorizationGuard e a Inspeção):
    1. Entre as políticas ativas que citam a permissão em questão, mantém só as que se aplicam a algum papel do ator (target_role_ids vazio conta como "se aplica a todos").
    2. Ordena por priority decrescente; em empate de prioridade, deny vence allow; em empate de prioridade e efeito, desempata por nome.
    3. A primeira política cujas todas as condições combinam com os atributos do contexto (tenantId, unitId, resourceType, resourceId etc.) decide o resultado.
    4. Se nenhuma política combina, o resultado é allow com reason_code: no-applicable-policy — silêncio de política nunca nega.
  • Condições: comparam um atributo por caminho (a.b.c) contra um valor literal ou contra outro atributo (value começando com $, ex.: $resource.tenantId). Operadores: equals, not_equals, in, not_in, contains, exists. Segmentos __proto__/constructor/ prototype são bloqueados explicitamente na resolução do caminho (proteção contra poluição de protótipo).

Endpoints​

MétodoRotaDescrição
POST/v1/tenants/:tenantId/policiesCria uma política
GET/v1/tenants/:tenantId/policiesLista políticas do tenant
GET/v1/tenants/:tenantId/policies/:policyIdLê uma política
PATCH/v1/tenants/:tenantId/policies/:policyIdAtualiza uma política
DELETE/v1/tenants/:tenantId/policies/:policyIdRetira uma política
POST/v1/tenants/:tenantId/policies/:policyId/evaluationsAvalia a política contra um ator/escopo, sem autorizar

Versão: v1

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

Permissões​

RotaGuardsPermissão exigida (escopo tenant)
POST /policies, PATCH /:policyIdIdentityOAuthAccessTokenGuard, AuthorizationGuardpolicy:write
DELETE /:policyIdidempolicy:delete
GET /policies, GET /:policyId, POST /:policyId/evaluationsidempolicy:read

Headers​

HeaderObrigatórioDescrição
AuthorizationSimBearer <access_token>

Path parameters​

NomeTipoObrigatórioDescrição
tenantIduuidSimTenant dono da política
policyIduuidSim, nas rotas de itemPolítica alvo

Query parameters​

GET /policies — IdentityAuthorizationPolicyListQueryRequest:

NomeTipoObrigatórioDefaultDescrição
pageintegerNão1Página
limitintegerNão20Itens por página (1–100)
searchstringNão—Busca textual (até 128 caracteres)
effectallow | denyNão—Filtra pelo efeito
is_activebooleanNão—Filtra por ativa/inativa
permission_namestringNão—Filtra pela permissão-alvo (até 128 caracteres)
resourcestringNão—Filtra por recurso; deve ser um resource existente no catálogo atribuível, senão IDENTITY_AUTHORIZATION_POLICY_RESOURCE_INVALID

Body​

POST /policies — CreateIdentityAuthorizationPolicyRequest:

json
{
"name": "deny-read-only-export",
"permission_name": "user:export",
"effect": "deny",
"priority": 100,
"target_role_ids": ["3f2a...uuid"],
"conditions": [ { "attribute": "resource.tenantId", "operator": "not_equals", "value": "$actor.tenantId" } ],
"is_active": true
}
CampoTipoObrigatórioValidação
namestringSim1 a 128 caracteres
descriptionstringNãoaté 500 caracteres
permission_namestringSimdeve existir e ser atribuível no catálogo, senão PERMISSION_NOT_ASSIGNABLE
effectallow | denySim—
conditions[]objectNãoaté 50 itens
conditions[].attributestringSimaté 128 caracteres, sem segmento vazio
conditions[].operatorenumSimequals, not_equals, in, not_in, contains, exists
conditions[].valuestring | string[]Simstring até 256 caracteres, ou array de até 100 strings ≤ 256 caracteres cada; in/not_in exigem array
priorityintegerNão (default 0)0 a 10.000
target_role_idsuuid[]Nãoaté 100, sem duplicatas; cada um deve ser um papel ativo visível no tenant
is_activebooleanNão (default true)—

PATCH /:policyId: mesmos campos, todos opcionais, mais expected_version (integer ≥ 1, obrigatório).

POST /:policyId/evaluations — EvaluateIdentityAuthorizationPolicyRequest:

json
{ "actor_user_id": "77aa...uuid", "unit_id": "9b3c...uuid" }
CampoTipoObrigatórioValidação
actor_user_iduuidSim—
unit_iduuidNãose informado, precisa pertencer e estar ativo no tenant da política
resource_iduuidNão—
resource_typestringNãoaté 128 caracteres

Response​

2xx — IdentityAuthorizationPolicyResponse:

json
{
"data": {
"id": "5e1c...uuid",
"tenant_id": "1f2a...uuid",
"name": "deny-read-only-export",
"description": null,
"permission_name": "user:export",
"effect": "deny",
"conditions": [ { "attribute": "resource.tenantId", "operator": "not_equals", "value": "$actor.tenantId" } ],
"priority": 100,
"target_role_ids": ["3f2a...uuid"],
"is_active": true,
"version": 1,
"created_at": "2026-09-01T12:00:00.000Z",
"updated_at": "2026-09-01T12:00:00.000Z"
}
}

POST /:policyId/evaluations — 200 (IdentityAuthorizationPolicyDecisionResponse):

json
{ "data": { "effect": "deny", "matched_policy_id": "5e1c...uuid", "reason_code": "policy-deny" } }

Mapa de campos​

CampoTipoValores possíveisDefaultDescrição
effectstringallow, deny—Efeito da política
priorityinteger0–10.0000Maior prioridade é avaliada primeiro; empate resolve para deny
target_role_idsuuid[]—[]Vazio = aplica-se a qualquer papel do ator
reason_code (avaliação)stringpolicy-allow, policy-deny, no-applicable-policy—Motivo da decisão simulada
matched_policy_iduuid | null—null quando no-applicable-policyPolítica que decidiu, se houver

Erros​

Classe de erroerrorCodeStatusQuando ocorre
(validação de payload)BAD_REQUEST / IDENTITY_AUTHORIZATION_POLICY_CONDITION_INVALID / IDENTITY_AUTHORIZATION_POLICY_INVALID400payload inválido, nome/prioridade/condições fora dos limites, operador incompatível com o valor
—IDENTITY_AUTHORIZATION_POLICY_RESOURCE_INVALID400filtro resource de GET /policies não existe no catálogo
—IDENTITY_AUTHORIZATION_POLICY_EXPECTED_VERSION_INVALID400expected_version ausente/não positivo em PATCH/DELETE
—PERMISSION_NOT_ASSIGNABLE400permission_name não existe ou está retired no catálogo
UnauthenticatedErrorIDENTITY_INVALID_OAUTH_ACCESS_TOKEN401access token ausente/inválido
ForbiddenAction (guard)—403ator sem policy:read/policy:write/policy:delete no tenant
IdentityAuthorizationPolicyNotFoundErrorIDENTITY_AUTHORIZATION_POLICY_NOT_FOUND404política inexistente/de outro tenant; tenant inativo; algum target_role_id inválido/de outro tenant; unidade de avaliação fora do tenant
IdentityAuthorizationPolicyAlreadyExistsErrorIDENTITY_AUTHORIZATION_POLICY_ALREADY_EXISTS409nome já usado por outra política ativa do tenant
IdentityAuthorizationPolicyVersionConflictErrorIDENTITY_AUTHORIZATION_POLICY_VERSION_CONFLICT409expected_version não bate com a versão atual

Regras de negócio​

IDRegraComportamento esperado
RN-01Uma política nunca amplia o que o RBAC concedeusó pode confirmar (allow) ou negar (deny) uma permissão que o bitmask do papel já concede
RN-02Maior priority decide primeiro; empate favorece denyordenação determinística confirmada em teste (gives deny precedence when allow and deny have equal priority)
RN-03Política inativa nunca participa da decisãois_active: false é ignorada mesmo que combine com tudo o mais
RN-04Sem política aplicável, o resultado é allowsilêncio de política nunca bloqueia; reason_code: no-applicable-policy
RN-05target_role_ids restringe a quais papéis a política se aplicavazio = todos; um papel-alvo atribuído só em outro tenant não ativa a política (confirmado em teste)
RN-06Nome de política é único por tenant, entre as ativascriar/atualizar com nome em conflito → 409
RN-07Atualização e retirada exigem concorrência otimistaexpected_version deve casar com a versão persistida
RN-08Caminho de atributo de condição bloqueia poluição de protótiposegmentos __proto__, constructor, prototype sempre resolvem para undefined

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPD / HIPAAControle de acesso refinado por atributo, auditáveltoda escrita grava outbox (identity.authorization_policy.created/updated/retired) com antes/depois
HIPAA / ANVISATrilha imutável de mudança de controle de acessoidem — inclui effect e permission_name nos metadados do evento

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 cria uma nova política); PATCH/DELETE protegidas por versão
PaginaçãoSim, em GET /policies (1–100, default 20)
Rate limitNão identificado
CacheNão identificado (a avaliação é síncrona a cada chamada)
AuditoriaSim — outbox em toda criação/atualização/retirada

Relacionado​