Oriente.Acessos.Client 0.9.0

Oriente.Acessos.Client

Liga um app .NET ao AcessoService, o registro central de acessos da Oriente Farma. É um data provider do Audit.NET: quem captura url, corpo, status, exceção, IP e usuário é o Audit.WebApi — este pacote só entrega isso ao serviço.

Você não escreve DTO, HttpClient nem filtro. Se estiver escrevendo, parou de usar o pacote.

Instalar

dotnet add package Oriente.Acessos.Client
dotnet add package Audit.WebApi.Core

Ligar

appsettings.json — a URL pode ficar aqui, a chave não:

{
  "Acessos": {
    "Url": "http://servicos_acessoservice:8080",
    "TimeoutSegundos": 5
  }
}

App no Easypanel usa o nome interno do serviço — o tráfego não sai da rede e o AcessoService não precisa de domínio nem de DNS público. Fora do Easypanel, use o domínio com https: o pacote recusa http quando o host tem ponto, porque aí a X-Api-Key e o corpo ainda não redigido trafegariam em claro.

A chave vai em env var (Acessos__ApiKey) ou user-secrets. Ela identifica a plataforma: idplataforma e plataforma saem dela, no servidor — o app não escolhe em nome de quem grava.

Program.cs, em app .NET 8 ou mais novo — MVC ou minimal API:

builder.Services.AddAcessosAudit(builder.Configuration);
...
app.UseAcessosAudit();

Pronto — duas linhas, e nenhum Map* nem controller muda. O middleware (0.8.0):

  • vai logo depois do Build(), antes do UseExceptionHandler: usuário, endpoint e status são lidos na volta da requisição, e assim a trilha grava o status que o handler devolveu;
  • grava todo endpoint do app e falha do servidor com ou sem endpoint. Asset estático, fallback da SPA, hub do SignalR, 404 de rota inexistente e preflight de CORS ficam de fora; health check anônimo nem sai do app;
  • copia o corpo de erro (JSON ou texto, até 8 KB) para a mensagemerro; HTML e resposta comprimida não;
  • cliente que desistiu grava status 499, sem stack trace;
  • em Development/Testing, sem Acessos:Url e Acessos:ApiKey, desliga e avisa no log — o teste de API que sobe o host sem env var não precisa de if. Em qualquer outro ambiente lança, como antes; só uma das duas configurada lança em qualquer ambiente;
  • manda a falha de envio para o ILogger do app. aoFalhar no AddAcessosAudit ainda vence.
  • rota ignorada no pacote não vale para o middleware: cadastre em Acessos__ApiKeys__<plataforma>__RotasIgnoradas no serviço antes de migrar.

App .NET 5–7 (filtro de MVC)

O middleware é net8.0. Abaixo disso, o filtro de MvcOptions:

builder.Services.AddAcessosAudit(builder.Configuration,
    aoFalhar: ex => Log.Warning(ex, "Falha ao registrar acesso"));
builder.Services.AddControllers(o => o.AddAcessosAuditFilter());

ComAuditoriaDeAcessos (filtro de minimal API)

Continua existindo para quem já usa. App que liga o middleware sem tirar o filtro não grava em dobro — o middleware pula o que o filtro já registrou —, mas o normal é trocar o grupo pelo UseAcessosAudit e devolver os Map* ao que eram:

var api = app.MapGroup("/api").ComAuditoriaDeAcessos();

O status não sai do Response.StatusCode, e isso importa: no minimal API o resultado ainda não foi executado quando o filtro devolve, então um Results.BadRequest() ainda vê 200. Quem diz o status é o próprio resultado (IStatusCodeHttpResult), e é dele que a trilha lê.

