Logs e Auditoria — Mapeamento de Banco de Dados (v1.0)

Documentos desta funcionalidade: 01 - Definição (v1.0) · 02 - Especificação (v1.0) · 03 - Protótipo (v1.0, código em 03 - Protótipo v1.0.html) · 04 - Mapeamento de Banco de Dados (v1.0) · Cenários de Teste (v1.0) · 05 - Registro de Decisões (v1.0)

Fonte primária da estrutura: Biblioteca de Schema (DDL) (IpSeguranca.sql, Gescom.sql, catalog.json, framework_pattern.md). O log de sistema não usa banco relacional (vai para Loki e Prometheus); este mapeamento trata da trilha de auditoria. Tudo que é tabela ou coluna nova está marcado [PROPOSTA].

Convenções: instantes em UTC (datetime2(3) no SQL Server, timestamptz no PostgreSQL). O banco final pode mudar de SQL Server para PostgreSQL (decisão em aberto), por isso há modelo lógico neutro e um mapeamento para cada motor (4.4).


4.1 Tabelas existentes utilizadas

TabelaBaseUso
Paciente (PK IdPaciente int)GescomClienteAlfaIdentificador opaco do paciente na trilha (RN-LOG-015). NumProntuario, CPF e nome não entram.
Clinica (CNES, IdChClinica)GescomClienteAlfaCNES na identificação da exportação (NGS1.07.08)
SegEmpresa (SegurancaClienteEmpresaId)GescomClienteAlfaLiga a clínica local ao ClienteEmpresa global
ClienteEmpresa (Id, Nome, CNPJ, ClienteBDId)IPSegurancaNome e CNPJ da instituição na exportação; clínica na trilha
ClienteBD (Id, NomeDatabase)IPSegurancaTenant (ClienteBDId)
Usuario (Id guid, TentativasLogin, DHBloqueio)IPSegurancaUsuário (GUID global)
UsuarioCliente (UsuarioId, ClienteBDId, Situacao)IPSegurancaUsuário por cliente
ClientePerfilAcesso (Id, Nome, ClienteBDId)IPSegurancaPerfil “Auditor” por cliente (seed 4.6)
Processo, DireitoProcesso, ProcessoDireitoIPSegurancaProcesso “Auditoria” e direito “Visualizar auditoria” (seed 4.6)
Log, LogComplementoIPSegurancaNão reutilizadas. Finalidade não confirmada, campos insuficientes (sem categoria, registro afetado, hash) e sem proteção de somente inclusão. Destino a decidir em P-LOG-07.

4.2 Outras bases

IpTerminologia: sem uso. GescomZeradoPadronizacao (legado): a trilha não é criada no legado; o legado tem PEPLog (com NovoValor), que não é migrado para a trilha nova (ver P-LOG-01).

4.3 Estados e domínios

DomínioValoresOrigem
Categoria1 = Clínica, 2 = OperacionalRN-LOG-011 [PROPOSTA]
TipoEventosmallint, numeração do catálogo AuditEventType (AUD-RES-001 = 1001, AUD-USR-001 = 2001, AUD-OPE-001 = 3001; sequência dentro da faixa)Especificação 2.3.3 [PROPOSTA]
Resultado1 = sucesso, 2 = negado, 3 = falha[PROPOSTA]

A numeração do tipo é estável e nunca reaproveitada; o nome vem do catálogo em código e do dicionário i18n, não do banco.

4.4 Modelo lógico e mapeamento por banco

4.4.1 Modelo lógico (neutro) — entidade auditoria.Evento [PROPOSTA]

Existe em dois lugares com a mesma estrutura: IpSeguranca (repositório global) e GescomClienteAlfa (repositório do cliente). A coluna PacienteId só é usada no repositório do cliente.

