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:
- Alta/edición de personas y usuarios.
- Asignación de acceso a empresas (
rel_usuario_empresa). - Asignación de permisos individuales (generales y por módulo) y de páginas del menú.
- 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
PUTde asignación son declarativos: el cliente envía la lista completa y el backend sincroniza (inserta, actualizais_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 = TRUEobliga a renovarla en el primer login (ADR 0010/0011). - El tipo por defecto es
usuario; solo se cambia arootvíaPATCH /api/v1/usuarios/{id}/tipo(acción restringida a otroroot). - 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_permisocon un permiso global (sin FK de módulo,empresa_id NULL). - Por módulo:
rel_usuario_permisoapuntando a un permiso conmodulo_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). rootno 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_empresasaparte del alta) está en el ADR 0016; las acciones finas por página en el 0015.