Docs PayMind
10
Seguridad y Cumplimiento — PayMind
PII laboral máxima sensibilidad, RBAC por rol RRHH, cifrado y compliance laboral
Versión
v1.0
Fecha
Mayo 2026
Audiencia
Founders · Dev · Legal
Estado
DRAFT
PAYMIND
Inteligencia que cuida a tu equipo. Sin errores, sin sustos, sin sorpresas.
Módulo #4 — Cheryx Suite

[CONSEJO — Doc 10] Contrarian: PayMind procesa el dato más personal que existe en el contexto laboral: el salario de cada empleado. En muchas culturas LATAM, el salario es un dato más privado que el número de cuenta bancaria. Si un empleado descubre que su colega ganó más por el mismo trabajo, el daño relacional puede ser irreversible. El sistema debe garantizar que ningún empleado puede ver el salario de otro empleado, incluso si tienen el mismo nivel de acceso. Auditor Seg.: El escenario de ataque más realista para PayMind no es un hacker externo — es un empleado interno con acceso al sistema que descarga todos los registros de salarios. El RBAC debe limitar estrictamente qué roles pueden ver salarios, y el acceso debe quedar en el audit log con cada consulta. Executor: Compliance de protección de datos para PayMind es más complejo que para los otros módulos porque los "datos personales" son datos de empleados — personas con derechos laborales específicos adicionales a los derechos ARCO generales. En CR, el empleado tiene derecho a solicitar copia de su expediente a la empresa (Art. 69 Código de Trabajo).


1. Modelo de amenazas PayMind

Amenaza Vector Control
Spoofing Gerente RRHH accede como otro empleado MFA + JWT con claim de rol verificado en cada request
Tampering Modificar el salario de un empleado antes de confirmar la planilla Audit log de cada cambio + aprobación dual para cambios de salario >20%
Repudiation Empleado dice "no aprobé mis vacaciones" Firma digital del empleado en solicitud self-service
Information Disclosure Empleado ve salarios de colegas Column-level encryption + RBAC restrictivo + audit log de lectura
Elevation of Privilege Asistente de RRHH accede a función de confirmar planilla RBAC estricto: solo RRHH Manager y Owner pueden confirmar planillas
DoS Reintento masivo de cálculos de planilla Rate limit por tenant en endpoint /calcular-borrador

2. Column-level encryption para datos sensibles

Tres campos requieren cifrado a nivel de columna (no solo disco):

# Datos sensibles cifrados con pgcrypto (key en Doppler, no en código)
CAMPOS_SENSIBLES = {
    'pm_empleados': ['cedula_enc', 'salario_mensual_enc', 'iban_enc'],
}

# Acceso al salario — SOLO desde PayrollService, con log de auditoría
class PayrollService:
    async def get_salario_empleado(self, empleado_id: UUID, tenant_id: UUID) -> Decimal:
        row = await self.db.execute(
            "SELECT pgp_sym_decrypt(salario_mensual_enc, :key)::numeric FROM pm_empleados WHERE id = :id AND tenant_id = :tid",
            {"key": APP_ENCRYPTION_KEY, "id": empleado_id, "tid": tenant_id}
        )
        # Log obligatorio
        await self.audit_log.log(
            accion='pm_salario_accedido',
            registro_id=empleado_id,
            tenant_id=tenant_id
        )
        return Decimal(row.scalar())

3. RBAC PayMind

Rol Puede ver salarios Puede editar empleados Puede confirmar planilla Puede aprobar vacaciones
Owner ✅ Todos
RRHH Manager ✅ Todos (con audit log)
Gerente Financiero ✅ Solo totales de planilla
Jefe de Departamento ✅ Solo su equipo
Empleado self-service ✅ Solo el propio ✅ Solo datos propios ✅ Solo propias solicitudes
Contador externo / CPA ✅ Solo totales planilla
Cheryx Admin ❌ (nunca datos de empleados)

3.1 Enforcement en FastAPI

async def verificar_acceso_salario(
    user: CurrentUser,
    empleado_id: UUID,
    db: AsyncSession
):
    if user.rol not in ['owner', 'rrhh_manager']:
        if user.rol == 'empleado' and user.empleado_id != empleado_id:
            raise PermissionError("No puede acceder al salario de otro empleado")
        elif user.rol != 'empleado':
            raise PermissionError("Rol no autorizado para ver salarios individuales")
    # Si tiene acceso, registrar en audit log antes de retornar
    await audit_log.log_salary_access(user.id, empleado_id, user.tenant_id)

