Saltar a contenido

0009. Gestión de usuarios y asignación de permisos

Estado: Propuesto Fecha: 2026-09-08 Autor: Duval Alcivar Módulo(s) afectado(s): svc-identidad, frontend (React 18), ERP/CRM (consumidores)

Contexto

El ADR 0005 define las tablas del usuario (mae_usuarios, mae_personas, rel_usuario_empresa, rel_usuario_sucursal), el ADR 0022 el patrón party (mae_entidades, mae_personas_juridicas) y el ADR 0007 los tipos de usuario (cat_tipos_usuario) y los permisos individuales. Falta definir cómo se gestionan en operación: cómo se da de alta un usuario, cómo se le asigna acceso a empresas, cómo se le entregan permisos (generales y por módulo) y qué endpoints expone svc-identidad para el resto de la plataforma.

Regla de negocio: toda la gestión de identidad pasa por svc-identidad. Ningún servicio crea usuarios ni asigna permisos por su cuenta; solo los consume (vía token o API de consulta).

Decisión

Exponer una API de administración de identidad (servicio interno, autenticada con token de servicio) que centraliza:

  1. Alta/edición de personas y usuarios.
  2. Asignación de acceso a empresas (rel_usuario_empresa).
  3. Asignación de permisos individuales (generales y por módulo) y de páginas del menú.
  4. Consulta de identidad para el resto de los servicios (parámetro GET /api/v1/usuarios/{id}).

1. Endpoints de gestión

Endpoint Método Descripción
/api/v1/usuarios POST / GET alta (un payload, según tipo_entidad) y listado de usuarios (ADR 0022)
/api/v1/usuarios/{id} GET / PATCH / DELETE consultar, editar, soft-delete
/api/v1/usuarios/{id}/tipo PATCH cambiar tipo_usuario_id (usuario/root/externo, ADR 0007)
/api/v1/usuarios/{id}/empresas PUT reemplazar acceso a empresas (rel_usuario_empresa)
/api/v1/usuarios/{id}/permisos PUT reemplazar permisos individuales del usuario en una empresa
/api/v1/usuarios/{id}/menu PUT reemplazar páginas del menú visibles (rel_usuario_menu)
/api/v1/usuarios/{id}/menu/{menu_id}/acciones PUT / GET reemplazar / consultar acciones finas del usuario en una página (rel_usuario_menu_accion, ADR 0015)
/api/v1/permisos GET listar el catálogo de permisos (ADR 0007)
/api/v1/empresas/{id}/aplicaciones PUT qué aplicaciones usa la empresa (rel_empresa_aplicacion, ADR 0006)

Los PUT de asignación son declarativos: el cliente envía la lista completa y el backend sincroniza (inserta, actualiza is_activo, elimina lo que sobrou). Evita bugs de "editar fila por fila".

Todos los endpoints viven bajo /api/v1 (convención de contratos, ADR transversal 0016).

2. Alta de un usuario (flujo)

1. POST /api/v1/usuarios → un solo payload con el `tipo_entidad` elegido:
   crea la entidad (natural o jurídica), la persona y la cuenta en una sola
   transacción (ADR 0022). Pydantic exige los campos obligatorios de cada tipo.
2. PUT /api/v1/usuarios/{id}/empresas → {empresa_ids: [1, 2]}
                                   → rel_usuario_empresa activa
3. PUT /api/v1/usuarios/{id}/permisos → {empresa_id, permiso_ids: [...]}
                                   → rel_usuario_permiso
4. PUT /api/v1/usuarios/{id}/menu     → {empresa_id, menu_ids: [...]}
                                   → rel_usuario_menu (ADR 0012)

El tipo_entidad del cuerpo discrimina la variante; el frontend pide los campos requeridos según la opción elegida.

// Persona natural
{
  "tipo_entidad": "PERSONA_NATURAL",
  "tipo_usuario": "usuario",
  "username": "jperez",
  "email": "jperez@empresa.com",
  "password": "una-clave-temporal",
  "entidad": { "identificacion": "0912345678", "nombres": "Juan", "apellidos": "Pérez" }
}

// Persona jurídica externa
{
  "tipo_entidad": "PERSONA_JURIDICA",
  "tipo_usuario": "externo",
  "username": "acme",
  "email": "contacto@acme.com",
  "password": "una-clave-temporal",
  "entidad": {
    "nombre": "ACME S.A.",
    "nombre_comercial": "ACME",
    "identificacion": "1790012345001",
    "pais_id": null,
    "ciudad_id": null,
    "tipo_identificacion_id": null
  }
}
  • La contraseña inicial se genera al azar; password_temp = TRUE obliga a renovarla en el primer login (ADR 0010/0011).
  • El tipo por defecto es usuario; solo se cambia a root vía PATCH /api/v1/usuarios/{id}/tipo (acción restringida a otro root).
  • Un usuario sin empresas asignadas no puede loguear (403, ADR 0010).
  • Asignar permisos no cambia el JWT ya emitido: los nuevos permisos valen en el próximo login/refresh. Se puede forzar deslogueo (revocar refresh) si un permiso sensible se retira hoy.

3. Asignación por módulo / página (individual)

Los permisos se asignan directamente al usuario, siempre referenciando un módulo/página del catálogo. No existen perfiles que agrupen permisos.

  • Generales: rel_usuario_permiso con un permiso global (sin FK de módulo, empresa_id NULL).
  • Por módulo: rel_usuario_permiso apuntando a un permiso con modulo_id.
  • Por página: rel_usuario_menu (qué páginas ve) y, cuando exista, rel_usuario_menu_accion (qué acciones de esa página puede ejecutar).
  • root no recibe asignaciones: su acceso a todo es implícito (ADR 0007).
usuario (mae_usuarios.tipo_usuario_id → cat_tipos_usuario)
   │  si es "usuario"
   ▼
permisos (rel_usuario_permiso) ──▶ cat_permisos (generales y por módulo)
   │
   ▼
páginas (rel_usuario_menu) ──▶ páginas del menú (visor/sidebar, ADR 0012)
   │
   ▼
JWT: tipo_usuario: "usuario", permisos: ["rrhh.nomina.ver", ...]

4. Consulta de identidad para otros servicios

Otros servicios no escriben identidad; solo consultan. GET /api/v1/usuarios/{id} devuelve la vista pública (persona + estado), útil para mostrar "creado por" o auditoría. El alcance por empresa para consultas de negocio lo da el empresa_id del token (RLS), no esta API.

5. Seguridad de la API de administración

  • Solo accesible con token de servicio (aud para el backend) o por un admin de plataforma; nunca es de acceso público.
  • Todo cambio se escribe en audit_log (ADR transversal 0014) con actor.
  • La contraseña nunca se expone; solo se permite reset (genera temporal y revoca sesiones).

Alternativas descartadas

  • Que cada servicio cree sus propias cuentas — fragmenta identidad, imposible SSO y auditoría única.
  • Perfiles/roles dinámicos que agrupen permisos — descartado por regla de negocio: solo existen 2 tipos de usuario y los permisos son individuales (ADR 0007).
  • Gestionar identidad conectando cada servicio a la BD de core_sigfa — rompe el aislamiento por base de datos (ADR transversal 0003); la API es el único punto de entrada.

Consecuencias

  • Toda la identidad se administra en un solo servicio y pasa por auditoría.
  • La asignación de permisos es declarativa y reversible (soft-delete, is_activo).
  • Los servicios consumidores solo leen; el token es su fuente de identidad.
  • Resuelto: la edición de empresa / soft-delete de empresa (CRUD completo de mae_empresas aparte del alta) está en el ADR 0016; las acciones finas por página en el 0015.