import { CanActivate, ExecutionContext, ForbiddenException, Injectable, } from '@nestjs/common'; /** * Runs AFTER JwtAuthGuard (guard execution order follows APP_GUARD registration order). * At this point req.user is populated — the order is load-bearing. * * Sets ONLY req.tenantId for downstream code. Super-Admin can override the * tenant via the x-tenant-id header (D-10). * * DECISION (260911-e2s, Aufgabe 2): an earlier design also published a * tenant-scoped Prisma client on the request object, under a property * named `tenantPrisma` (assigned via `forTenant(...)`), duplicated * identically in a never-registered Express middleware class with the same * logic (deleted with 260911-e2s). A full-text search across * `apps/api/src` found no reader of that property outside those two * files — every one of the nine areas converted before this one binds * service-internally instead, one client per method call via the * tenant-binding helper in * `prisma-tenant.extension.ts`. That convention, settled by nine-fold * practice, is why this guard no longer creates a client at all: dead * wiring that LOOKS like a protection mechanism is worse than none — it * suggests a safeguard to a later reader that never actually ran. See * docs/mandantentrennung-etappe2-fehlerrichtung.md, section "## Bereich * tenant", (n4)(a) for the measurement and the reasoning. */ @Injectable() export class TenantGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const req = context.switchToHttp().getRequest(); const user = req.user; if (!user) { // Public route (login, health) — skip tenant context return true; } let tenantId: string | undefined = user.tenantId; if (user.role === 'SUPER_ADMIN' && req.headers['x-tenant-id']) { tenantId = req.headers['x-tenant-id'] as string; } if (!tenantId && user.role !== 'SUPER_ADMIN') { throw new ForbiddenException('No tenant context'); } if (tenantId) { req.tenantId = tenantId; } else { req.tenantId = null; } return true; } }