feat(operator): show per-tenant role in tenant users list

GET /tenants/:slug/users now returns a tenant-scoped `tenantRole`
(resolved server-side via roleForTenant), and the operator tenant page
displays it instead of the global `role` — so a user who is admin here
but member elsewhere reads correctly in this tenant's context. The
global `role` field is kept intact for other consumers.
This commit is contained in:
Ronni Baslund
2026-05-31 21:31:51 +02:00
parent f094158334
commit 2a43a7bbf3
3 changed files with 16 additions and 2 deletions
@@ -28,6 +28,7 @@ import { UpdateTenantBrandingDto } from './dto/update-tenant-branding.dto.js'
import { UpdateTenantDto } from './dto/update-tenant.dto.js'
import { StorageService } from './storage.service.js'
import { TenantBrandingService } from './tenant-branding.service.js'
import { roleForTenant } from '../schemas/user.schema.js'
import { TenantSsoService } from './tenant-sso.service.js'
import { TenantsService } from './tenants.service.js'
@@ -89,7 +90,15 @@ export class TenantsController {
if (!actor.platformAdmin && !actor.tenantIds.some((id) => id.equals(tenant._id))) {
throw new ForbiddenException(`No access to tenant "${slug}"`)
}
return this.tenants.listUsersForTenant(slug)
// Add a tenant-scoped `tenantRole` to each user so the UI shows the role
// for THIS tenant, not the global fallback. `role` is left intact for any
// other consumer; resolution stays server-side so the precedence in
// roleForTenant() isn't duplicated per frontend.
const users = await this.tenants.listUsersForTenant(slug)
return users.map((u) => ({
...u.toObject(),
tenantRole: roleForTenant(u, tenant._id),
}))
}
// Aggregate storage usage for the customer-admin Storage page. Same membership