metis-desk/docs/recursos-compartidos-smb.md
David fe6ec361b7 feat(companies): vincular identidades locales a activos y añadir cuenta de acceso a FolderMapping
- Asociar entornos locales con activos y conservar su trazabilidad.
- Registrar la cuenta de acceso del mapeo con historial de cambios.
- Calcular cobertura documental específica sin alterar la cobertura general.
- Añadir migraciones, validaciones, endpoints protegidos, pruebas y documentación.
2026-10-21 11:25:23 +02:00

313 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.

# Recursos compartidos SMB — pasos 3.1 y 3.2
## Identidad y alcance
Una carpeta SMB es un Resource existente especializado, con el mismo UUID. Se reutilizan
`template:shared_folder`, `field:unc`, `field:server` y `field:description`. El nombre visible
es independiente de la UNC. La capacidad del recurso es `network:smb-share` y solo se
expone cuando existe una proyección válida; la clasificación de plantilla por stockKey
identifica el tipo esperado, no convierte un recurso antiguo en SMB.
No se incorporan publicadores, relaciones Asset–Resource, cuentas nuevas, resolutores
de relaciones ni modificaciones de WorkProfile o FolderMapping. Los accesos reutilizan
el modelo de permisos, necesidades y concesiones existente.
## Dirección y normalización
`field:unc` es la única entrada editable. La presentación conserva mayúsculas, Unicode
y un separador final introducido por el usuario, después del trim exterior ya utilizado
por Resource. La clave de comparación elimina un único separador final y pliega solo
ASCII A–Z. No aplica NFC/NFKC, eliminación de tildes ni case folding Unicode.
Se admiten hosts NetBIOS, DNS e IPv4 como nombres documentales, comparticiones
administrativas como C$, Unicode y espacios interiores en nombres de carpetas.
No se resuelve DNS ni se identifica el publicador por el host o la IP.
Se rechazan rutas locales, URL, host/compartición vacíos, separadores repetidos,
segmentos `.` y `..`, caracteres de control, Unicode mal formado, nombres de segmento
con espacios exteriores o punto final, caracteres estructuralmente incompatibles,
UNC extendidas de dispositivo y direcciones con más de 255 unidades UTF-16.
IPv6 con dos puntos, URI SMB y hosts terminados en punto quedan fuera de v1.
La comparación conservadora considera equivalentes `\\NAS\Docs` y `\\nas\docs`, pero
no equipara `\\nas\Docs` con `\\files.empresa.local\Docs`, ni formas Unicode distintas.
Las variantes Unicode no ASCII pueden permanecer distintas aunque un servidor concreto
las trate como equivalentes: no inventamos una política de cotejo universal.
### Raíz y subcarpetas
Se permiten rutas profundas por compatibilidad con el modelo UNC existente de Companies.
El contrato distingue `host`, `shareName` y `relativePath`; una ruta relativa vacía indica
la raíz de la compartición. Se recomienda registrar la raíz cuando se inventaría una
compartición y utilizar rutas más profundas en FolderMapping, pero no se invalidan
registros por contener subcarpetas. La unicidad corresponde a la dirección completa;
una raíz y una subcarpeta son direcciones distintas.
## Persistencia e integridad
La migración idempotente `migrateNetworkShares` crea `resource_network_shares`, sin
rellenar registros anteriores. Se ejecuta después de las migraciones de Resources y
Dominios, cuyos índices compuestos de raíz y plantilla reutiliza.
```sql
resourceId CHAR(36) BINARY PRIMARY KEY
companyId CHAR(36) BINARY NOT NULL
templateId CHAR(36) BINARY NOT NULL
templateKey VARCHAR(100) NOT NULL -- fijo: template:shared_folder
protocol VARCHAR(10) NOT NULL -- fijo: smb
normalizedAddress VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
activeAddress VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL
UNIQUE (companyId, activeAddress)
UNIQUE (resourceId, companyId)
FOREIGN KEY (resourceId, companyId, templateId)
REFERENCES resources(id, companyId, templateId)
FOREIGN KEY (templateId, templateKey)
REFERENCES resource_definitions(id, stockKey)
```
CHECK restringe protocolo, plantilla, dirección no vacía y coincidencia binaria entre
clave activa y normalizada cuando no es null. El código mantiene `activeAddress` igual a
la dirección canónica para activos y null para inactivos. MariaDB garantiza la unicidad
de las claves activas, incluso bajo concurrencia; no se depende de un SELECT previo.
El cotejo binario conserva diferencias Unicode y acentos.
La proyección no es otra entidad principal ni otro editor. No duplica dirección de
presentación, host o shareName: se obtienen del campo UNC validado de Resource.
## Transacciones, historial y ciclo de vida
El guardado existente valida nuevas carpetas y carpetas ya especializadas. Dentro de
una transacción escribe Resource, revisiones y proyección SMB. Un conflicto de índice se
traduce a `SMB_ADDRESS_EXISTS` y provoca rollback de todas esas escrituras, también de
la proyección access si existía. Una UNC inválida produce `INVALID_SMB_ADDRESS`.
Cambiar UNC o nombre conserva UUID y revisiones. Un guardado idéntico no añade revisión.
Modificar solo notas/nombre/presentación no actualiza innecesariamente la proyección si
la dirección canónica y el estado no cambian.
Desactivar conserva Resource, proyección e historial y libera la clave activa.
Reactivar vuelve a adquirirla; un conflicto mantiene el recurso inactivo sin una revisión
parcial. Las direcciones inactivas se pueden reutilizar.
Eliminar utiliza las protecciones anteriores y comprueba también referencias a la
proyección SMB. Si existen referencias actuales o históricas externas, devuelve
`RESOURCE_IN_USE`. Sin referencias, elimina los datos propios, incluida la proyección,
en la transacción existente. No conserva un historial separado tras un borrado físico
explícito: se mantiene la semántica anterior de Resource.
## Catálogo
Renombrar plantilla/campos, cambiar etiquetas, iconos, secciones u orden no elimina la
capacidad. `template:shared_folder` debe conservar el campo de serie `field:unc`; quitarlo
produce `SMB_FIELD_REQUIRED`. El mecanismo anterior ya impide cambiar el tipo del campo.
No se modifica automáticamente la obligatoriedad del catálogo: el validador especializado
exige UNC para altas SMB aunque la característica siga figurando como opcional.
Deshabilitar disponibilidad por compañía impide altas nuevas, sin desespecializar los
registros existentes. No existe una operación ordinaria de borrado físico de plantillas;
las FK protegen la plantilla de los recursos especializados. La sincronización de
especificaciones conserva las características y añade revisiones mediante el mecanismo
existente; no altera la dirección ni la proyección por cambios de presentación.
## Registros anteriores y diagnóstico
Los registros previos **no se convierten automáticamente**, incluso si su UNC es válida,
al reiniciar, editar, activar/desactivar o sincronizar la plantilla. La distinción se
basa en la presencia de la proyección persistida, no en fechas aproximadas.
`GET /api/resources/:companyId/network-share-diagnostics`, con la autenticación existente,
no acepta parámetros adicionales y devuelve solo identificadores y clasificación:
- `classification`: `shared-folder` cierta por stockKey, o `legacy-candidate` heurística.
- `addressStatus`: `valid`, `invalid` o `missing`.
- `duplicate`: dirección canónica compartida por candidatos activos de esa compañía.
- `managed`: ya tiene proyección SMB.
- `requiresConversion`: todavía no tiene esa proyección.
La heurística para `template:access` busca una referencia o característica con prefijo
UNC. No usa nombres visibles, no declara capacidad y puede necesitar revisión humana.
No descubre carpetas escondidas en cualquier texto libre ni interpreta nombres de tipos
de acceso. El diagnóstico informa duplicados también entre candidatos, pero la restricción
SQL solo se aplica a recursos especializados; resolver legados queda fuera de 3.1 y 3.2.
El diagnóstico usa el inventario autorizado ya existente, no escribe ni incluye notas,
credenciales o características completas. No modifica plantilla, integración ni historial,
y no fusiona, elimina o sustituye recursos.
## Contratos públicos y siguientes fases
ResourceDTO incluye `networkShare` opcional con capacidad, protocolo, dirección principal,
clave canónica, host, nombre compartido y ruta relativa. Es solo respuesta, no entrada.
`@metis/resources/services` exporta `INetworkShareReader` y `NetworkShareReference`.
`resources.services.networkShares(transaction).find(companyId, resourceId)` devuelve
Maybe de la referencia mínima con UUID, compañía y estado; comprueba la especialización
y la dirección persistida y bloquea la lectura para los futuros comandos de publicación.
El consumidor debe autenticar y autorizar el comando antes de usar el lector interno.
No expone repositorios ni introduce dependencias de Resources hacia Assets o Companies.
La integración access sigue teniendo contexto fijo. Una carpeta histórica sin integración
no puede incorporarla mediante edición ordinaria. Las nuevas carpetas reciben integración
automática según el paso 3.2 descrito más abajo; no se incorpora conversión histórica ni
se crean cuentas o una segunda ficha editable.
`field:server` sigue siendo documental y no se resuelve contra Assets.
Una modificación de UNC en un recurso integrado actualiza su proyección access mediante
el flujo existente. No reescribe UNC, letra ni historial de FolderMapping; una ruta vieja
sigue siendo detectable por las reglas de disponibilidad anteriores. `permissionsCovered`
no cambia y continúa siendo cobertura documental, no comprobación real de permisos.
## Validación
- Resources: normalizador, equivalencias/no equivalencias, catálogo y diagnóstico.
- MariaDB: alta, edición, rollback, estados, concurrencia, FK, unicidad, lector, borrado,
legado, access, dominios, recursos ordinarios, FolderMapping y contrato HTTP autenticado.
- Integración dedicada: `pnpm --filter @metis/server test:network-shares`.
- La prueba exige NODE_ENV=test y DB_NAME=metis_desk_test antes de conectar; limpia solo
UUID de fixture y tablas auxiliares de referencias creadas por esa ejecución.
Las incidencias previas de dos textos de Companies y TS2375 en el calendario vendor
permanecen fuera del alcance. Los resultados de validación de esta entrega se informan
al finalizar, sin atribuir a esta fase pruebas que no se hayan ejecutado.
### Resultados del paso 3.1
- Suite completa de Resources: 78/78.
- Integración combinada Dominios 2.1–2.4 y SMB 3.1: 27/27 casos nativos, solo
metis_desk_test. Incluye 11 subcasos SMB y su contenedor.
- Typecheck de Resources, Assets, Companies, servidor y web: correcto.
- Biome y ESLint de los archivos afectados: correctos; build del servidor y
`git diff --check`: correctos.
- Suite completa Companies: 171/173; se reproducen únicamente los dos fallos previos
de texto en companies-page.test.tsx. No se modifican esas pruebas.
- Typecheck directo de vendor: reproduce TS2375 en calendar.tsx:165. No se modifica
packages/shadcn-ui ni se oculta el error.
Comandos utilizados (PNPM requiere desactivar su verificación automática de dependencias
antes de ejecutar en este entorno):
```bash
pnpm --config.verify-deps-before-run=false --filter @metis/resources --filter @metis/assets --filter @metis/companies --filter @metis/server --filter @metis/web typecheck
pnpm --config.verify-deps-before-run=false --filter @metis/server build
pnpm --config.verify-deps-before-run=false --filter @metis/server test:network-shares
```
Las suites completas de Resources y Companies se ejecutaron con todos los archivos de
sus scripts existentes, añadiendo `--test-isolation=none` para obtener los casos nativos.
La integración combinada se ejecutó con ese mismo flag y `--test-concurrency=1` sobre
los cinco archivos directory-domains, asset-directory-memberships, typed-relationships,
windows-domains-ui y network-shares. Biome y ESLint se limitaron a los archivos afectados.
## Paso 3.2 — integración automática con AccessWorkspace
Esta sección actualiza el comportamiento de las **nuevas altas** descrito para 3.1.
El paso 3.2 no incorpora conversión, adopción ni migración de recursos anteriores.
Cualquier adopción histórica requerirá un encargo independiente.
### Alta y contexto
El servidor establece `integration.kind=access`, referencia `field:unc` y fija
`systemId=null` y `environmentId=null`. No deduce autenticación del host UNC, dominio
ni publicador. Un cliente que suministra buzón, otro campo/tipo o un contexto incompatible
recibe `SMB_ACCESS_INCOMPATIBLE`; omitir integración activa la configuración automática.
El tipo configurable de AccessWorkspace se obtiene por el UUID ya utilizado por otra
carpeta de esa plantilla en la compañía. Así, renombrar el tipo no cambia las referencias.
Para la primera carpeta, Companies reutiliza un tipo visible llamado «Carpeta compartida
SMB» o crea ese tipo local usando el agregado y su auditoría habituales. Esta etiqueta
solo selecciona un valor por defecto de catálogo: la capacidad SMB sigue dependiendo de
stockKey y de la especialización, nunca del nombre. No se crean permisos, cuentas ni grupos.
Un tipo existente inactivo no se reactiva silenciosamente.
No se añade una migración de esquema ni una clasificación paralela. El tipo se conserva
por sus referencias UUID existentes; después de eliminar todas las carpetas, una primera
alta vuelve a aplicar la selección de catálogo descrita.
### Puerto transaccional y ownership
`IResourceAccessWriter`, exportado por `@metis/resources/services`, dispone de:
- `ensureSmbType`: prepara el tipo de acceso para una nueva carpeta dentro de la transacción.
- `project`: recibe valores e identidades de Resource y sus revisiones, sin exponer su
repositorio ni acceder al agregado de Companies desde Resources.
Companies implementa el puerto con `ResourceAccessWriter`, reutiliza `writeAccessRow` y
conserva el UUID de Resource y los UUID, actor, fecha y orden de sus revisiones. La
composición inyecta `companies.resourceAccess(transaction)` en Resources siguiendo
el patrón de DirectoryContextWriter. Resources no importa Companies: no aparece un ciclo.
La escritura de proyecciones access se ha trasladado de resource-compatibility a su módulo
propietario. Se conservan las consultas de referencias y las protecciones de eliminación
existentes, sin refactorizar globalmente AccessWorkspace.
### Flujo atómico
1. Bloquear catálogo de Resources, catálogo de accesos y compañía en el orden compartido.
2. Solo para una nueva carpeta, preparar el tipo configurable dentro de la transacción.
3. Validar datos y establecer integración en el servidor.
4. Escribir Resource y sus revisiones.
5. Escribir la proyección SMB y comprobar unicidad.
6. Proyectar valores y revisiones en AccessWorkspace.
7. Confirmar; cualquier fallo revierte también el tipo creado y ambas proyecciones.
`ACCESS_PROJECTION_UNAVAILABLE` indica que no se puede preparar el acceso; no se guarda
una nueva carpeta a medias si falta el participante de Companies. Las composiciones
que escriban recursos con integración access deben inyectar ese participante.
Renombrar, editar características/UNC y activar/desactivar reutiliza la misma proyección.
No se cierran necesidades, concesiones ni configuraciones al desactivar. Las referencias
actuales o históricas de acceso siguen impidiendo el borrado físico de Resource.
### Interfaz y utilización
El formulario existente muestra ayuda traducida sobre acceso automático, configurado o
histórico. No ofrece selectores incompatibles de sistema, entorno o tipo para SMB.
El navegador sigue editando únicamente Resource; los endpoints antiguos de escritura
AccessItem(resource) continúan rechazados por `RESOURCE_MOVED`.
Cuentas y accesos permite crear permisos configurables, necesidades y concesiones para
la carpeta, con las reglas existentes. Una misma carpeta puede recibir una concesión
a un grupo AD y otra a una cuenta local NAS. La cobertura es documental: no autentica,
comprueba ACL ni demuestra permisos efectivos SMB/NTFS.
WorkProfile y FolderMapping conservan su implementación, referencias, UNC registrada,
letra de unidad, historial y cálculo de cobertura. El cambio de dirección del recurso
no reescribe mapeos anteriores: sigue siendo detectable como desactualización.
### Históricos y límites
El alta automática se decide por una operación nueva, no por la presencia de una UNC ni
por una fecha. Leer, reiniciar, editar o activar una carpeta antigua sin integración
no añade AccessItem ni cambia su contexto. También se conservan carpetas especializadas
en 3.1 sin integración, y recursos anteriores bajo template:access.
No hay publicadores (3.3), identidades locales vinculadas a activos ni cambios de
FolderMapping (3.4), pantallas de administración de carpetas (3.5), LDAP, credenciales,
grupos anidados ni permisos efectivos. No se amplía el diagnóstico histórico.
Prueba dedicada: `pnpm --filter @metis/server test:smb-access`, exclusivamente contra
metis_desk_test. Comprueba integración, rechazos, sincronización, tipo renombrado, concesiones
AD/NAS, cobertura, borrado, rollback tras escribir accesos y ausencia de adopción histórica.
### Verificación del paso 3.2
- Resources completo: 80/80, incluidos contrato automático, rechazo de contexto y ayuda es/en.
- MariaDB: 35/35 en la ejecución combinada de Dominios 2.1–2.4, SMB 3.1 y SMB Access 3.2.
Solo metis_desk_test; incluye concesiones directas y por grupo, cobertura, rollback real
después de escribir proyecciones, contexto renombrado y registros anteriores sin adopción.
- Typechecks de Resources, Assets, Companies, servidor y web: correctos.
- Biome, ESLint de los archivos afectados, build del servidor y git diff --check: correctos.
- Grafo de los 13 paquetes del workspace: sin ciclos.
- Companies completo: 171/173; permanecen las dos expectativas previas de texto en
companies-page.test.tsx. Las pruebas de accesos incluidas en esa suite pasan.
- Se reproduce TS2375 en packages/shadcn-ui/src/components/calendar.tsx:165; vendor intacto.
La integración combinada usa NODE_ENV=test, DB_NAME=metis_desk_test,
--test-isolation=none y --test-concurrency=1. Las comprobaciones completas de paquetes
mantienen todos los archivos de sus scripts y añaden --test-isolation=none para obtener
los casos del runner nativo. Este paso no añade migraciones, conversiones históricas ni
pruebas de interacción en navegador; el cambio mínimo del formulario se verifica mediante SSR.
## Publicador documental (paso 3.3)
La relación especializada, sus comandos e historial, las restricciones SQL y las consultas tipadas se describen en [publicación de recursos compartidos SMB](publicacion-de-recursos-compartidos-smb.md). Assets es su propietario y consume el lector SMB público de Resources; esta relación no modifica la integración de accesos descrita en este documento.