forked from Yara724/api
130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
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<boolean> {
|
|
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<boolean> {
|
|
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;
|
|
}
|
|
}
|