metis-desk/docs/integracion-visual-de-dominios-windows.md
2026-10-20 13:20:22 +02:00

13 KiB
Raw Blame History

Integración visual de dominios Windows · Paso 2.4

Uso y responsabilidades

Los dominios se consultan en Recursos, dentro de su plantilla existente. La identificación funcional es resource.directory.capability === "directory:domain"; renombrar una plantilla no cambia la clasificación. La ficha conserva nombre, características DNS/NetBIOS, estado, observaciones e historial propios de Resource.

Companies compone CompanyResourceRelatedContent mediante relatedContent, tanto para el panel lateral como para el diálogo existente. El adaptador integra el componente público DomainDirectoryMemberships de Assets y los enlaces de accesos. Los recursos ordinarios mantienen su comportamiento y los buzones conservan MailboxRelations. Resources no depende de Assets ni de implementaciones privadas de Companies.

En la ficha del dominio hay una única tabla paginada, con columnas de activo, plantilla, estado del activo, rol y vigencia de la pertenencia. Se adopta la alternativa A: miembros y controladores comparten el cursor del proveedor; el resumen de controladores indica expresamente que corresponde a la página visible. No hay paginaciones independientes, filtrados que oculten miembros de otras páginas ni contadores globales inferidos de veinte filas. La clasificación usa roleCode.

Cada nombre disponible enlaza al activo por su UUID. Para asociar o administrar una pertenencia desde el dominio se abre el inventario o la ficha del activo. Los comandos y sus diálogos permanecen en Assets; no se duplican en Resources.

La sección Dominio Windows del activo está integrada en AssetDetail, compartido por página, diálogo y panel lateral. En el panel adopta un AccordionItem de la raíz multiple existente. Permite:

  • Ver dominio, DNS, rol y estado, y abrir el recurso mediante su UUID.
  • Asociar un equipo compatible, eligiendo manualmente un dominio activo.
  • Cambiar rol: solo los servidores ofrecen controlador; también se respeta la clasificación técnica explícita de infraestructura del paso 2.2.
  • Trasladar mediante un único comando atómico existente; el diálogo explica y confirma el cierre de la pertenencia anterior al guardar.
  • Finalizar con confirmación, conservando los datos y el historial.
  • Consultar los periodos vigentes y finalizados mediante el proveedor tipado.

La tabla histórica muestra el rol y estado de cada periodo, no un timeline de cada revisión intermedia del rol: el contrato tipado no proporciona fechas ni revisiones. Las revisiones completas siguen conservadas y accesibles por la API especializada del paso 2.2. No se inventan fechas, códigos de inventario ni metadatos ausentes.

HTTP y servicios

Se añaden tres GET acotados bajo el prefijo ya existente:

Ruta Finalidad
/api/asset-directory-memberships/:companyId/relationships Página del proveedor tipado y resúmenes del extremo relacionado por lote
/api/asset-directory-memberships/:companyId/domains Opciones activas de Resources, búsqueda por nombre/DNS y cursor
/api/asset-directory-memberships/:companyId/:assetId/eligibility Clasificación de un activo con la política existente

La primera exige typeId=assets:directory-membership, anchorType=assets:asset o resources:resource, anchorId, direction, includeEnded=true|false y pageSize=1..100, más cursor opcional. La respuesta es {page: {items, nextCursor}, summaries}; conserva roleCode y el orden de correspondencia entre filas y resúmenes. El adaptador llama a runtime.relationships.memberships y al resolutor propietario correspondiente. No reconstruye las consultas del proveedor ni hidrata inventarios completos.

Las opciones se obtienen del servicio público resources.services.directoryOptions: una proyección de Recursos y su tabla de dominios, limitada por compañía y estado, con búsqueda parametrizada y LIMIT 21 para devolver hasta veinte opciones y continuación. El cursor de este selector es una identidad existente tratada como opaca por la UI; no es el cursor HMAC de relaciones. Cambiar la búsqueda reinicia su página. No se descarga el catálogo completo para filtrar dominios en el navegador.

La compatibilidad proyecta únicamente el activo solicitado, su plantilla y la clasificación explícita de servidor de la última revisión de infraestructura. Reutiliza directoryMembershipEligibility; la validación definitiva continúa en el comando, incluyendo dominio activo, estado del activo, concurrencia y compañía.

Las escrituras siguen usando, sin alterar su contrato:

