import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, ForbiddenException, } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; import { RoleEnum } from "src/Types&Enums/role.enum"; import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service"; import { Types } from "mongoose"; /** * Guard that allows: * - Users to access their own claim files * - Experts to access IN_PERSON claim files they initiated */ @Injectable() export class ClaimAccessGuard implements CanActivate { constructor( private readonly jwtService: JwtService, private readonly claimDbService: ClaimRequestManagementDbService, ) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const token = this.extractTokenFromHeader(request); if (!token) { throw new UnauthorizedException(); } try { const payload = await this.jwtService.verifyAsync(token, { secret: `${process.env.JWT_SECRET}`, }); // Allow users to pass through (they will be checked by service methods) if (payload.role === RoleEnum.USER) { request.user = payload; request.identity = request.user; return true; } // For experts, check if they're accessing an IN_PERSON claim file they initiated if ( payload.role === RoleEnum.EXPERT || payload.role === RoleEnum.DAMAGE_EXPERT ) { // Extract claimRequestId from route params const claimRequestId = request.params?.claimRequestId || request.params?.claimRequestID || request.params?.id; if (!claimRequestId) { // If no claim ID in params, allow access (e.g., for listing endpoints) // The service will filter appropriately request.user = payload; request.actor = payload; request.identity = payload; return true; } // Verify expert has access to this specific claim file const hasAccess = await this.verifyExpertClaimAccess( claimRequestId, payload.sub, ); if (!hasAccess) { throw new ForbiddenException( "You can only access IN_PERSON claim files that you initiated", ); } request.user = payload; request.actor = payload; request.identity = payload; return true; } throw new UnauthorizedException("Invalid role"); } catch (error) { if ( error instanceof ForbiddenException || error instanceof UnauthorizedException ) { throw error; } throw new UnauthorizedException(); } } private async verifyExpertClaimAccess( claimRequestId: string, expertId: string, ): Promise { try { const claim = await this.claimDbService.findOne(claimRequestId); if (!claim) { return false; } const blameFile = claim.blameFile; if (!blameFile) { return false; } // Check if it's an expert-initiated IN_PERSON file if ( blameFile.expertInitiated && blameFile.creationMethod === "IN_PERSON" && blameFile.initiatedBy ) { // Verify the expert is the one who initiated it return String(blameFile.initiatedBy) === expertId; } return false; } catch (error) { return false; } } private extractTokenFromHeader(request: any): string | undefined { const [type, token] = request.headers.authorization?.split(" ") ?? []; return type === "Bearer" ? token : undefined; } }