Campo lógicoTipo lógicoObrig.DescriçãoRequisito
Idinteiro 64 bits, sequencialsimIdentificador únicoNGS1.07.05
OcorridoEminstante UTCsimData e horaNGS1.07.05
TipoEventointeiro 16 bitssimCódigo do catálogoNGS1.07.05
Categoriainteiro 8 bitssimClínica ou operacionalRN-LOG-011
UsuarioIdGUIDsim (nulo em login de usuário desconhecido: grava o login informado em Detalhe, sem senha)Usuário globalNGS1.07.05
ClienteBDIdGUIDsimTenantRN-LOG-017
ClinicaIdGUIDnãoClienteEmpresa.IdRN-LOG-002
PacienteIdinteiro 32 bitsnãoPaciente.IdPaciente, identificador do registro afetado clínicoNGS1.07.07c
Recursotexto 60nãoTipo do registro afetado (ex.: Prontuario, Prescricao, Usuario)NGS1.07.05
RecursoIdtexto 64nãoId do recurso afetadoNGS1.07.05
Componentetexto 60simServiço/componenteNGS1.07.05
Iptexto 45nãoIP de origemNGS1.07.05
TraceIdtexto 32nãoLiga ao log de sistemaRN-LOG-002
Resultadointeiro 8 bitssimSucesso, negado, falha[PROPOSTA]
DetalheJSONnãoInformação complementar: nomes de campos alterados; sem valores clínicosNGS1.07.06
HashLinha32 bytessimSHA-256 dos campos canônicosRN-LOG-025

Hash canônico: SHA-256 sobre a concatenação UTF-8 de OcorridoEm (RFC 3339), TipoEvento, Categoria, UsuarioId, ClienteBDId, ClinicaId, PacienteId, Recurso, RecursoId, Componente, Ip, TraceId, Resultado e Detalhe, na ordem fixa, separados por |, nulo como texto vazio. Calculado pela aplicação, igual nos dois bancos.

Exceção ao padrão de 16 campos: a tabela não leva o carimbo de última ação (CriadoEm, AtualizadoEm, RemovidoEm, etc.), porque o registro nunca é alterado nem removido; o próprio registro é o carimbo. A detecção de “tabelas sem o padrão” da Biblioteca de Schema deve tratar auditoria.Evento como exceção declarada.

4.4.2 SQL Server [PROPOSTA]

CREATE PARTITION FUNCTION pf_auditoria_mes (datetime2(3))
  AS RANGE RIGHT FOR VALUES ('2026-12-01','2027-01-01' /* ... criadas por rotina */);
CREATE PARTITION SCHEME ps_auditoria_mes
  AS PARTITION pf_auditoria_mes ALL TO ([PRIMARY]);
 
CREATE TABLE auditoria.Evento (
  Id          bigint IDENTITY(1,1) NOT NULL,
  OcorridoEm  datetime2(3)     NOT NULL,
  TipoEvento  smallint         NOT NULL,
  Categoria   tinyint          NOT NULL,
  UsuarioId   uniqueidentifier NULL,
  ClienteBDId uniqueidentifier NOT NULL,
  ClinicaId   uniqueidentifier NULL,
  PacienteId  int              NULL,
  Recurso     varchar(60)      NULL,
  RecursoId   varchar(64)      NULL,
  Componente  varchar(60)      NOT NULL,
  Ip          varchar(45)      NULL,
  TraceId     char(32)         NULL,
  Resultado   tinyint          NOT NULL,
  Detalhe     nvarchar(max)    NULL,
  HashLinha   binary(32)       NOT NULL,
  CONSTRAINT Evento_PK PRIMARY KEY CLUSTERED (OcorridoEm, Id)
    WITH (DATA_COMPRESSION = PAGE) ON ps_auditoria_mes (OcorridoEm),
  CONSTRAINT Evento_Detalhe_CK CHECK (Detalhe IS NULL OR ISJSON(Detalhe) = 1)
);
CREATE INDEX Evento_Usuario_IX  ON auditoria.Evento (UsuarioId,  OcorridoEm) ON ps_auditoria_mes (OcorridoEm);
CREATE INDEX Evento_Paciente_IX ON auditoria.Evento (PacienteId, OcorridoEm) WHERE PacienteId IS NOT NULL ON ps_auditoria_mes (OcorridoEm);
CREATE INDEX Evento_Tipo_IX     ON auditoria.Evento (TipoEvento, OcorridoEm) ON ps_auditoria_mes (OcorridoEm);

Compressão PAGE por partição (partições antigas podem ser comprimidas mais tarde). Tabela ledger só existe no SQL Server 2022 ou superior e não é adotada (RN-LOG-025). Permissões: o papel da aplicação recebe INSERT e SELECT; DENY UPDATE, DELETE sobre o schema auditoria; o dono do schema é outro papel.

4.4.3 PostgreSQL [PROPOSTA]