4. Inmutabilidad de planillas confirmadas

Una vez que el RRHH confirma una planilla (y se genera el archivo banco), es inmutable:

REVOKE UPDATE ON pm_planillas FROM app_role;
REVOKE DELETE ON pm_planillas FROM app_role;
REVOKE UPDATE ON pm_planilla_detalle FROM app_role;
REVOKE DELETE ON pm_planilla_detalle FROM app_role;

Si hay un error en una planilla ya confirmada (ej: el salario de un empleado estaba mal): 1. No se modifica la planilla original — es inmutable 2. Se crea una planilla de corrección en el siguiente período que incluye el ajuste 3. El audit log registra la corrección con referencia a la planilla original


5. Compliance de protección de datos — datos laborales

5.1 Ley 8968 CR — datos de empleados

Los datos de los empleados son datos personales de los propios empleados (no del tenant). El tenant es el empleador y tiene obligación legal de protegerlos. BookMind (como sistema del empleador) es Encargado de datos del empleador, pero en el caso de PayMind, la situación es más compleja:

Dato Titular del dato Responsable Encargado
Datos del empleado (nombre, cédula, salario) El empleado El empleador (tenant) PayMind/Cheryx
Datos de planilla (cálculos, deducciones) El empleado El empleador (tenant) PayMind/Cheryx

El empleado tiene derecho a: - Acceder a sus propios datos (portal self-service) - Solicitar copia de su expediente (exportación PDF) - Solicitar corrección de datos incorrectos

PayMind NO puede borrar datos laborales a solicitud del empleado mientras exista relación laboral (y hasta 4 años después por prescripción de acciones laborales CR).

5.2 Artículo 69 Código de Trabajo CR

El empleado tiene derecho a solicitar copia de su expediente al empleador. PayMind implementa esto como feature de exportación en el portal self-service: el empleado puede descargar su expediente completo (contratos, planillas, incapacidades, evaluaciones) en PDF.


6. Tablas regulatorias — control de integridad

Las tablas regulatorias son el dato más crítico del motor de planilla:

class TablaRegulatoriasService:
    async def publicar_tabla(self, tabla: NuevaTablaRegulatoriaRequest, aprobador: UUID):
        # Paso 1: Validar la tabla contra fixtures de referencia
        resultados = []
        for fixture in cargar_fixtures_validacion(tabla.pais):
            motor = get_motor(tabla.pais)
            resultado = motor.calcular_con_tabla(fixture.input, tabla)
            resultados.append((fixture, resultado))
            if abs(resultado.salario_neto - fixture.esperado_neto) > 1:
                raise ValueError(f"Tabla falla fixture {fixture.id}: esperado {fixture.esperado_neto}, obtenido {resultado.salario_neto}")

        # Paso 2: Requiere aprobación de dos personas (dual control)
        await self.pending_approvals.crear(tabla, aprobador, requiere_segundo_aprobador=True)

        # Paso 3: Solo después de aprobación dual → publicar
        # La tabla es inmutable desde el momento de publicación

7. Checklist de seguridad pre-lanzamiento PayMind


8. Log de auditoría PayMind

Eventos específicos que deben quedar en sm_audit_log:

Evento Cuándo Datos auditados
pm_salario_accedido Cada vez que se lee un salario individual user_id, empleado_id, timestamp, IP
pm_salario_modificado Cambio de salario de cualquier empleado user_id, empleado_id, salario_anterior_hash, motivo
pm_planilla_confirmada Confirmación de planilla user_id, planilla_id, total_neto, n_empleados
pm_tabla_regulatoria_publicada Nueva tabla de tasas publicada user_id, pais, version, aprobadores
pm_empleado_liquidado Liquidación de empleado user_id, empleado_id (solo hash), motivo
pm_expediente_exportado Empleado exporta su expediente empleado_id, timestamp

Ver también: Doc 08 (Modelo de Datos — column-level encryption en pm_empleados) · Doc 09 (Motor Core — inmutabilidad del cálculo) · Doc 14 (Marco Legal — DPA y derechos del empleado)