POST /api/asset-directory-memberships/:companyId/:assetId
  join | role | transfer | end

No hay un escritor genérico de relaciones, nueva tabla, migración o dependencia externa en este paso. Los contratos del paquete común y los cursores HMAC del paso 2.3 permanecen intactos.

Sesión, errores y actualización

Las rutas nuevas usan el middleware público de autenticación. Derivan usuario y authVersion de la sesión y obtienen un contexto con la fábrica existente, sin copiar su credencial. La compañía se valida antes de leer datos propietarios; cada servicio conserva la autorización del contexto. No se acepta operatorId en query o body de estas lecturas. Los tres GET se registran de forma que no intercepten los comandos anteriores.

Errores tipados: INVALID_QUERY → 400, FORBIDDEN → 403 y UNAVAILABLE → 404; la ausencia de sesión produce 401 mediante el middleware existente. Los errores inesperados siguen el diagnóstico central del servidor. Las referencias ajenas no revelan nombres. Sigue existiendo acceso general de operadores autenticados a las compañías atendidas, sin ACL granular por compañía.

El cliente valida las respuestas mediante schemas públicos y utiliza csrfFetch para las escrituras. Muestra carga, vacío real, error y datos disponibles de forma diferenciada, conserva errores funcionales y no presenta guardados optimistas. Tras éxito o conflicto invalida exclusivamente las consultas de pertenencias de esa compañía, que incluyen ambos extremos e historial; ante errores también marca como obsoletas sus opciones de dominio para no reutilizar un dominio retirado. El formulario solo se cierra tras confirmar el éxito y actualizar las consultas.

El diálogo captura el UUID de la pertenencia al abrir la operación. Un refresco concurrente no lo sustituye por el de un periodo nuevo: el backend puede rechazar la operación obsoleta. No se realizan finalización y alta separadas para trasladar.

Los listados de relaciones actualizan cada sesenta segundos en primer plano mediante useListQuery. El navegador conserva una pila de cursores opacos para anterior y siguiente. «Primera página» reinicia la consulta inicial y sus datos, recuperándose de cursores invalidados por un reinicio del servidor. No se fingen páginas totales ni se reordena localmente una sola página del servidor como si fuera el conjunto.

Cuentas y grupos

Los enlaces abren el AccessWorkspace existente con:

/companies/:companyId/access?kind=account&environmentId=:environmentId
/companies/:companyId/access?kind=group&environmentId=:environmentId

environmentId procede de la asociación estructurada del dominio. Las rutas validan UUID, parámetros repetidos y ámbito permitido. El listado existente combina el filtro con búsqueda y tipo, indica el entorno y permite quitarlo. Cambiar a una sección incompatible retira el filtro; modificarlo reinicia la paginación. No se copian cuentas/grupos ni se añaden formularios AD, consultas SQL privadas o permisos del directorio como autorizaciones de Metis.

Componentes y archivos

Creados:

  • packages/rdx-ui/src/components/cursor-pagination.tsx: navegación genérica sin dependencia de entidades, DTO o consultas.
  • modules/assets/src/common/directory-membership-read.ts: contratos HTTP acotados.
  • modules/resources/src/common/directory-domain-options.ts: contrato del selector.
  • modules/assets/src/api/infrastructure/persistence/directory-membership-eligibility-reader.ts.
  • modules/resources/src/api/infrastructure/persistence/directory-domain-options-reader.ts.
  • apps/server/src/infrastructure/http/directory-relationship.routes.ts y apps/server/src/windows-domains-ui.integration.test.ts.
  • En modules/assets/src/web/directory-memberships/: API y query keys, hooks de consultas/mutación, secciones de activo y dominio, tabla de página, selector de dominios y diálogos de edición/finalización. Cada componente principal tiene su fichero; las operaciones están separadas en detail, manage, select y shared.
  • modules/companies/src/web/resources/detail/ui/company-resource-related-content.tsx.
  • Predicado y prueba de filtro de entorno, junto al listado de AccessWorkspace.

Modificados principalmente:

  • Composition roots de Assets/Resources y runtime HTTP del servidor: inyección y rutas.
  • AssetDetail, AssetSelectionPanel, ResourceDetailDialog y CompanySection: composición contextual y altura estable del diálogo de recurso.
  • Ruta, controlador y listado de AccessWorkspace, con sus pruebas de navegación.
  • ConfirmDeleteDialog: opción layout="stable" utilizada por la confirmación nueva; los consumidores anteriores conservan su presentación.
  • Exports públicos, traducciones es/en, manifiestos de pruebas, fixture compartido y documentación. No se modifica código vendor ni se revierten los pasos anteriores.

