Consultar logs de auditoria — API
Lista o histórico de eventos de auditoria já persistidos, com filtros e paginação, aplicando mascaramento de campos sensíveis na resposta. Existem três rotas equivalentes que diferem apenas em qual escopo organizacional restringe a consulta: todos os tenants que o chamador pode acessar, um tenant específico, ou uma unidade específica.
Funcionamento
JwtAuthenticationGuardexige um access token válido;AuthorizationGuardexige a permissãoaudit:reade resolve o escopo da rota (ver Permissões).- A query string é validada (
ListAuditLogsQueryDto): paginação, filtro por evento, poruserId, por intervalofrom/to, e por parresourceType+resourceId(os dois são obrigatórios juntos). - Se o chamador não tem nenhum tenant acessível, a resposta é uma página vazia (não é erro).
- Se o filtro pedir um
resourceTypeque o escopo da rota não permite (usernas rotas de tenant/unidade), a resposta também é uma página vazia. - O repositório busca a página no Postgres, já filtrada por escopo, evento, usuário, período e
recurso, ordenada por
occurredAt DESC, createdAt DESC, seq DESC(mais recente primeiro; em empate, desempata pela ordem real de inserção na cadeia). - Se a página tem itens, o conjunto de regras de mascaramento vigente (cacheado) é aplicado sobre
metadata,before,after,description,ip,userId,targetUserId,clientIderesourceIdde cada entrada, antes de montar a resposta.
Endpoints
| Método | Rota | Descrição |
|---|---|---|
| GET | /v1/audit-logs | Histórico entre todos os tenants que o chamador pode acessar |
| GET | /v1/tenants/:tenantId/audit-logs | Histórico restrito a um tenant (e suas unidades) |
| GET | /v1/units/:unitId/audit-logs | Histórico restrito a uma unidade |
Versão: v1
Swagger: Audit · Rota (Dev): http://localhost:3001/v1/audit-logs
Lógica de decisão (comum às três rotas; o que muda é apenas a origem do escopo):
Permissões
Todas as rotas exigem JwtAuthenticationGuard + AuthorizationGuard com a permissão
audit:read (PermissionName.AUDIT_READ). O que muda entre as três é o escopo exigido:
| Rota | Guards | Escopo (Authorize) | Efeito |
|---|---|---|---|
GET /audit-logs | JwtAuthenticationGuard, AuthorizationGuard | tenant-list (filtro policy) | Vê o histórico de todos os tenants que a política de autorização concede ao ator; sem nenhum tenant concedido, a lista vem vazia. Um administrador de plataforma vê tudo, incluindo o escopo __system__. |
GET /tenants/:tenantId/audit-logs | idem | tenant, identificador = tenantId do path | Exige que o tenantId do path esteja entre os tenants concedidos ao ator; caso contrário, 403. |
GET /units/:unitId/audit-logs | idem | unit, identificador = unitId do path | Exige que o unitId do path esteja entre as unidades concedidas ao ator; caso contrário, 403. |
As rotas de tenant e unit excluem sempre entradas com resourceType: user, mesmo que o
chamador tenha a permissão e peça esse filtro explicitamente (retorna página vazia). Ações sobre
usuário só aparecem pela rota geral.
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization | Sim | Bearer <access_token> |
Path parameters
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tenantId | UUID | Apenas na rota de tenant | Validado por ParseUUIDPipe; UUID inválido → 400 |
unitId | UUID | Apenas na rota de unidade | Validado por ParseUUIDPipe; UUID inválido → 400 |
Query parameters
| Nome | Tipo | Obrigatório | Default | Descrição |
|---|---|---|---|---|
page | integer | Não | 1 | Página (1-based) |
limit | integer | Não | 20 | Itens por página (máx. 100) |
event | string | Não | — | Filtra por nome exato do evento (ex.: tenant.created); vazio/só espaços é tratado como ausente |
userId | UUID | Não | — | Filtra pelo ator (sub do JWT) |
from | ISO 8601 date-time | Não | — | Limite inferior (inclusive) de occurredAt |
to | ISO 8601 date-time | Não | — | Limite superior (inclusive) de occurredAt; deve ser ≥ from |
resourceType | enum AuditResourceType | Só junto com resourceId | — | Tipo do recurso (ex.: exam, unit, user) |
resourceId | string (até 64) | Só junto com resourceType | — | Identificador do recurso |
resourceType e resourceId devem ser enviados juntos; enviar só um dos dois é 400.
Body
Não se aplica — as três rotas são GET.
Response
200 — PaginatedAuditLogsResponseDto:
json{"data": [{"id": "123e4567-e89b-12d3-a456-426614174000","event": "tenant.created","description": "Tenant created","userId": "0c5e2f48-1d9a-4ad1-9f0d-32a0d11a8a20","unitId": null,"tenantId": "02ea27af-05c0-4af6-be75-8cd17bdf6340","resourceType": "tenant","resourceId": "02ea27af-05c0-4af6-be75-8cd17bdf6340","ip": "203.0.113.42","clientId": "mm-portal-web","metadata": { "plan": "enterprise" },"before": null,"after": { "name": "Clínica Exemplo" },"occurredAt": "2026-04-09T00:00:00.000Z","createdAt": "2026-04-09T00:00:00.000Z"}],"page": 1,"limit": 20,"total": 1,"totalPages": 1}
Campos internos da cadeia de hash (hash, previousHash, chainScope, seq, hashVersion,
idempotencyKey) não são expostos por esta rota — confirmado pelos testes de contrato
(query-list.e2e.spec.ts, "never exposes the chain internals").
metadata, before e after passam pelo mascaramento antes de sair; podem conter identificadores
não cobertos por nenhuma regra configurada — não trate a ausência de marcador como garantia de que
o campo não tem dado sensível.
Erros
| Classe de erro | errorCode | Status | Quando ocorre |
|---|---|---|---|
UnauthenticatedError | UNAUTHENTICATED | 401 | Sem token ou token inválido |
ForbiddenAction | AUTHORIZATION_CLAIM_UNAVAILABLE | 403 | Ator sem claim de autorização (ex.: sem vínculo ativo) |
ForbiddenAction | FORBIDDEN_ACTION | 403 | Sem audit:read no escopo pedido, ou tenantId/unitId do path fora do que foi concedido ao ator |
ForbiddenAction | AUTHORIZATION_SCOPE_UNAVAILABLE | 403 | tenantId/unitId do path não corresponde a um tenant/unidade ativo |
| (validação de payload) | — | 400 | tenantId/unitId não é UUID; to anterior a from; resourceType sem resourceId (ou vice-versa); paginação fora dos limites |
Regras de negócio
| ID | Regra | Comportamento esperado |
|---|---|---|
| RN-01 | Sem tenant acessível → página vazia | Não é erro; a rota geral devolve data: [], total: 0 |
| RN-02 | Rotas de tenant/unidade nunca mostram ações de usuário | resourceType: user é sempre excluído do filtro base, e um filtro explícito por user devolve página vazia |
| RN-03 | Ordenação é por ocorrência, desempatada pela cadeia | occurredAt DESC, createdAt DESC, seq DESC |
| RN-04 | Mascaramento é por nome de campo, case-insensitive, com regra coringa | Uma regra na entidade * mascara aquele nome de campo em qualquer evento; uma regra em diagnosis só se aplica a eventos com prefixo diagnosis. |
| RN-05 | Mascaramento cobre subárvores inteiras | Se a chave mascarada aponta para um objeto/array, todo o conteúdo abaixo dela é mascarado, não só o valor de topo |
| RN-06 | event, resourceId etc. são normalizados (trim) antes do filtro | Filtro em branco é tratado como ausente |
Compliance
| Órgão / norma | Exigência | Como a rota atende |
|---|---|---|
| LGPD (Art. 5/6) | minimização de dados pessoais expostos | mascaramento por regra configurável (CPF, e-mail, telefone, senha) aplicado em toda leitura, independente de quem pergunta |
| HIPAA (identificador #15 — IP) | tratar IP como dado identificável | campo ip documentado como dado pessoal e sujeito às mesmas regras de mascaramento |
| LGPD/dado clínico | não expor conteúdo de laudo (PHI) sem necessidade | regra de mascaramento dedicada para a entidade diagnosis (nome/identidade/data de nascimento/e-mail do paciente e o próprio conteúdo do laudo em HTML/OIT/mamografia/eco) |
Variáveis de ambiente
| Variável | Uso | Default |
|---|---|---|
AUDIT_MASKING_CACHE_TTL_SECONDS | tempo de cache do conjunto de regras de mascaramento em memória | 300s |
AUDIT_API_PORT | porta HTTP do microsserviço de auditoria | 3001 |
Tempo médio de resposta
A confirmar — responsável: time de Audit; data: 24/09/2026. Não há medição publicada; não foi
executado neste levantamento.
Requisitos não funcionais
| Requisito | Definição |
|---|---|
| Idempotência | Sim — GET sem efeito colateral |
| Paginação | Sim — page/limit, limite máximo de 100 itens por página |
| Rate limit | Aplicado no nível do microsserviço (RateLimitModule); limites não documentados nesta página — ver configuração de ambiente do serviço |
| Cache | Apenas o conjunto de regras de mascaramento (TTL configurável); os dados de auditoria em si não são cacheados |
| Auditoria | Não se aplica — esta é a própria rota de consulta de auditoria |
Consumo pelo BFF (GraphQL)
O frontend nunca chama esta API REST diretamente. O BFF expõe uma única query GraphQL,
auditLogs, que decide qual das três rotas REST chamar (unitId informado → rota de unidade;
senão tenantId → rota de tenant; senão → rota geral) e adiciona duas coisas que a API REST não
tem:
- Autenticação por sessão de navegador (
BffAuthGuard): em vez de um Bearer token do cliente, o BFF lê o cookie de sessão do navegador, valida a sessão, obtém um access token de serviço e só então chama a API de auditoria com esse token — o frontend não manipula token de acesso. - Hidratação de nomes e diff de campos: a resposta GraphQL adiciona
user,unit,tenant,resourceeapplication(nomes legíveis resolvidos por chamadas aos diretórios de usuário/organização/papéis, com fallback para valores presentes emmetadata/before/after) e um campochanges— a lista de campos debefore/afterque realmente mudaram, já formatada como texto para exibição, calculada comparando as duas snapshots campo a campo.
Filtros aceitos pela query auditLogs (AuditLogListRequest): tenantId, unitId, event,
user (busca por nome/e-mail, resolvida para um userId antes de consultar a API), userId,
from, to, resourceType, resourceId, page, limit — o mesmo contrato de filtros da API
REST, com limit limitado a 100 também no BFF.
O tipo AuditLogPageType expõe items (nome atual) e mantém data como alias descontinuado
(@deprecated, "renomeado para items") apenas por compatibilidade.
Relacionado
- 🖥️ Tela: Consultar auditoria e Histórico de um registro
- 📂 Módulo: Audit
- 🔗 Próximo passo: Verificar integridade