metis-desk/docs/auditorias/resources-global-scope/10-plan-implementacion.md

108 lines
18 KiB
Markdown
Raw Normal View History

2026-10-24 23:19:17 +00:00
# 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.**