metis-desk/docs/auditorias/resources-global-scope/10-plan-implementacion.md
2026-10-25 01:19:17 +02:00

108 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 10 — Plan incremental posterior
Plan **PROPUESTO**, no ejecutado. Dependencias: R2.0 → R2.1 → R2.2 → R2.3 → R2.4/R2.5 → R2.6. Se puede preparar código de autorización/lectura antes de DDL, pero no activar ni crear productos globales hasta completar lectores y controles. El plan preserva los módulos, la identidad Resource y relaciones operativas separadas.
## R2.0 — Cerrar contratos y pruebas de aceptación
- **Objetivo:** aprobar D01–D10 de [09](09-alternativas-arquitectonicas.md), fijar invariantes, visibilidad y límites del catálogo. Resolver si todos los operadores continúan accediendo al espacio de soporte o se requiere ACL nueva por compañía.
- **Áreas:** documentación, schemas propuestos en Resources common, puertos públicos y composition roots de Identity/Resources/Companies/servidor.
- **Dependencias/migraciones:** ninguna migración para especificación y tests de políticas puras; ACL persistente requeriría diseño/migración aparte si se aprueba.
- **Pruebas:** matriz C/otra compañía/global; permisos globales; propósito application/software/license/operativo; inválidos e inactivos; pruebas de contrato antes de cambios.
- **Riesgos:** aprobar implícitamente permiso global o asumir adopción como licencia.
- **Aceptación:** cada decisión con responsable; endpoints/DTO y política de historial acordados, sin recursos ni datos globales creados.
## R2.1 — Persistencia y ámbito
- **Objetivo:** ampliar Resource sin cambiar UUID ni compañías de datos legacy; mantener tablas normalizadas.
- **Áreas:** ResourceProps/ResourceValues según responsabilidad, mapper y InventoryRow, resource.model, resource-inventory-relational, resource-revision-specifications, prepare-resource-template-updates, remove-resource, ResourceDashboardReader; revisar directory/network/support constraints.
- **Dependencias:** D01/D02 y ensayo de esquema efectivo. Si A, introducir scope en raíz y esquema discriminado; si B, mismo alcance salvo columna/CHECK de par.
- **Migraciones:** scope + backfill company y companyId nullable; CHECK y constraints/índices necesarios, conservando FK company. Nada de NULL en relaciones operativas; no JSON ni reasignación de IDs.
- **Pruebas:** migración repetida e interrumpida por paso, filas legacy, combinaciones inválidas, FK operativa local, lectura de globales y revisión histórica, propagación de plantilla sobre globales/locales y campos retirados, rollback antes de activación.
- **Riesgos:** DDL con commit implícito, FK dependiente de índice/columna, lectores legacy, locks y recuentos con NULL.
- **Aceptación:** esquema ensayado en metis_desk_test; todos los datos anteriores siguen company con mismos IDs/revisiones; ninguna ruta permite crear global aún. Inventario SQL de constraints resultante documentado.
## R2.2 — Autorización, contratos y resolución
- **Objetivo:** establecer visibilidad por propósito y restricción de escritura global antes de publicar catálogo.
- **Áreas:** Identity servicio/guard público, rutas Resource y catálogo de definiciones que afecte productos, Resources common/services, ResourceInventoryService o servicio nuevo con consumidor real, runtime, builders y API web.
- **Dependencias:** R2.0/R2.1; preservar contrato genérico list local. Definir serialización de validación/asociación con servicio público sin exportar ORM/repositorio.
- **Migraciones:** ninguna adicional si administrador inicial soporta permiso acordado; una ACL nueva no debe improvisarse como columna ajena a Resources.
- **Pruebas:** usuario normal rechaza alta/edición/status/delete global; revocación de sesión; por ID/lote no revela B desde C; global con plantilla/campo local se rechaza; integración operativa global se rechaza; global inactivo se resuelve pero no se ofrece; no IDOR en actualización/borrado/importadores futuros.
- **Riesgos:** solo ocultar botones, autorizar fuera del servicio o ampliar listado para todos, separar transacciones sin contrato de concurrencia.
- **Aceptación:** políticas probadas vía HTTP además de tests puros; error 403 correcto, sin nombres/SQL/stacks en denegaciones; clientes nuevos y legacy compatibles según contrato aprobado.
## R2.3 — Catálogo global de aplicaciones
- **Objetivo:** crear/consultar/editar/desactivar Resource global con plantilla Aplicación y atributos de producto existentes.
- **Áreas:** Resources web por entidad/operación, global-catalog-content/shell de Companies, i18n es/en, rutas/servicios Resources, ResourceDetail y cache keys.
- **Dependencias:** permisos y resolución completos R2.2, prueba de nombre/duplicados globales, políticas de campos/historial.
- **Migraciones:** ninguna adicional usando name/provider/url/description/active. Categoría u otros metadatos requieren decisión explícita y no son prerequisite de primer catálogo.
- **Pruebas:** CRUD permitido/denegado, inmutabilidad de plantilla/ámbito, nombre concurrente, desactivación idempotente, borrado usado actual/histórico, búsqueda/orden/paginación/retorno; vista producto sin usos ajenos.
- **Riesgos:** confundir ResourceCatalog de plantillas con catálogo de productos; historial global con notas privadas; invalidación entre compañías.
- **Aceptación:** catálogo separado funcionalmente de plantillas, permisos de servidor, componentes existentes reutilizados, cero copias automáticas por compañía; pruebas visuales y evidencias según [08](08-propuesta-ui-ux.md).
## R2.4 — Selectores e instalaciones
- **Objetivo:** buscar globales y locales en una consulta y guardar instalación de compañía con application global.
- **Áreas:** servicio de candidatos/resolución Resources, software policies/mappers/repository, SoftwareInstallationsPanel/Form/History y editor rápido; fichas de activo/puesto/entorno y nombres/enlaces; SequelizeProcessRepository/ProcessReviewReader deben resolver los productos globales usados por configuraciones locales sin ampliar destinos de remoto.
- **Dependencias:** R2.2/R2.3. Antes de activar, lectura por referencia debe funcionar en todos los sitios, no solo el formulario.
- **Migraciones:** ninguna de instalaciones si se conservan resourceId y licencia local; no backfill de producto ni versión. Índices de candidatos solo si ensayo demuestra necesidad.
- **Pruebas:** C crea con global; rechaza local B aunque manipule ID; OS/soporte/licencia global rechazados; licencia local B rechazada; historial/licencia/estado/versiones independientes; inactive conserva notas/retirada; concurrencia deactivate/create; selección con búsqueda/página y homónimos; nuevo producto no asigna licencia.
- **Riesgos:** caché por compañía incompleta, consulta de historial para todo candidato, transacción anidada o lock order inverso, global convertido en target remoto.
- **Aceptación:** buscar Chrome encuentra global y local sin scope previo, guardar conserva ID global; activo/relación/licencia siguen locales; backend valida al guardar; el lector genérico de remoto/certificados sigue local.
## R2.5 — Configuraciones por perfil
- **Objetivo:** reutilizar candidatos application y resolución, preservando perfil/instalación independientes.
- **Áreas:** application policies/mappers/repository, applicationOptions/form controller/form/quick editor, listados/historial, puesto y entorno.
- **Dependencias:** R2.2/R2.3 y contrato/composición compartida de selector R2.4. Puede desarrollarse tras R2.2 en paralelo de código, activación solo con catálogo y resolución listos; esta auditoría no delegó trabajo.
- **Migraciones:** ninguna de application_configurations; resourceId actual ya representa UUID del producto.
- **Pruebas:** application global en perfiles de C y B con distintas versiones/estado; perfil/activo/persona ajenos rechazados; web sin versión; destino fijo; parejas abiertas únicas; inactivo global, retirada, notas y nombres históricos; no instalación creada automáticamente.
- **Riesgos:** copiar filtro de software y admitir OS, descartar inactivos, perder contexto de perfil fijo o foco de modal padre.
- **Aceptación:** ambos flujos usan la misma política application; sus datos/versiones/historias siguen separados y no se propagan cambios operativos entre compañías.
## R2.6 — Duplicados y adopción
- **Objetivo:** convivencia documentada y detección no destructiva de coincidencias; consolidación solo si se aprueba contrato separado.
- **Áreas:** Resources, políticas de identidad/destino de software/application, comandos de migración y auditoría, vistas de coincidencias.
- **Dependencias:** D07/D08, catálogo estable y pruebas de volumen. No es requisito para usar un catálogo nuevo sin migrar recursos previos.
- **Migraciones:** ninguna para coexistir/avisar por lectura. Asociación de adopción o equivalencias requeriría tabla normalizada con identidad y reglas propias, solo tras requisito. Reasignar referencias requiere estrategia de historia/colisiones aprobada, sin reutilizar update ordinario.
- **Pruebas:** productos homónimos diferentes, ediciones/fabricantes, colisiones de parejas abiertas, operación repetida sin duplicados, interrupción, rollback y preservación de UUID/historial/FKs.
- **Riesgos:** fusión por nombre, pérdida de trazabilidad, eliminación de local con referencias históricas.
- **Aceptación:** nunca borrado/fusión automático; resultado previsualizado y verificable, responsables y procedimiento de reversión. Sin consolidación aprobada, entrega final es coexistencia con origen explícito.
## Registro de riesgos
Probabilidad **no cuantificada**: no hay telemetría ni ensayo global. La prioridad se basa en impacto y en precondiciones observables, no en porcentaje estimado. Cada riesgo incluye escenario y prueba pendiente.
| ID / severidad | Evidencia y escenario | Impacto | Mitigación / prueba |
| --- | --- | --- | --- |
| A01 Alto | authenticate único en catálogo global; operador normal lo edita [catalogRoutes: resource-catalog.routes.ts:65](../../../modules/resources/src/api/infrastructure/http/resource-catalog.routes.ts#L65) | Alteración masiva de definiciones/productos | Guard global R2.2; HTTP normal/admin y authVersion revocado |
| A02 Crítico condicionado | Sesión no aplica ACL por compañía [auth: require-authenticated.ts:6](../../../modules/identity/src/api/infrastructure/http/require-authenticated.ts#L6) [relationshipAuth: relationship-read-access.ts:7](../../../apps/server/src/infrastructure/http/relationship-read-access.ts#L7); despliegue presume aislamiento por operador | Lectura/escritura de otra compañía cambiando path | Acordar modelo compartido o ACL R2.0; pruebas de permisos reales, no solo IDs de referencia |
| A03 Crítico | Ampliar list para todo consumidor; remoteReferences acepta cualquier recurso [remotePolicy: remote-connection-policy.ts:6](../../../modules/companies/src/api/application/remote-connection-policy.ts#L6) | Producto convertido en destino operativo o exposición no prevista | Servicio por propósito; pruebas negativas remoto/certificado/SMB/support y licencia ajena |
| A04 Alto | ResourceSchema/mapper exigen UUID compañía [dto: inventory.ts:35](../../../modules/resources/src/common/inventory.ts#L35) [mapper: resource-persistence.mapper.ts:24](../../../modules/resources/src/api/infrastructure/persistence/resource-persistence.mapper.ts#L24) | Fallo de lectura general con NULL o campos strict nuevos | Contratos coordinados, gates de despliegue y tests cliente legacy/nuevo |
| A05 Alto | FKs compuestas/DDL [directorySQL: directory-domain-migration.ts:5](../../../modules/resources/src/api/infrastructure/persistence/directory-domain-migration.ts#L5) [supportSQL: support-migration.ts:86](../../../modules/assets/src/api/infrastructure/persistence/support-migration.ts#L86) | Migración bloqueada o integridad debilitada | Ensayo DDL paso a paso, reconstrucción de constraints y no NULL en hijos |
| A06 Alto | remove descubre FKs actuales/históricas [remove: remove-resource.ts:37](../../../modules/resources/src/api/infrastructure/persistence/remove-resource.ts#L37) | Borrado global usado causa pérdida o errores operativos | Mantener protección/locks y probar referencia solo histórica y carrera de alta/borrado |
| A07 Alto | Propagación enumera compañías [propagation: prepare-resource-template-updates.ts:15](../../../modules/resources/src/api/infrastructure/persistence/prepare-resource-template-updates.ts#L15) | NULL omitido/fallo al editar plantilla; cambios masivos parciales | Rama global, misma transacción, casos con campos nuevos/retirados |
| A08 Medio | Nombre no identifica producto; destino operativo fijo [softwareMapper: software-installation-input.mapper.ts:15](../../../modules/companies/src/api/application/software-installation-input.mapper.ts#L15) [applicationMapper: application-configuration-input.mapper.ts:11](../../../modules/companies/src/api/application/application-configuration-input.mapper.ts#L11) | Duplicados y consolidación inválida | Convivencia, origen, comando explícito; pruebas de colisiones e idempotencia |
| A09 Medio | readInventory carga valores, especificación y toda historia [read: resource-inventory-relational.ts:195](../../../modules/resources/src/api/infrastructure/persistence/resource-inventory-relational.ts#L195) | Selectores pesados; memoria/latencia a volumen | Proyección paginada mínima, medir volumen/EXPLAIN R2.4 |
| A10 Medio | Lecturas seriales de servicios y filtros por cada recurso/revisión [softwareRead: sequelize-software-installation.repository.ts:27](../../../modules/companies/src/api/infrastructure/persistence/sequelize-software-installation.repository.ts#L27) [applicationRead: sequelize-application-configuration.repository.ts:34](../../../modules/companies/src/api/infrastructure/persistence/sequelize-application-configuration.repository.ts#L34) | Cascadas y CPU repetida; posible N+1 al añadir getById por fila | Resolver lotes, no llamada por fila; prueba de número de consultas y tamaño de payload; N+1 global no medido hoy |
| A11 Alto | Cache keys solo locales e invalidación dirigida actual [inventoryAPI: inventory-api.ts:11](../../../modules/resources/src/web/resources/shared/api/inventory-api.ts#L11) [invalidation: resource-related-query-keys.ts:4](../../../modules/resources/src/web/resources/shared/api/resource-related-query-keys.ts#L4) | Estado/nombre/needsReview obsoletos en otras compañías o navegadores | Claves por propósito, invalidación global dirigida + refetch; prueba dos compañías y sesiones |
| A12 Alto | Lectura de recursos usa otra transacción [softwareRead: sequelize-software-installation.repository.ts:27](../../../modules/companies/src/api/infrastructure/persistence/sequelize-software-installation.repository.ts#L27) [service: resource-inventory.service.ts:7](../../../modules/resources/src/api/application/resource-inventory.service.ts#L7) | Validación active no atómica frente a desactivar | Unidad de consistencia/orden de locks; prueba concurrente desactivar/asociar |
| A13 Medio | Dashboard count y filas con inner join companies [dashboard: resource-dashboard-reader.ts:15](../../../modules/resources/src/api/infrastructure/persistence/resource-dashboard-reader.ts#L15) | Total incluye globales mientras filas los omiten | Métrica explícita local/global; prueba global + locales con paginación |
| A14 Alto | Historial Resource incluye notas y actorName [resourceDetail: resource-detail.tsx:11](../../../modules/resources/src/web/resources/detail/ui/resource-detail.tsx#L11) [dto: inventory.ts:35](../../../modules/resources/src/common/inventory.ts#L35) | Exposición de notas internas u operadores, usos de otros clientes | Proyección de lectura mínima y prueba de ausencia de datos de B |
| A15 Alto | usage y plantilla fijos, estructura propagable [definitionEntity: resource-definition.entity.ts:35](../../../modules/resources/src/api/domain/resource-definition.entity.ts#L35) [update: resource.aggregate.ts:100](../../../modules/resources/src/api/domain/resource.aggregate.ts#L100) | Cambio de clasificación/ámbito invalida datos usados | Mantener inmutabilidad, allowlist y pruebas de plantilla/campos locales en global |
| A16 Medio | Composición por servicios y SQL transversal en asistencia [module: module.ts:39](../../../modules/resources/src/api/module.ts#L39) [supportRead: sequelize-support.repository.ts:21](../../../modules/assets/src/api/infrastructure/persistence/sequelize-support.repository.ts#L21) | Frontera modular rota o globales filtrados accidentalmente en consumidores | Puertos públicos explícitos; pruebas de contratos/registro de relaciones y smoke de módulos |
| A17 Alto | companyId NULL en unique no asegura duplicado global; validación actual de nombres en memoria [save: resource.use-cases.ts:64](../../../modules/resources/src/api/application/resource.use-cases.ts#L64) | Altas globales duplicadas concurrentes o índice demasiado restrictivo | Definir semántica nombre/contexto y lock de catálogo; test concurrente; índice solo tras decisión |
| A18 Medio | Historial operativo guarda resourceId, no nombre de producto [softwareDTO: software-installations.ts:19](../../../modules/companies/src/common/software-installations.ts#L19) [applicationDTO: application-configurations.ts:20](../../../modules/companies/src/common/application-configurations.ts#L20) | Cambio de nombre se ve retroactivamente; fusión confunde historia | Conservar historia de Resource; decisión explícita de snapshots y test de renombrado |
No se corrigen estos puntos en esta fase. Un riesgo condicionado no se presenta como vulnerabilidad explotada ni como resultado de pentest.
## Validación y salida por fase
Reutilizar pruebas existentes de dominio/HTTP/UI y añadir comportamiento nuevo con global/local ajeno, inválidos, límites, inactivos, idempotencia y concurrencia. HTTP/SQL solo contra metis_desk_test desechable, sin semillas ni limpieza de bases reales. Pruebas visuales e integración de listados con 15 compañías y principal con al menos 50 personas/activos/recursos, más perfiles/puestos coherentes; unidades pequeñas como complemento.
Cambios transversales futuros: pnpm typecheck, pnpm lint, pnpm test, pnpm check, pnpm build, además de Playwright y ensayo de migración. Registrar fallos reales sin ocultarlos. En esta auditoría documental solo se ejecutaron las pruebas dirigidas y comprobaciones documentales descritas en [12](12-matriz-trazabilidad.md).
Partes sin esquema: análisis/contratos, guard con privilegio existente aprobado, proyección de candidatos locales, UI de procedencia, resolución por lotes, invalidaciones y coexistencia/avisos. **Ninguna habilita persistir un Resource global válido sobre el esquema actual NOT NULL.**