CREATE TABLE auditoria.evento (
  id           bigint GENERATED ALWAYS AS IDENTITY,
  ocorrido_em  timestamptz NOT NULL,
  tipo_evento  smallint    NOT NULL,
  categoria    smallint    NOT NULL,
  usuario_id   uuid,
  cliente_bd_id uuid       NOT NULL,
  clinica_id   uuid,
  paciente_id  int,
  recurso      varchar(60),
  recurso_id   varchar(64),
  componente   varchar(60) NOT NULL,
  ip           varchar(45),
  trace_id     char(32),
  resultado    smallint    NOT NULL,
  detalhe      jsonb,
  hash_linha   bytea       NOT NULL,
  PRIMARY KEY (ocorrido_em, id)
) PARTITION BY RANGE (ocorrido_em);
CREATE INDEX ON auditoria.evento (usuario_id, ocorrido_em);
CREATE INDEX ON auditoria.evento (paciente_id, ocorrido_em) WHERE paciente_id IS NOT NULL;
CREATE INDEX ON auditoria.evento USING brin (ocorrido_em);
REVOKE UPDATE, DELETE, TRUNCATE ON auditoria.evento FROM gemed_app;

Partições mensais criadas e rotacionadas por pg_partman (ou rotina própria). A chave primária precisa conter a chave de partição, por isso (ocorrido_em, id). Sem compressão de página nativa: as opções são sistema de arquivos com compressão (ZFS), TimescaleDB ou Citus colunar; decidir após medir o volume (P-LOG-03). Partição antiga pode sair com DETACH PARTITION [CONCURRENTLY] para arquivamento, desde que continue visível na tela (NGS1.07.07b). Gatilho opcional que recusa UPDATE e DELETE como segunda barreira.

4.4.4 Equivalência de pacientes (cliente) [PROPOSTA]

auditoria.PacienteEquivalencia (PacienteIdOrigem int, PacienteIdDestino int, UnificadoEm datetime2(3), UsuarioId uniqueidentifier). Usada só se houver unificação de pacientes (EX-LOG-09); a consulta por IdPaciente inclui os de origem. Criar quando a unificação existir (P-LOG-08).

4.4.5 Log de sistema

Sem tabelas. Loki (rótulos service, environment, level; campos estruturados para o resto) e Prometheus. Retenção e armazenamento de objetos (MinIO/S3) a definir após medir o volume (P-LOG-02).

4.5 Lacunas e propostas

IDLacunaProposta
L-01Não há tabela de trilha real em nenhuma das 4 basesCriar auditoria.Evento em IpSeguranca e em cada banco de cliente
L-02Não há perfil “Auditor” nem direito de visualização de auditoria em IpSegurancaSeeds em 4.6
L-03Log/LogComplemento sem finalidade confirmadaManter como estão; decidir em P-LOG-07
L-04Sem equivalência de paciente4.4.4, quando necessário
L-05Sem rotina de criação de partiçõesRotina agendada que cria as partições dos próximos 3 meses [PROPOSTA]
L-06Sem papel de banco só de inserção para a aplicaçãoCriar papéis aplicação e dono, separados

4.6 Seeds da Funcionalidade

SeedTabela(s)Por cliente ou global?StatusDetalhe
Processo “Auditoria” (rota da tela, contexto Segurança)IpSeguranca.ProcessoGlobal[PROPOSTA]Processo, Nome, Prefixo, Sistema, Contexto, Rota a seguir o padrão dos processos existentes (conferir na Fase 4)
Direito “Visualizar auditoria”IpSeguranca.DireitoProcessoGlobal[PROPOSTA]Direito, Nome, Tipo, domínio conforme os direitos existentes
Ligação processo–direitoIpSeguranca.ProcessoDireitoGlobal[PROPOSTA]Acao conforme o padrão existente
Perfil “Auditor”IpSeguranca.ClientePerfilAcessoPor cliente (ClienteBDId)[PROPOSTA]Criado por cliente na implantação; sem usuários por padrão
Permissão do perfil “Auditor” ao processotabela de ligação perfil–processo (ClientePerfilAcessoProcesso, conferir nome exato no DDL)Por cliente[PROPOSTA]Concede “Visualizar auditoria”
Papéis de banco da aplicação (inserção e consulta) e do donoauditoria nos dois bancosPor banco[PROPOSTA]Scripts de implantação
Partições iniciaisauditoria.EventoPor banco[PROPOSTA]Mês corrente e 3 seguintes

O administrador do cliente já poderá ser vinculado ao direito pela gestão de perfis existente.

4.7 Migração

Nada é migrado para a trilha nova. Os bancos de clientes em produção no legado não ganham a trilha; ela nasce com o Gemed 2.1 (GescomClienteAlfa). A troca de SQL Server para PostgreSQL, se ocorrer, copia a trilha por partição (mantendo Id e HashLinha, que independem do motor).