Reutilizados: DetailPanel, DetailSection, Accordion multiple, EmptyState, EntityStatus, Table/Button/Input de shadcn/ui, FormDialog, FormBody, FormHelp, FormActions, FormLabel, RequiredIndicator, ConfirmDeleteDialog, BrowserLink, TanStack Query y las operaciones públicas anteriores. No se usa AssetPickerDialog: la gestión se inicia desde el activo seleccionado y no necesita descargar otro inventario. El selector de dominios tiene búsqueda remota y selección mediante botones accesibles, sin añadir paginación local sobre su página remota.

Verificación y límites

Las suites usan Node y tsx con --test-isolation=none, conservando todos los ficheros de cada script. La integración solo utiliza metis_desk_test, datos con UUID propios y limpieza por compañía, sin tocar bases de desarrollo o producción.

  • Assets: 126/126; Resources: 53/53; RDX UI: 52/52.
  • Companies: 171/173; pasan las nuevas pruebas de ruta y filtrado. Persisten las dos expectativas previas de texto en companies-page.test.tsx:180 y :210.
  • Integración conjunta de pasos 2.1–2.4: 15/15. Incluye consultas directas/inversas, roles, cursor ajeno/inválido, límites, sesión, compañía inexistente, datos ajenos, CSRF, asociación, cambio de rol, traslado, finalización repetida y conflictos. El selector se comprueba con más de veinte dominios, búsqueda y exclusión de dominios inactivos y de otra compañía.
  • Se comprueban renderizado es/en, enlaces UUID, estados, ausencia de filtraciones de nombres no disponibles y transporte de un único comando atómico con CSRF.
  • Typecheck correcto en Assets, Resources, Companies, servidor, web y RDX UI.
  • Biome y ESLint correctos en los 41 ficheros TypeScript del paso; Biome también comprueba tres manifiestos y cuatro ficheros de traducciones.
  • Builds de servidor y web correctos; git diff --check sin incidencias.
  • Grafo workspace verificado: 13 paquetes, sin ciclos.
  • Se ha reproducido TS2375 en el fichero vendor sin modificar packages/shadcn-ui/src/components/calendar.tsx:165. No se corrige aquí.
  • El build web conserva el aviso de chunks mayores de 500 kB.

No se ha realizado una sesión interactiva de navegador: el entorno no dispone de herramienta de navegador ni ejecutable Chromium. Los diálogos usan el foco inicial de búsqueda y la restitución de foco de FormDialog/Base UI; los tests SSR no sustituyen una comprobación manual de teclado, viewport móvil y tema oscuro.

Los cursores HMAC siguen siendo por instancia y sin instantánea entre peticiones. No hay sincronización LDAP, automatización AD ni gestión SMB. La siguiente fase puede reutilizar contratos, resolución por lotes y navegación por cursor, pero las reglas y comandos de SMB deberán seguir en su propietario; no están implementados.

Comandos de verificación

Desde la raíz, usando Node 24 y el PNPM fijado:

pnpm --config.verify-deps-before-run=false --filter @metis/assets --filter @metis/resources --filter @metis/companies --filter @metis/server --filter @metis/web --filter @repo/rdx-ui typecheck
pnpm --config.verify-deps-before-run=false --filter @metis/server test:windows-domains-ui
pnpm --config.verify-deps-before-run=false --filter @metis/server build
pnpm --config.verify-deps-before-run=false --filter @metis/web build
git diff --check

La integración conjunta se ejecutó desde apps/server:

DB_NAME=metis_desk_test NODE_ENV=test node --env-file=../../.env --import tsx \
  --test --test-isolation=none --test-concurrency=1 \
  src/directory-domains.integration.test.ts \
  src/asset-directory-memberships.integration.test.ts \
  src/typed-relationships.integration.test.ts \
  src/windows-domains-ui.integration.test.ts

Las suites completas de Assets, Resources, Companies y RDX UI se ejecutaron con sus listas originales de ficheros más los casos nuevos, añadiendo --test-isolation=none al runner para enumerar los casos reales. No se eliminaron pruebas ni se cambiaron las expectativas previas fallidas. No se afirma que los checks globales del monorepo estén verdes.