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

18 KiB
Raw Blame History

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.

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.

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):

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