# nsj-audit-lib

Biblioteca de auditoria interna da Nasajon. Ela implementa a instrumentação de auditoria descrita em `docs/Nasajon ERP4 - Estratégia de auditoria v1.0.pdf` e fornece utilitários para:

- Publicar eventos de request no Redis Streams (request_started/request_finished).
- Registrar evidências transacionais (audit_outbox) no banco do tenant.
- Normalizar parâmetros e mascarar dados sensíveis.

## 1. Funcionamento geral da auditoria

Resumo do fluxo (conforme a especificação):

1. Em todo request:
   - Gera request_id.
   - Resolve tenant_id, grupo_empresarial_id e area_atendimento_id.
   - Captura identidade (user_id/subject_user_id/session_id).
   - Define action e params_normalizados.
   - Publica `request_started` no Redis Streams.
2. Ao finalizar o request:
   - Publica `request_finished` com status, duração, erro normalizado e flags de
     transação.
3. Em requests transacionais (POST/PUT/PATCH/DELETE):
   - Grava uma linha na `audit_outbox` no mesmo commit da transação.
   - Se houver rollback, não existe linha na outbox.

O consumo e consolidação (ClickHouse/persist_worker) fazem parte da arquitetura geral, mas não pertencem a este repositório.

## 2. Objetivo da lib

- Enviar eventos de auditoria para o Redis (baixa latência e alto throughput).
- Garantir a existência/consistência da estrutura `audit_outbox` no banco do tenant quando necessário.
- Fornecer insumos para rastrear transações (tentadas x comitadas) e apoiar análise operacional.

## 3. Explicações de uso

### 3.1 Util `AuditRequestUtil`

Responsável por registrar início e fim de request. Ela:

- Gera `g.request_id`.
- Resolve tenant/grupo/area (via `AuditConfig` ou leitura de query/body).
- Normaliza parâmetros (via `util_normaliza_parametros`).
- Publica `request_started` e `request_finished` via `AuditUtil`.
- Em caso de erro, inclui `request_json` truncado e mascarado.

Exemplo (Flask):

```python
from nsj_audit_lib.util.audit_request_util import AuditRequestUtil
from nsj_audit_lib.util.audit_config import AuditConfig

audit = AuditRequestUtil(
    audit_config=AuditConfig(
        tenant_field="tenant_id",
        grupo_empresarial_field="grupo_empresarial_id",
        area_atendimento_field="area_atendimento_id",
    ),
    dto_class=MeuDTO,
)

def handler(**path_args):
    audit.record_audit_request(**path_args)
    response = processar_request()
    audit.record_audit_response(response)
    return response
```

### 3.2 Util `AuditUtil`

Camada de baixo nível que publica eventos no Redis Streams.

Campos principais de `request_started`:
- request_id, tenant_id, grupo_empresarial_id, area_atendimento_id
- actor_user_id, subject_user_id, session_id
- http_method, http_route, action
- params_normalizados e flag `is_transaction_intent`

Campos principais de `request_finished`:
- request_id, tenant_id, grupo_empresarial_id
- http_status, duration_ms, tx_attempted
- error_normalized (quando aplicável) e request_json em caso de erro

Exemplo:

```python
from nsj_audit_lib.util.audit_util import AuditUtil, DBTypes, HTTPMethods

audit_util = AuditUtil()
audit_util.emit_request_started(
    request_id=request_id,
    tenant_id=tenant_id,
    grupo_empresarial_id=grupo_id,
    area_atendimento_id=area_id,
    db_type=DBTypes.MULTIBANCO,
    db_key="db1",
    db_user="db_user",
    actor_user_id="user@example.com",
    subject_user_id=None,
    session_id=None,
    http_method=HTTPMethods.POST,
    http_route="/items/123",
    action="route_called",
    params_normalizados={"query_args": {}, "body": {}, "path_args": {"id": "123"}},
)
```

### 3.3 Classe `AuditService`

Registra evidências transacionais na `audit_outbox`. Ela:

- Só grava se existir contexto de request e `g.request_id`.
- Resolve tenant/grupo/area de `g` ou request.
- Monta `commit_json` (delta em updates; estado em inserts; id em deletes).
- Inclui `payload_sha256` para referência do payload.
- Usa `DAOBaseAudit` para inserir e garantir schema quando necessário.

Exemplo:

```python
from nsj_audit_lib.service.audit_service import AuditService

audit_service = AuditService(db_adapter)
audit_service._dto_class = MeuDTO
audit_service._entity_class = MinhaEntity

audit_service.record_audit_outbox(
    action="update",
    dto=novo_dto,
    resource_id=novo_dto.id,
    old_dto=dto_antigo,
)
```

## 4. Integração nativa com o RestLib

O RestLib (nsj_rest_lib) já utiliza:

- `AuditRequestUtil` na camada de rotas (início/fim de request).
- `AuditService` na camada de serviços (outbox transacional).

### 4.1 Como habilitar

Defina `AUDIT_REDIS_URL` no ambiente. Sem essa variável, o Redis não é configurado e os eventos não são publicados.

Variáveis relacionadas:
- `AUDIT_REDIS_URL` (obrigatória para publicar no Redis)
- `AUDIT_STREAM_KEY` (default: `audit:requests`)
- `AUDIT_OUTBOX_TRANSACTION` (default: true)
- `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_DRIVER` (suporte ao outbox)

### 4.2 Limites da auditoria no RestLib

- Foco em auditoria de negócio: requests autenticados e com contexto de tenant.
- Dados sensíveis são mascarados/truncados no payload de erro.
- `request_json` só é armazenado no Redis quando há erro.
- A captura não cobre eventos de borda (ex.: gateway), apenas o backend.

## 5. Planos futuros

- Adicionar decorators para registrar `request_received` e `request_dispatch`, permitindo rastreio do fluxo completo (entrada/saída) com granularidade maior.
