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

230 lines
15 KiB
Markdown

# Dominios Windows · Modelo e identidad (paso 2.1)
Un dominio Windows se registra como `Resource`. Conserva el UUID, nombre, compañía,
estado, notas, características e historial nativos. No existe una nueva raíz
`WindowsDomain`, ni depende de un activo, servidor o controlador.
## Catálogo y capacidad
La semilla añade la plantilla **Dominio Windows** al final del catálogo, sin cambiar
la selección inicial ni el orden de las plantillas anteriores. Contiene Nombre DNS
(texto obligatorio) y Nombre NetBIOS (texto opcional). No duplica el nombre o estado
del recurso. La inicialización es idempotente y no repone los nombres personalizados.
La clave persistida e inmutable `stockKey = template:windows_domain` se traduce
explícitamente a `directory:domain` mediante `resourceTemplateCapability`. Se
reutiliza la clasificación ya existente: `stockKey` no forma parte del contrato
editable del catálogo. No se deduce por nombre visible, idioma, UUID o presencia de
campos. Las claves de campo `field:domain_dns` y `field:domain_netbios` identifican
los campos semánticos cuyos UUID se generan mediante `UniqueID`, como los demás.
No hay un motor universal ni un catálogo de capacidades independiente. La función
podrá ampliarse con otra correspondencia explícita cuando exista un consumidor de
`network:smb-share`. Una plantilla creada por el usuario con los mismos nombres
no adquiere la capacidad. Renombrar o reordenar la plantilla conserva su semántica.
Se impide retirar DNS/NetBIOS y hacer opcional DNS; el tipo texto de los campos se
protege con las reglas existentes de edición del catálogo.
## Identidad y validación
`DirectoryDomainValues` valida los datos documentales, sin consultar DNS o AD:
- Elimina espacios exteriores y un único punto final DNS. Conserva las mayúsculas
del nombre introducido y obtiene `normalizedDns` en minúsculas. `Empresa.Local.`
y `empresa.local` comparten clave de negocio.
- Rechaza vacío, espacios interiores, protocolos, direcciones numéricas, etiquetas
vacías, guiones en extremos y caracteres fuera de letras ASCII, números y guiones.
- Admite etiquetas de hasta 63 caracteres y un nombre AD de 2 a 64 caracteres.
Se aceptan etiquetas únicas heredadas; registrarlas no es una recomendación para
desplegar nuevos dominios de etiqueta única.
- Política IDN v1: se introduce el A-label ASCII (punycode), por ejemplo
`xn--espaa-rta.local`. No se convierte silenciosamente Unicode ni se utilizan
bibliotecas nuevas o resoluciones de red.
- NetBIOS es opcional, se recorta y normaliza a mayúsculas, con máximo 15 caracteres,
sin caracteres de control, separadores reservados ni nombres exclusivamente
numéricos. No se impone que coincida con la primera etiqueta DNS.
La política ASCII y los límites de AD se apoyan en las
[convenciones de nombres de Microsoft](https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/naming-conventions-for-computer-domain-site-ou).
La elección de A-label explícito y el registro de etiquetas únicas heredadas son
políticas documentales de Metis; no certifican que exista o sea desplegable el dominio.
El mapper de entrada normaliza las características antes de crear o actualizar
`ResourceValues`. El repositorio vuelve a obtener la identidad semántica antes de
proyectarla. Actualizar el DNS cambia características y proyección en la misma
transacción y conserva el UUID. También los cambios de estado pasan por esa
comprobación. La propagación de plantillas conserva los campos semánticos y sus
valores; no puede retirar el DNS obligatorio de la plantilla de dominio.
## Contexto de accesos y responsabilidad modular
Se elige **creación automática de un contexto dedicado**. Al crear el primer dominio
de una compañía, Companies crea un `AccessItem(system)` clasificado como AD. Cada
dominio obtiene un `AccessItem(environment)` propio dentro de ese sistema. Un
segundo dominio reutiliza el sistema clasificado y obtiene otro entorno. Los nombres
visibles no clasifican sistemas: un sistema anterior llamado «Active Directory» no
se adopta automáticamente. Si su nombre impide crear el sistema dedicado, se devuelve
un conflicto de contexto y se revierte el alta; no se reasignan cuentas existentes.
La vinculación o adopción explícita de entornos anteriores queda fuera de este paso.
`Resources` es propietario de la identidad DNS y del vínculo con el entorno.
`Companies` conserva sistemas, entornos, cuentas, grupos y pertenencias. Se reutilizan
sus agregados, políticas, validaciones, persistencia e historial; no se crean cuentas
ni grupos en Resources. Identity sigue reservado a usuarios de autenticación Metis.
El contrato público nuevo es `IDirectoryContextWriter`, exportado por
`@metis/resources/services`. No contiene ORM, HTTP ni operaciones genéricas. Companies,
que ya depende de Resources, lo implementa. Su composition root devuelve
`directoryContexts(transaction): IDirectoryContextWriter`. El servidor inyecta esa
factoría en `createResourceModule`. La transacción solo aparece en composición e
Infrastructure; Application consume el puerto ya vinculado a ella. No hay import
`Resources → Companies`, dependencias nuevas, imports privados entre módulos ni
cambios en `@metis/relationship-contracts`.
`SequelizeResourceRepository.save` coordina un único commit. Resource, su historial,
el sistema/entorno, sus revisiones, referencias y la asociación se guardan con la
misma conexión y transacción Sequelize. Un Result de fallo posterior a escribir se
convierte en excepción interna para provocar rollback y se traduce de nuevo a
Result fuera del callback gestionado. No se coordinan peticiones HTTP separadas.
Orden de bloqueos al guardar con el participante configurado: catálogo de recursos,
catálogo global de accesos, compañía y workspace. Mantiene el orden usado por los
repositorios de catálogo y accesos, y serializa altas concurrentes. Las restricciones
SQL siguen protegiendo frente a escrituras ajenas a esa coordinación.
## Persistencia relacional
Las migraciones explícitas siguen el mecanismo de arranque existente, con tablas,
índices y restricciones completables al repetirse. Solo se han ejecutado contra
`metis_desk_test` durante este trabajo, nunca contra datos de desarrollo o producción.
| Tabla | Responsabilidad y restricciones |
|---|---|
| `access_directory_systems` | Clasifica un sistema AD por workspace de compañía. PK `systemId`, unicidad de workspace, FK a `(id, workspaceId, kind)` de `access_items`, CHECK `kind=system` y FK de compañía al workspace. |
| `access_directory_environments` | Clasifica los entornos de ese sistema. PK `environmentId`, FK al AccessItem de tipo environment, FK al sistema clasificado del mismo workspace y compañía, clave compuesta para referencias desde Resources. |
| `resource_directory_domains` | Proyección mínima: PK/FK `resourceId`, compañía, plantilla, DNS normalizado, entorno y actor/fecha de asociación. No copia nombre, estado, notas, NetBIOS ni historial del recurso. |
En la proyección de dominios:
- UNIQUE `(companyId, normalizedDns)` incluye dominios desactivados. Un mismo DNS
se admite en compañías distintas.
- UNIQUE `environmentId` impide asociar dos dominios al mismo entorno.
- FK compuesta a `(id, companyId, templateId)` de Resources garantiza compañía y
plantilla; otra a `(id, stockKey)` del catálogo y un CHECK restringen la semántica
a la plantilla de dominio, sin codificar su UUID.
- FK `(environmentId, companyId)` exige un entorno AD clasificado de esa compañía.
- FK al actor y fecha inmutables trazan la creación de la asociación. El historial
Resource audita nombres, DNS, NetBIOS y estado; AccessRevision audita el contexto.
- DNS se almacena en columna ASCII con comparación binaria, CHECK de no vacío y
representación canónica sin mayúsculas, espacios exteriores ni punto final.
- Los índices compuestos auxiliares de las tablas raíz permiten las FK de tipo,
compañía y plantilla. No son identidades de negocio adicionales.
Las identidades auxiliares son las de Resource y AccessItem, no UUID sustitutos de
esas entidades. No se usa JSON. Ninguna FK nueva apunta a
`access_environment_values` o `access_system_values`: esas tablas se borran y
reinsertan al editar. El sistema de un entorno ya es inmutable por el dominio
AccessItem; se protege además la desactivación lateral de contextos clasificados.
Las escrituras válidas siguen pasando por los agregados de Companies.
## Operaciones e interfaz
Se mantienen los endpoints existentes de Resources:
- `POST /api/resources/:companyId`: alta mediante la plantilla de dominio.
- `POST /api/resources/:companyId/:id/update`: nombre, notas y características,
incluidos DNS y NetBIOS.
- `GET /api/resources/:companyId`: consulta con metadatos opcionales `directory`
únicamente cuando existe una asociación real confirmada.
- `GET /api/resources/:companyId?environmentId=<uuid>`: consulta inversa, filtrada
por compañía. Valida query estricta y no crea un endpoint transversal nuevo.
- `POST /api/resources/:companyId/:id/status`: activa o desactiva el Resource sin
desactivar el entorno ni sus cuentas/grupos.
`directory` contiene capacidad, DNS normalizado y UUID de sistema y entorno; los
nombres editables DNS/NetBIOS siguen en las características. No se aceptan estos
metadatos de salida como privilegios o asociación de entrada. Se conserva la
protección de autenticación/sesión del router y el ámbito de compañía existente.
No se añade una ACL nueva ni se confunden grupos externos con permisos Metis.
El formulario actual muestra DNS/NetBIOS y ayuda contextual inferior es/en. Antes
de guardar indica que el contexto **se creará**, sin simular que ya existe. En
edición presenta los nombres del sistema y entorno realmente asociados. Se reutilizan
selector, campos, estados, historial y diálogo existentes; no hay pantallas nuevas.
El contexto es fijo: no hay sustitución, traslado ni desvinculación en este paso.
Cambiar DNS tampoco renombra silenciosamente el entorno existente: el vínculo sigue
siendo su UUID, aunque su etiqueta documental conserve el nombre inicial o sea
editada desde accesos. Las cuentas y grupos mantienen sus referencias.
El borrado definitivo continúa usando la protección por referencias de Resources:
la asociación estructural impide eliminar un dominio vinculado, incluso sin cuentas.
La operación soportada para retirarlo es desactivarlo; se reserva el DNS y se
conserva trazabilidad. Liberar o eliminar contextos requerirá una política explícita
posterior, sin borrar cuentas/grupos silenciosamente.
## Pruebas y alcance restante
Las pruebas nuevas cubren clasificación, renombrado, plantilla protegida,
normalización y límites DNS/NetBIOS, contexto dedicado, referencias incompatibles,
compañía, conservación de UUID/historial e idempotencia de semillas. El formulario
se verifica en es/en para estados pendiente y confirmado.
La integración dedicada `pnpm --filter @metis/server test:directory-domains` exige
`NODE_ENV=test` y `DB_NAME=metis_desk_test`. Usa UUID de fixture, no limpia datos
ajenos y cierra conexiones. Comprueba repetición de migraciones, dos altas concurrentes,
unicidad y FK reales, compañía, lectura directa/inversa por HTTP, edición legítima de
valores de entorno, cuentas/grupos conservados, archivo, borrado protegido y rollback
tras crear contexto. El script usa el runner nativo con tsx y sin aislamiento por
subprocesos para ejecutar y enumerar los casos en este entorno.
No se implementan LDAP, descubrimiento, sincronización, controladores, equipos unidos,
relaciones con activos, SMB, FolderMapping, paneles de cuentas/grupos ni navegación
mediante Typed Relationships. No se modifica Asset, Workstation o WorkProfile.
## Verificación de esta entrega
- Typecheck de `@metis/resources`, `@metis/companies` y `@metis/server`: correcto.
- Biome check y ESLint de los 38 ficheros de código/configuración afectados: correctos.
- Suite Resources, ejecutada con el script del paquete y `--test-isolation=none`:
**53/53** pruebas aprobadas.
- Suite Companies con el mismo modo del runner: **169/171** aprobadas. Fallan dos
expectativas de textos en `companies-page.test.tsx` (líneas 180 y 210), sobre
«Personas (1)» y «Esta persona no tiene acceso a Metis Desk». Sus pruebas y
componentes de pantalla no se han modificado. No se han eliminado ni omitido
para presentar un resultado global satisfactorio.
- Las cuatro pruebas nuevas de `directory-context-plan.test.ts` pasan también
de forma dirigida.
- `test:directory-domains`: integración real en `metis_desk_test` aprobada,
incluidos rollback y concurrencia; datos propios eliminados al finalizar.
- Las 11 pruebas de `@metis/relationship-contracts` siguen aprobadas, sin cambios
en el paquete durante el paso 2.1.
- Grafo de los 13 paquetes del workspace sin ciclos; se conserva la dirección
Companies → Resources.
- Build de `@metis/server` y `git diff --check`: correctos.
No se ejecuta la suite global de persistencia ni se aplica ninguna migración a
bases de desarrollo o producción. En este entorno PNPM requiere
`--config.verify-deps-before-run=false` para ejecutar los comandos con las
dependencias ya instaladas; no se añade esa opción a la configuración del proyecto.
## Archivos principales
| Ámbito | Archivos |
|---|---|
| Semántica y transporte | `modules/resources/src/common/resource-capabilities.ts`, `directory-domains.ts`, `inventory.ts`, `index.ts`, `resource-template-icons.ts` y traducciones `locales/es.json`, `locales/en.json`. |
| Dominio y catálogo | `modules/resources/src/api/domain/value-objects/directory-domain-values.vo.ts`, `resource-catalog-seed.ts`, `resource-catalog.aggregate.ts` y pruebas de dominio/catálogo. |
| Aplicación Resources | `modules/resources/src/api/application/directory-context-writer.ts`, `directory-domain-input.ts`, `resource-input.mapper.ts`, `resource.repository.ts`, `resource-response.builder.ts`. |
| Persistencia y API Resources | `modules/resources/src/api/infrastructure/persistence/directory-domain-migration.ts`, `directory-domain-projection.ts`, `sequelize-resource.repository.ts`; `infrastructure/http/resource.routes.ts`, `api/module.ts` y `api/services.ts`. |
| Contexto Companies | `modules/companies/src/api/application/directory-context-plan.ts` y su prueba; `infrastructure/persistence/directory-context-migration.ts`, `directory-context-writer.ts`, `access-persistence.ts` y `api/module.ts`. |
| Formulario existente | `modules/resources/src/web/resources/shared/controllers/use-resource-form-controller.ts`, `ui/resource-form.tsx`, `ui/resource-fields.tsx` y `resources.test.tsx`. |
| Composición y ejecución | `apps/server/src/infrastructure/http/identity-runtime.ts`, `apps/server/src/directory-domains.integration.test.ts` y scripts en los `package.json` de servidor, Resources y Companies. |
## Integración visual · Paso 2.4
La ficha de recurso compone ahora las pertenencias y la navegación contextual a
cuentas/grupos. La ficha de activo permite consultar, asociar, cambiar rol, trasladar
y finalizar pertenencias con los comandos existentes. Véase
[integración visual de dominios Windows](integracion-visual-de-dominios-windows.md)
para rutas, paginación, componentes, pruebas y límites.