O que o filtro liga, e o que não liga

  • Todas as actions, sem [AuditApi] em lugar nenhum.
  • Corpo da resposta: capturado sempre, mas descartado no caminho feliz — em 2xx/3xx a mensagemerro vira nula e o corpo nem chega a ser serializado. O filtro de action lê context.Result, então capturar custa uma referência ao objeto, não uma cópia da resposta.
  • Corpo da requisição: desligado por padrão. Ligue por rota, com AddAcessosAuditFilter(incluirCorpoDaRequisicao: true) ou [AuditApi(IncludeRequestBody = true)] no controller onde o corpo realmente responde "o que o usuário fez".

De qual tela do front veio a chamada

Uma tela de SPA carrega e dispara dezenas de requisições. Sem saber a tela, a trilha responde "houve 20 requisições" em vez de "o usuário abriu a tela de pedidos" — e cresce vinte vezes mais rápido do que precisa.

O front manda o header X-Pagina em toda requisição (um interceptor no Angular ou no axios). O pacote lê e manda junto; o serviço grava na coluna pagina e agrupa por tela: a leitura repetida do mesmo usuário na mesma tela, dentro de 30 segundos, vira uma linha. Falha, escrita, leitura de registro identificado (/pacientes/4821) e anônimo nunca agrupam.

Desde o 0.9.0 o agrupamento mora só no serviço (ADR 0008): regra no pacote só mudava com versão nova e redeploy de cada app. JanelaDedupeSegundos e PostDeLeituraPorConvencao continuam compilando, marcadas [Obsolete], e não fazem nada; a janela é Acessos__JanelaAgrupamentoSegundos do AcessoService.

Sem o header o pacote cai no Referer, e só se ele for do mesmo host da requisição — Referer de outra origem é descartado, porque o host é jogado fora na normalização e https://evil.com/pedidos ficaria idêntico a /pedidos do seu front. A coluna fica nula e o agrupamento do serviço passa a valer por url.

Libere X-Pagina no CORS da API (WithHeaders("X-Pagina", ...)). Header custom fora da policy faz o browser derrubar a requisição inteira, não só o header.

Polling continua sendo caso de rotasIgnoradas, não do agrupamento: a janela reabre a cada 30s, e um endpoint chamado a cada 5s ainda geraria 120 linhas por hora.

Por que o corpo da requisição não vem ligado

Não é desempenho, é LGPD. Ligado globalmente, o corpo de toda action de todo app passa a ser enviado — e o serviço mascara credencial, não CPF, nome nem dado de saúde. Num grupo farmacêutico isso concentra dado pessoal sensível numa tabela sem endpoint de leitura, sem cifra e sem controle de acesso próprio.

Ligue onde o corpo é o dado da auditoria. Deixe desligado onde ele é só o formulário do usuário.

O que vai parar em mensagemerro

Só quando a chamada falha. E não é a exceção crua: o pacote a reduz ao que serve numa trilha.

System.InvalidOperationException: Pedido 123 sem itens
 ---> System.ArgumentException: quantidade deve ser maior que zero
   at Oriente.Pedidos.Domain.Pedido.Validar(Int32 quantidade)
   at Oriente.Pedidos.Application.CriarPedidoHandler.Handle(CriarPedido cmd)
   --- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.Execute()

vira

System.InvalidOperationException: Pedido 123 sem itens | System.ArgumentException: quantidade deve ser maior que zero

A cadeia de causas fica; o stack trace sai. Stack trace responde "onde no código" — pergunta do log da sua aplicação, não da trilha de auditoria — e gasta milhares de caracteres para isso. A trilha responde "o que aconteceu com este usuário nesta rota".

Teto de 2000 caracteres, com ... no corte. E o serviço ainda mascara credencial por cima: mensagem de exceção carrega valor com a mesma facilidade que corpo de requisição.

Marque o que não pode ser registrado

[HttpPost("login")]
public IActionResult Login([AuditIgnore] LoginRequest requisicao) { ... }

O serviço mascara antes de gravar — em url, requisicao e mensagemerro:

  • credencial: senha, password, token, authorization, bearer, jwt, apiKey, secret, cookie, sessao, pin, cartao, cvv
  • documento de pessoa: cpf, cnpj

