diff --git a/src/claims/claims.controller.ts b/src/claims/claims.controller.ts index 7a523fb..42fc7ae 100644 --- a/src/claims/claims.controller.ts +++ b/src/claims/claims.controller.ts @@ -24,10 +24,17 @@ export class ClaimsController { @ApiBearerAuth() @ApiOperation({ summary: 'Submit a manual claim for a policy' }) @ApiResponse({ status: 201, description: 'Claim submitted successfully' }) + @ApiResponse({ status: 403, description: 'Claimant does not match authenticated wallet' }) @ApiResponse({ status: 409, description: 'Claim already exists for this policy' }) async submitClaim(@Body() dto: SubmitClaimDto, @Req() req: AuthenticatedRequest) { - const walletAddress = req.wallet || dto.claimant; - const claimId = await this.claims.submitClaim(walletAddress, dto.policyId); + const authedWallet = req.user?.walletAddress || req.wallet; + if (!authedWallet) { + throw new UnauthorizedException('Not authenticated'); + } + if (dto.claimant && dto.claimant !== authedWallet) { + throw new ForbiddenException('Claimant does not match authenticated wallet'); + } + const claimId = await this.claims.submitClaim(authedWallet, dto.policyId); return { success: true, data: { claimId } }; } @@ -47,11 +54,12 @@ export class ClaimsController { @Query('limit') limit: string, @Req() req: AuthenticatedRequest, ) { - const targetWallet = wallet || req.wallet; - if (!targetWallet) { - throw new UnauthorizedException('Wallet address is required'); + const authedWallet = req.user?.walletAddress || req.wallet; + if (!authedWallet) { + throw new UnauthorizedException('Not authenticated'); } - if (req.wallet && req.wallet !== targetWallet) { + const targetWallet = wallet || authedWallet; + if (targetWallet !== authedWallet) { throw new ForbiddenException('Wallet address does not match authenticated user'); } const result = await this.claims.getClaimsByWallet( @@ -89,7 +97,8 @@ export class ClaimsController { if (!claim) { throw new NotFoundException('Claim not found'); } - if (claim.claimant !== req.wallet) { + const authedWallet = req.user?.walletAddress || req.wallet; + if (claim.claimant !== authedWallet) { throw new ForbiddenException('Claim belongs to a different wallet'); } return { success: true, data: claim }; @@ -110,12 +119,13 @@ export class ClaimsController { @Query('limit') limit: string, @Req() req: AuthenticatedRequest, ) { - const targetWallet = wallet || req.wallet; - if (!targetWallet) { - throw new UnauthorizedException('Wallet address is required'); + const authedWallet = req.user?.walletAddress || req.wallet; + if (!authedWallet) { + throw new UnauthorizedException('Not authenticated'); } - if (req.wallet && req.wallet !== targetWallet) { - throw new UnauthorizedException('Cannot read claims for another wallet'); + const targetWallet = wallet || authedWallet; + if (targetWallet !== authedWallet) { + throw new ForbiddenException('Cannot read claims for another wallet'); } const result = await this.claims.getClaimsByWallet( targetWallet, diff --git a/src/policy/policy.controller.ts b/src/policy/policy.controller.ts index d05c11f..de107e2 100644 --- a/src/policy/policy.controller.ts +++ b/src/policy/policy.controller.ts @@ -8,6 +8,7 @@ import { HttpCode, HttpStatus, NotFoundException, + ForbiddenException, BadRequestException, UseGuards, Req, @@ -56,12 +57,13 @@ export class PolicyController { @Query('limit') limit: string = '20', @Req() req: AuthenticatedRequest, ) { - const targetWallet = wallet || req.wallet; - if (!targetWallet) { + const authedWallet = req.user?.walletAddress || req.wallet; + if (!authedWallet) { throw new BadRequestException('wallet query param required'); } - if (req.wallet && req.wallet !== targetWallet) { - throw new UnauthorizedException('Cannot fetch policies for another wallet'); + const targetWallet = wallet || authedWallet; + if (targetWallet !== authedWallet) { + throw new ForbiddenException('Cannot fetch policies for another wallet'); } const pageNum = Math.max(1, parseInt(page, 10) || 1); const limitNum = Math.min(100, Math.max(1, parseInt(limit, 10) || 20)); @@ -69,17 +71,24 @@ export class PolicyController { return { success: true, ...result }; } - /** GET /api/v1/policies/:id — get a single policy by ID */ + /** GET /api/v1/policies/:id — get a single policy by ID (owner only) */ @Get('policies/:id') + @UseGuards(JwtAuthGuard) + @ApiBearerAuth() @ApiOperation({ summary: 'Get a single policy by ID' }) @ApiParam({ name: 'id', description: 'Policy UUID' }) @ApiResponse({ status: 200, description: 'Returns the policy details' }) + @ApiResponse({ status: 403, description: 'Policy belongs to a different wallet' }) @ApiResponse({ status: 404, description: 'Policy not found' }) - async getPolicy(@Param('id') id: string) { + async getPolicy(@Param('id') id: string, @Req() req: AuthenticatedRequest) { const policyData = await this.policy.getPolicy(id); if (!policyData) { throw new NotFoundException(`Policy ${id} not found`); } + const authedWallet = req.user?.walletAddress || req.wallet; + if (policyData.policyholder !== authedWallet) { + throw new ForbiddenException('Policy belongs to a different wallet'); + } return { success: true, data: policyData }; } @@ -92,8 +101,9 @@ export class PolicyController { @ApiResponse({ status: 200, description: 'Returns premium quote for the requested coverage' }) @ApiResponse({ status: 400, description: 'Invalid request body' }) async buyPolicy(@Req() req: AuthenticatedRequest, @Body() dto: BuyPolicyDto) { - if (dto.walletAddress !== req.wallet) { - throw new UnauthorizedException('Wallet address does not match authenticated user'); + const authedWallet = req.user?.walletAddress || req.wallet; + if (dto.walletAddress !== authedWallet) { + throw new ForbiddenException('Wallet address does not match authenticated user'); } const products = await this.policy.getActiveProducts(); const product = products.find((p) => p.id === dto.productId); @@ -135,7 +145,11 @@ export class PolicyController { @ApiOperation({ summary: 'Submit signed XDR to complete policy purchase on-chain' }) @ApiResponse({ status: 200, description: 'Policy created on-chain and persisted; returns policyId and txHash' }) @ApiResponse({ status: 400, description: 'Invalid request body or on-chain submission failed' }) - async confirmPolicy(@Body() dto: ConfirmPolicyDto) { + async confirmPolicy(@Body() dto: ConfirmPolicyDto, @Req() req: AuthenticatedRequest) { + const authedWallet = req.user?.walletAddress || req.wallet; + if (dto.walletAddress !== authedWallet) { + throw new ForbiddenException('Wallet address does not match authenticated user'); + } const result = await this.policy.confirmAndCreatePolicy(dto); return { success: true, data: result }; }