import { CanActivate, ExecutionContext, ForbiddenException, Injectable, UnauthorizedException, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import type { AuthUser } from '../../../common/auth/auth-user'; import type { PrivilegeAction } from '../../privileges/privilege-action'; import { PrivilegesService } from '../../privileges/privileges.service'; import { fieldPrivilegeKeys, isFieldPurpose, type FieldPurpose, type FieldResource, } from './field-purpose'; import { REQUIRE_FIELD_PRIVILEGE_KEY, type RequireFieldPrivilegeMeta, } from './field-privilege.decorator'; @Injectable() export class FieldPrivilegeGuard implements CanActivate { constructor( private readonly reflector: Reflector, private readonly privilegesService: PrivilegesService, ) {} async canActivate(context: ExecutionContext): Promise { const required = this.reflector.getAllAndOverride< RequireFieldPrivilegeMeta | undefined >(REQUIRE_FIELD_PRIVILEGE_KEY, [context.getHandler(), context.getClass()]); if (!required) { return true; } const request = context.switchToHttp().getRequest<{ user?: AuthUser; body?: { purpose?: string }; query?: { purpose?: string }; }>(); const user = request.user; if (!user) { throw new UnauthorizedException(); } if (user.isSuperadmin) { return true; } const purposeRaw = request.body?.purpose ?? request.query?.purpose; const allowed = await this.allowedPurposes( user.id, required.resource, required.action, ); if (purposeRaw !== undefined && purposeRaw !== '') { if (!isFieldPurpose(purposeRaw)) { throw new ForbiddenException('Insufficient privilege'); } if (!allowed.includes(purposeRaw)) { throw new ForbiddenException('Insufficient privilege'); } return true; } if (allowed.length === 0) { throw new ForbiddenException('Insufficient privilege'); } return true; } async allowedPurposes( userId: string, resource: FieldResource, action: PrivilegeAction, ): Promise { const purposes: FieldPurpose[] = ['sales', 'logistics']; const matches: FieldPurpose[] = []; for (const purpose of purposes) { const ok = await this.privilegesService.checkAnyPermission( userId, fieldPrivilegeKeys(resource, purpose), action, ); if (ok) { matches.push(purpose); } } return matches; } }