230 lines
15 KiB
Markdown
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.
|