99 lines
13 KiB
Markdown
99 lines
13 KiB
Markdown
# R2.0 — Contrato de seguridad
|
||
|
||
> **Ratificación posterior — 24 de octubre de 2026:** D01–D10 y D04-R ratificadas en los términos recomendados de R2.0; variante A, Resource único con `scope` explícito. Solo se autoriza implementar R2.1a. El registro vigente de alcance y evidencias está en [11 — Lectores compatibles](11-r2-1a-lectores-compatibles.md). Los estados «propuesto», «recomendado» y «pendiente» del cuerpo se conservan como evidencia de la versión documental inicial y quedan actualizados por este registro; ratificación conceptual no equivale a ejecución de R2.1b–R2.6.
|
||
|
||
Estado: diseño propuesto, condicionado a D03/D04/D05. Las restricciones de integridad/no exposición están establecidas por la petición; la asignación de capacidades no se da por aprobada.
|
||
|
||
## 1. Separar cuatro garantías
|
||
|
||
| Garantía | Implementación actual comprobada | Contrato v2 |
|
||
| --- | --- | --- |
|
||
| Pertenencia operativa | Compañía en instalación/configuración/activo/perfil | Inmutable y obligatoria; no se deriva del dueño global |
|
||
| Integridad de referencias | IDs contrastados con recursos cargados de compañía | Propósito autoriza global elegible o local de la misma compañía |
|
||
| Visibilidad del operador | Sesión activa y espacio compartido; sin ACL granular operador–compañía | Mantener explícitamente ese modelo si D04 se ratifica; no proclamar aislamiento de operador inexistente |
|
||
| Administración global | No guard específico Resource; isInitialAdmin para usuarios | Evaluación explícita de capacidad propuesta en Identity antes de global CRUD/historial |
|
||
|
||
[require-authenticated.ts:6](../../../modules/identity/src/api/infrastructure/http/require-authenticated.ts#L6) [relationship-read-access.ts:7](../../../apps/server/src/infrastructure/http/relationship-read-access.ts#L7) [software-installation-policy.ts:5](../../../modules/companies/src/api/application/software-installation-policy.ts#L5) [application-configuration-policy.ts:5](../../../modules/companies/src/api/application/application-configuration-policy.ts#L5) [identity.use-cases.ts:75](../../../modules/identity/src/api/application/identity.use-cases.ts#L75). «No local B desde A» significa que la operación contextual A nunca devuelve/asocia B. En el modelo compartido el mismo operador puede abrir explícitamente B y operar en B; eso no es mezcla de referencias ni prueba una ACL. Si el negocio quiere impedir ese cambio, necesita ACL adicional antes de activar, no un filtro frontend.
|
||
|
||
## 2. Capacidades conceptuales y asignación propuesta
|
||
|
||
Los nombres siguientes son vocabulario del contrato, **no permisos existentes** ni una nueva tabla de roles. No todos requieren un grant persistido:
|
||
|
||
| Capacidad conceptual | Sujeto propuesto | Operaciones |
|
||
| --- | --- | --- |
|
||
| resources.global.read | Cualquier sesión válida del espacio de soporte | Listado y ficha pública de producto, incluida referencia inactiva |
|
||
| resources.global.manage | Solo sesión válida de isInitialAdmin | Alta/edición/status/reactivación; ficha administrativa; historial global; mutaciones de definiciones globales que puedan alterar productos |
|
||
| resources.company.manage | Operadores de soporte bajo el modelo compartido actual | CRUD local en contexto explícito C, mismas restricciones existentes |
|
||
| Consulta operativa | Operadores con contexto C válido | Listados/historial operativos de C y referencias mínimas por propósito |
|
||
|
||
Historial administrativo se protege con manage; no crear history.read delegable sin consumidor. El manager no recibe un bypass de ámbito ni una lista de clientes/instalaciones al consultar producto global. Sin sesión: 401. Con sesión sin capacidad requerida: 403 antes de acceder a datos administrativos. Con contexto C o ID inaccesible: respuesta uniforme según 04.
|
||
|
||
**Identity:** proponer un guard/servicio público de autorización; no importar el método privado administrator ni UserRepository desde Resources. Evaluar sesión vigente y flag isInitialAdmin en backend en cada operación administrativa. La API de sesiones actual solo expone isAuthenticated, insuficiente para acreditar manage. [module.ts:19](../../../modules/identity/src/api/module.ts#L19) [user.aggregate.ts:8](../../../modules/identity/src/api/domain/user.aggregate.ts#L8).
|
||
|
||
Asignación inicial por política, sin backfill de grants: admin inicial recibe manage si D03 es ratificada. Ningún operador concede ni revoca esta capacidad individual en v2 inicial. Una capacidad delegable necesitaría modelo/almacenamiento, protección de concesión y revocación e invalidación de sesiones, y se difiere. Ni body ni query admiten isInitialAdmin, capabilities, actorId o companyId global para elevarse.
|
||
|
||
## 3. Lectura y escritura por ID
|
||
|
||
Orden obligatorio en cada frontera nueva:
|
||
|
||
1. Autenticar sesión del servidor; comprobar user activo/authVersion. Resolver permiso por operación antes de historial/gestión global.
|
||
2. Validar params/query/headers/body por schema estricto. En operación de compañía validar C y su existencia/contexto de soporte.
|
||
3. Buscar UUID con ámbito/tipo apropiados al propósito. Si no coincide, mismo resultado que no encontrado; no recuperar y serializar valores ajenos antes de autorizar.
|
||
4. Para escritura global comprobar scope global, plantilla elegible y ausencia de integración; para local comprobar owner=C. Repetir la comprobación aunque el selector o URL ya lo sugiera.
|
||
5. Para asociación validar activo/perfil/persona/licencia de C y actividad del producto en la misma unidad de escritura. El propósito lo selecciona el servidor que ejecuta software/configuración, no el payload del navegador.
|
||
6. Construir DTO de proyección concreta. Registrar requestId/resultado e IDs necesarios sin SQL, stack, cookies, token, notas ni características libres.
|
||
|
||
Permiso global no permite convertir un local a global por cambiar scope, owner o templateId: esos campos no son editables en el schema y el agregado los conserva. Tampoco permite crear instalación de A usando licencia de B. No aceptar un header de versión, creationId o cursor como credencial.
|
||
|
||
Origen/CSRF actuales siguen aplicándose a todos los POST, incluida resolución POST de lectura por lotes; no se desactiva seguridad por ser «solo resolver». Cookie y sesión permanecen en backend; no introducir tokens en localStorage. [identity.routes.ts:105](../../../modules/identity/src/api/infrastructure/http/identity.routes.ts#L105) [identity-runtime.ts:17](../../../apps/server/src/infrastructure/http/identity-runtime.ts#L17).
|
||
|
||
## 4. Seguridad de plantillas y campos
|
||
|
||
Una persona que puede editar una definición global podría propagar cambios a todos los productos. Se recomienda exigir manage para mutaciones de plantilla/campo global en el catálogo de recursos, al menos cuando afecten a productos globales; para evitar permiso cambiante según existan referencias, la primera versión aplica guard a **todas** las mutaciones globales del catálogo de Resources. La lectura y overrides por compañía mantienen su contrato. Es una restricción nueva de autorización pendiente de D03, no una refactorización ya aprobada.
|
||
|
||
Comprobar owner de plantilla/campos y stockKey contra catálogo real; no validar por builtin boolean de cliente. Un campo local no puede entrar en especificación global mediante plantilla manipulada. Campos globales adicionales siguen siendo privados salvo whitelist de publicación. Propagación bloquea raíz de catálogo y falla atómicamente en DML ante inconsistencia. [resource-catalog.routes.ts:65](../../../modules/resources/src/api/infrastructure/http/resource-catalog.routes.ts#L65) [prepare-resource-template-updates.ts:15](../../../modules/resources/src/api/infrastructure/persistence/prepare-resource-template-updates.ts#L15).
|
||
|
||
## 5. Privacidad y proyecciones
|
||
|
||
| Información | Producto público autenticado / candidato | Administración global | Relación operativa C |
|
||
| --- | --- | --- | --- |
|
||
| UUID, nombre, scope, template UUID, clasificación application | Sí | Sí | Referencia mínima |
|
||
| Fabricante/proveedor, URL, descripción de producto aprobadas | Público/ficha; candidato solo proveedor | Sí | Solo si hace falta para reconocer |
|
||
| active del producto | Sí; candidatos solo activos | Sí | Deriva aviso de relación |
|
||
| Notes de Resource y características extra de plantilla | No | Sí | No copiar al consumidor |
|
||
| Especificación completa y campos históricos | No | Sí | No en referencia mínima |
|
||
| ActorId/actorName/fechas del historial global | No | Sí, endpoint administrativo paginado | No; historia propia de C sigue su contrato |
|
||
| Compañías usuarias, contadores de uso transversales | No | No en contrato inicial | Solo operación propia C |
|
||
| Activos/personas/versiones/licencias/configuraciones de B | No | No por capacidad global | No desde A |
|
||
|
||
Historial público de producto sin actor se **difiere**; no hay necesidad para seleccionar. Fechas y actores globales solo se muestran a manager en auditoría administrativa. Historial operativo conserva sus actores/fechas/versiones de compañía, sin sumar quién instaló en otras compañías. Resolver nombre actual de producto no garantiza nombre histórico inmutable: se conserva semántica actual y los snapshots de Resource originales, que no se publican como historial operativo. [resource-persistence.mapper.ts:24](../../../modules/resources/src/api/infrastructure/persistence/resource-persistence.mapper.ts#L24) [software-installations.ts:19](../../../modules/companies/src/common/software-installations.ts#L19) [application-configurations.ts:20](../../../modules/companies/src/common/application-configurations.ts#L20).
|
||
|
||
## 6. Sesiones y caché de permisos
|
||
|
||
Validar active/authVersion como hoy en cada petición; ningún resultado global administrativo se reutiliza para otro sujeto. Si el privilegio se mapea a isInitialAdmin, se lee del usuario vigente, no de un booleano en navegador. No cachear autorización entre peticiones en la primera versión; contexto inmutable emitido por servidor válido solo para petición/unidad de trabajo. Cerrar sesión/contraseña/cambio de authVersion anula peticiones posteriores.
|
||
|
||
Una sesión aceptada justo antes de invalidarse puede completar una transacción ya iniciada: este contrato fija autorización al inicio de la operación, sin prometer cancelación retroactiva. Las solicitudes nuevas deben fallar. Un futuro grant/revoke delegable requerirá incrementar versión de autorización o consulta equivalente que no deje privilegios cacheados.
|
||
|
||
Query keys de datos administrativos incluyen sujeto y versión de sesión; al logout/401/403 se elimina caché correspondiente, no se presenta stale como permiso. Las proyecciones públicas y administrativas nunca comparten key. Cambio global requiere invalidación/refetch explícito; no supone que otros navegadores reciben notificación instantánea. Ver [04](04-contratos-api-dto.md).
|
||
|
||
## 7. Casos de abuso y expectativas
|
||
|
||
Los IDs S01–S12 son pruebas obligatorias del plan 08; todos son futuros, no pentest ejecutado.
|
||
|
||
| Caso | Intento | Resultado verificable |
|
||
| --- | --- | --- |
|
||
| S01 | Operador normal POST alta/update/status global | 403 antes de datos administrativos; cero cambios/revisiones |
|
||
| S02 | Admin escribe ruta global con ID de Resource local A/B | 404 uniforme; no cambio de owner/scope; no nombre ajeno |
|
||
| S03 | Operador asocia local B en software/perfil A o usa global como licencia | *_REFERENCE_NOT_FOUND; cero altas, incluso si falsifica purpose/scope |
|
||
| S04 | Usuario envía isInitialAdmin/actorId/capabilities/companyId en body global | 400 schema estricto; privilegio no cambia |
|
||
| S05 | Sesión invalidada por authVersion/cambio de contraseña | 401 en nueva petición aunque cachee catálogo o formulario abierto |
|
||
| S06 | Usuario lee /admin o /history global; inspecciona candidato y ficha pública | 403 en admin/history; resto no incluye notas, actores, B ni campos extra |
|
||
| S07 | Lista/batch contiene IDs inexistentes, ajenos y de tipo equivocado | Misma forma unavailable, sin claves que indiquen B ni total de denegados por compañía |
|
||
| S08 | Global creado con plantilla local o copia usage application | Rechazo de elegibilidad; no proyección operativa |
|
||
| S09 | Se amplía list genérico al probar remoto/Access/SMB/certificados | Test falla: ningún global es candidato/target en esos consumidores |
|
||
| S10 | Alta de relación concurrente con desactivación | Resultado serial definido en 05, sin commit de nueva asociación tras desactivación ya serializada |
|
||
| S11 | Normal modifica campo/plantilla global que propagaría a productos | 403 con guard propuesto; sin revisiones parciales |
|
||
| S12 | Cambiar companyId en URL bajo modelo compartido | Nueva operación B puede estar autorizada; referencia local B sigue rechazada dentro de A. Si se exige ACL, test debe cambiar tras ADR específico |
|
||
|
||
No devolver un 403 que distinga ID ajeno de inexistente en resolución contextual; 403 se usa para la capacidad/contexto como conjunto, no para revelar existencia del recurso. Sin conexión a BD ni ejecución de escenarios en R2.0.
|