Apariencia
0051. Gestión cross-tenant de usuarios y roles de tenant desde plataforma
Estado
Aceptada
Contexto
La gestión de usuarios y roles de cada organización (invitar, cambiar rol, activar/ desactivar; crear/renombrar/borrar roles y editar su matriz de permisos) vivía solo en camaroneras_admin, operada por el propio administrador de esa organización (plataforma/rbac-flow.md). El superadmin de plataforma no tenía forma de intervenir ahí sin pedirle credenciales al tenant — un problema real de soporte (ej. una organización se queda sin administrador activo, o necesita ayuda para dar de alta a su primer operador).
La tarea platform-tenant-user-role-admin centraliza esa gestión también en camaroneras_platform, para que el superadmin la opere cross-tenant sin salir de su panel. Se depreca la UI equivalente de camaroneras_admin — decisión explícita del usuario, no una consecuencia técnica de esta tarea.
Decisión
Nuevos endpoints en platform/organizations/:orgId/{users,roles}, guardados por el mismo stack que el resto de /platform/* (PlatformJwtGuard + PlatformPasswordChangeGuard
PlatformPermissionGuard), que actúan como passthrough puro sobre los servicios tenant existentes (UsersService/RolesService— los mismos que usaPlatformControllerde tenant): ninguna regla de negocio se reimplementó.organizationIdse toma siempre del param:orgIdde la URL, nunca del token — el actor es el superadmin, no un miembro de la organización, así que no hayorganizationIden su JWT de plataforma (scopeplatform, ver ADR-0038).
(a) Módulos de permiso nuevos y granulares, no reuso de organizaciones
Se agregaron usuarios-tenant/roles-tenant al catálogo de plataforma (platform-rbac.constants.ts) en vez de gatear esta gestión con el permiso organizaciones ya existente (que cubre supervisión/activación de organizaciones). Un superadmin que puede ver la lista de organizaciones no necesariamente debería poder tocar los usuarios o los roles de una — son superficies de impacto distintas (una es "¿existe y está activa esta organización?", la otra es "¿quién entra a esta organización y con qué permisos?"). Separar el permiso hace la matriz más auditable: un rol de plataforma "Soporte" puede tener organizaciones:ver sin usuarios-tenant:editar, por ejemplo. Mismo patrón que ya usa el resto del catálogo de plataforma (módulos finos en vez de uno amplio).
(b) Passthrough sobre los servicios tenant existentes, organizationId desde el param
Se descartó reimplementar la lógica de usuarios/roles del lado plataforma. UsersService y RolesService ya reciben organizationId como parámetro explícito (nunca lo leían del JWT directamente) — la única adaptación necesaria fue exportarlos desde sus módulos (UsersModule/RolesModule) e importarlos en PlatformModule. Esto evita duplicar invariantes sensibles (último administrador activo, unicidad de nombre de rol, rol por defecto intocable) en dos lugares que podrían divergir con el tiempo.
Validación agregada, no heredada: ningún servicio tenant valida hoy que la organización exista — confían en que organizationId viene de un JWT ya autenticado contra esa org (nunca es arbitrario en la superficie de tenant). Acá el :orgId es un param de URL que el superadmin puede escribir a mano, así que un id inventado disparía un error de FK crudo de Prisma en vez de un 404 limpio. Se agregó PlatformTenantAdminService.assertOrganizationExists(), llamado una vez al inicio de cada handler, antes de delegar — no se tocó el servicio tenant.
Catálogo tenant expuesto bajo guard de plataforma: la matriz de permisos de un rol de tenant necesita el catálogo de tenant (tablas Module/Action), distinto del catálogo de plataforma (PLATFORM_MODULES, solo-código). Se agregó GET /platform/tenant-catalog/modules (permiso roles-tenant:ver) como un simple wrapper de PermissionsService.getCatalog() bajo el guard de plataforma, sin crear un catálogo paralelo.
(c) Se conservan los endpoints tenant; solo se depreca la UI de admin
UsersController/RolesController (/users, /roles, tenant-scoped, protegidos por AccessJwtGuard + PermissionGuard) siguen existiendo sin cambios — son los que consume PlatformTenant{Users,Roles}Controller por debajo, y quedan disponibles para cualquier otro consumidor futuro. Lo que se depreca es únicamente la superficie de admin (camaroneras_admin/src/features/{users,roles}, sus rutas y su navegación): decisión de producto del usuario (ahora esta gestión se opera desde camaroneras_platform), no una consecuencia obligada por el diseño del backend — no correspondía inventarle un motivo técnico que no hubo.
Consecuencias
platform-rbac.constants.ts: dos módulos nuevos (usuarios-tenant,roles-tenant), backfill automático a los roles de sistema de plataforma (Owner: todas las acciones;Operator: solover) vía elonModuleInitexistente dePlatformPermissionsService— mismo mecanismo que los módulos previos, sin código nuevo.UsersModule/RolesModuleahora exportan sus servicios (exports: [UsersService]/exports: [RolesService]);PlatformModulelos importa.- Controllers nuevos:
PlatformTenantUsersController,PlatformTenantRolesController,PlatformTenantCatalogController, másPlatformTenantAdminService(la validación de organización compartida). 13 endpoints en total — verplataforma/superadmin-flow.md→ "Gestión cross-tenant de usuarios y roles de una organización". camaroneras_admin: se eliminaronfeatures/users,features/roles, sus rutas y sus ítems de navegación.plataforma/rbac-flow.mddocumenta ese flujo de cliente como histórico — pendiente de una limpieza de docs que refleje que el admin ya no lo expone.- Riesgo aceptado y cubierto con tests e2e dedicados (
test/platform-tenant-admin.e2e-spec.ts): fuga cross-tenant si:orgIdno acotara correctamente. El test verifica que un:orgIdno puede leer ni mutar datos de otra organización, y que un:orgIdinexistente devuelve 404 antes de tocar el servicio tenant. - Precondición ya cumplida: RBAC de plataforma (ADR-0048) y onboarding de tenants desde plataforma ya en
main.
Referencias
- Tarea
platform-tenant-user-role-admin(2026-07-22). - ADR-0038 — aislamiento de superficies del que esta tarea es una extensión (gestión, no solo supervisión).
- ADR-0048 — RBAC de plataforma sobre el que se agregan los módulos
usuarios-tenant/roles-tenant. camaroneras_backend/src/modules/platform/platform-tenant-users.controller.ts,platform-tenant-roles.controller.ts,platform-tenant-catalog.controller.ts,platform-tenant-admin.service.ts.camaroneras_backend/test/platform-tenant-admin.e2e-spec.ts.camaroneras_docs/plataforma/superadmin-flow.md(contrato actualizado),plataforma/rbac-flow.md(servicios tenant reutilizados).