Saltar a contenido

Audit Trail y PII

GexCom implementa un registro de auditoria inmutable y proteccion de datos personales (PII) segun la Ley 1581/2012 de Colombia.

Audit Trail

Arquitectura

El IAuditLogger es un Port (Protocol) implementado por SqlAuditLogger en infrastructure:

# src/gexcom/application/ports/audit.py
class IAuditLogger(Protocol):
    def log(
        self,
        tabla: str,
        accion: str,
        usuario_id: UUID | None,
        detalles: dict[str, object],
    ) -> None: ...

Use Cases que Registran Auditoria

Use Case Accion registrada
CrearNotificacionUseCase CREAR_NOTIFICACION
CambiarEstadoNotificacionUseCase CAMBIAR_ESTADO
RegistrarProcesoUseCase CREAR_PROCESO
GestionarSujetoUseCase CREAR_SUJETO, ACTUALIZAR_SUJETO
GestionarAudienciaUseCase CREAR_AUDIENCIA
DispatchNotificacionUseCase DESPACHO_NOTIFICACION

Schema del AuditLog

CREATE TABLE audit_log (
    id         SERIAL PRIMARY KEY,      -- autoincrement, append-only
    tabla      VARCHAR NOT NULL,
    accion     VARCHAR NOT NULL,
    usuario_id UUID,                    -- NULL si accion automatica
    detalles   JSONB,
    timestamp  TIMESTAMP DEFAULT NOW()
);

Inmutabilidad en PostgreSQL

Un trigger impide modificaciones al audit_log:

-- src/gexcom/infrastructure/persistence/audit_trigger.py
CREATE RULE audit_log_no_delete
    AS ON DELETE TO audit_log DO INSTEAD NOTHING;

CREATE RULE audit_log_no_update
    AS ON UPDATE TO audit_log DO INSTEAD NOTHING;

Solo PostgreSQL

El trigger de inmutabilidad aplica unicamente en PostgreSQL. En SQLite (desarrollo/tests) no hay proteccion a nivel de DB — confiar en que el codigo de aplicacion nunca elimina registros de audit_log.

Backlog P03

AUDIT-TRUNCATE: El comando TRUNCATE no esta bloqueado por las reglas (solo DELETE/UPDATE). En produccion se recomienda revocar el privilegio TRUNCATE al usuario de la aplicacion con REVOKE TRUNCATE ON audit_log FROM gexcom_user;.

PII — Proteccion de Datos Personales

GexCom maneja datos de identificacion personal (PII) de las partes procesales. Cumple con la Ley 1581/2012 (Habeas Data) mediante:

1. Enmascaramiento en UI

# src/gexcom/interfaces/gui/pii_masking.py

def mask_dni(dni: str) -> str:
    """123456789 → ***6789"""
    if len(dni) <= 4:
        return "****"
    return "*" * (len(dni) - 4) + dni[-4:]

def mask_email(email: str) -> str:
    """juan.perez@mail.com → j***@mail.com"""
    local, domain = email.split("@", 1)
    return local[0] + "***@" + domain

def mask_telefono(telefono: str) -> str:
    """3001234567 → ******4567"""
    if len(telefono) <= 4:
        return "****"
    return "*" * (len(telefono) - 4) + telefono[-4:]

2. PII Excluido de Logs

Los campos dni, email y telefono de sujetos nunca se incluyen en: - Logs de structlog - Mensajes de error - Detalles del AuditLog (se usan UUIDs en su lugar)

# CORRECTO — usar ID, no PII
audit_logger.log("sujeto", "CREAR", usuario_id, {"sujeto_id": str(sujeto.id)})

# INCORRECTO — no incluir PII en logs
# audit_logger.log("sujeto", "CREAR", usuario_id, {"dni": sujeto.dni})

3. Control de Acceso por Rol

En la pagina Directorio:

Rol DNI Email Telefono
ADMINISTRADOR Completo Completo Completo
NOTIFICADOR Enmascarado (***6789) Enmascarado (j***@mail.com) Enmascarado (***4567)

4. Directorio Siempre Accesible

Segun la politica del CSJ Bello, el directorio de contactos debe ser accesible para el notificador en todo momento (aunque con datos enmascarados). Esta restriccion esta documentada en CLAUDE.md:

Directorio de contactos accesible en cualquier momento

Resumen de Cumplimiento

Requerimiento Implementacion Estado
PII enmascarado en UI pii_masking.py aplicado en 4 paginas
PII excluido de logs Convencion de codigo verificada en auditoria
Audit trail inmutable Rules PG + append-only en aplicacion
RBAC en datos sensibles check_permission() en todas las paginas
Lockout cuenta TTL 15 min persistente en DB
TRUNCATE bloqueado ⏳ P03