A máscara olha o nome do campo, não o formato do valor. Casar 11 dígitos pegaria código de produto e número de nota junto. Por isso /api/clientes/12345678900 — CPF no caminho da rota, sem nome de campo — não é alcançado: a saída aí é o app não pôr documento em rota.

O que ele não pega: segredo em prosa. "usuário X com senha Y não encontrado" não é mascarado, porque a máscara casa chave: valor e chave=valor, não texto corrido. Se a sua exceção monta frase com o valor dentro, o [AuditIgnore] é a defesa, não a redação — mas isso é rede de proteção, não desculpa. O que o [AuditIgnore] cobre nem sai da sua aplicação.

O que esperar

  • Não derruba nem segura o app. O envio sai do caminho da resposta e falha em silêncio, com timeout curto. Auditoria indisponível não vira app indisponível.
  • Perde evento se o serviço cair. Não há fila durável nesta versão. Se algum dia não puder perder nenhum registro, o lugar de resolver é aqui, não no app consumidor.
  • mac fica nulo em app web — navegador não entrega MAC. A coluna espera pelos apps que têm essa informação.
  • idusuarioplataforma é o id, não o nome — e quem escolhe é o serviço. O pacote manda as claims uid, NameIdentifier, sub, Sid e e-mail, cruas, e nenhuma outra: papel e token nunca saem do app. O serviço fica com a primeira, nessa ordem; sem nenhuma, vale o Identity.Name, que em vários apps é o nome de exibição. Mudar a ordem é deploy do serviço, não versão nova do pacote.
  • O IP gravado é o do usuário, mesmo atrás do proxy. O pacote manda o X-Forwarded-For e o serviço usa quando a conexão veio da sub-rede do Traefik, configurada no serviço (Acessos__RedesDoProxy). Não precisa configurar ForwardedHeaders no app para isso.
  • Método, status HTTP e trace id vão junto. O trace id é o que liga a linha da trilha ao log do app numa investigação.
  • Leitura anônima não entra na trilha (/health, endpoint público de consulta). GET e HEAD sem usuário nem claim não saem do app: a trilha é de uso de usuário (ADR 0007), e mandar deixava um flood numa rota pública ocupar as vagas de envio dos usuários logados. Anônimo que escreve — redefinir senha, cadastro público — continua, e token de integração sem Name, que chega com claim, também. Fila cheia vira um aviso por minuto no log, com a contagem.

Ao subir de versão

0.9.0 — o AcessoService precisa estar no ar com a versão de 2026-09-21 antes do primeiro app com o 0.9.0: o pacote deixa de mandar idUsuarioPlataforma quando há claim, e o serviço antigo recusa o registro sem ele (400, acesso perdido).

  • O agrupamento por tela saiu do pacote (ADR 0008). JanelaDedupeSegundos e PostDeLeituraPorConvencao ficam [Obsolete] e sem efeito: compila, avisa, e pode apagar.
  • Com claim, o nome de exibição não sai mais do app. O id vem das claims, escolhido no serviço.
  • Leitura anônima (GET/HEAD) não sai do app, e a fila cheia avisa uma vez por minuto.
  • Das 32 vagas de envio, 8 ficam para escrita e falha: leitura bem-sucedida não as usa.
  • O Mapear público ganhou o parâmetro opcional pagina desde o 0.4.0: quem chama direto recompila. Quebra de compatibilidade binária, aceita no 0.x.

No packages depend on Oriente.Acessos.Client.

Version Downloads Last updated
0.9.0 9 09/21/2026
0.8.0 5 09/16/2026
0.7.0 132 09/10/2026
0.6.0 24 09/08/2026
0.5.0 29 08/26/2026
0.4.0 6 08/25/2026
0.3.0 5 08/24/2026
0.2.1 8 08/21/2026
0.2.0 7 08/21/2026