From 422195235af627bc30f61c3b80c389c82a97d304 Mon Sep 17 00:00:00 2001 From: J-hoplin1 Date: Sat, 6 Jan 2024 14:46:05 +0900 Subject: [PATCH] Feat/Test: Add ContributerProblem Guard and remove unused test codes --- .../contributer/contributer.controller.ts | 8 +++ .../contributer/contributer.service.spec.ts | 68 ------------------- src/judge/contributer/contributer.service.ts | 19 +----- .../decorator/contributer-problem.guard.ts | 43 ++++++++++++ src/judge/decorator/problem.guard.ts | 9 +-- src/judge/judge.service.ts | 32 ++++----- 6 files changed, 68 insertions(+), 111 deletions(-) create mode 100644 src/judge/contributer/decorator/contributer-problem.guard.ts diff --git a/src/judge/contributer/contributer.controller.ts b/src/judge/contributer/contributer.controller.ts index fc2378c..355248e 100644 --- a/src/judge/contributer/contributer.controller.ts +++ b/src/judge/contributer/contributer.controller.ts @@ -18,6 +18,7 @@ import { UpdateProblmeDto } from 'app/judge/contributer/dto/update-problem.dto'; import { ContributerDocs } from './contributer.docs'; import { ContributerService } from './contributer.service'; import { UpdateExampleDto } from './dto'; +import { ContributerProblemGuard } from './decorator/contributer-problem.guard'; @Controller() @Role(['Admin', 'Contributer']) // Set Controller Level RBAC @@ -38,6 +39,7 @@ export class ContributerController { } @Get('problems/:pid') + @UseGuards(ContributerProblemGuard) @ContributerDocs.readProblem() readProblem( @GetUser('id') uid: string, @@ -53,6 +55,7 @@ export class ContributerController { } @Patch('problems/:pid') + @UseGuards(ContributerProblemGuard) @ContributerDocs.updateProblem() updateProblem( @GetUser('id') uid: string, @@ -63,6 +66,8 @@ export class ContributerController { } @Delete('problems/:pid') + @UseGuards(ContributerProblemGuard) + @ContributerDocs.deleteProblem() deleteProblem( @GetUser('id') uid: string, @Param('pid', ParseIntPipe) pid: number, @@ -71,6 +76,7 @@ export class ContributerController { } @Post('problems/:pid/examples') + @UseGuards(ContributerProblemGuard) @ContributerDocs.createExample() createExample( @GetUser('id') uid: string, @@ -80,6 +86,7 @@ export class ContributerController { } @Patch('problems/:pid/examples/:eid') + @UseGuards(ContributerProblemGuard) @ContributerDocs.updateExample() updateExample( @GetUser('id') uid: string, @@ -91,6 +98,7 @@ export class ContributerController { } @Delete('problems/:pid/examples/:eid') + @UseGuards(ContributerProblemGuard) @ContributerDocs.deleteExample() deleteExample( @GetUser('id') uid: string, diff --git a/src/judge/contributer/contributer.service.spec.ts b/src/judge/contributer/contributer.service.spec.ts index 4920230..1c3fee0 100644 --- a/src/judge/contributer/contributer.service.spec.ts +++ b/src/judge/contributer/contributer.service.spec.ts @@ -77,38 +77,6 @@ describe('ContributerService', () => { expect(problem1.title).toBe('Title'); }); - it('should throw if problem ID does not exist', async () => { - try { - await service.updateProblem(user1.id, 10000, { - title: 'Title', - problem: 'Problem', - input: 'Input', - output: 'Output', - timeLimit: 10, - memoryLimit: 10, - tags: ['string'], - }); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } - }); - - it('should throw if other contributer tries to update', async () => { - try { - await service.updateProblem(user2.id, problem1.id, { - title: 'Title', - problem: 'Problem', - input: 'Input', - output: 'Output', - timeLimit: 10, - memoryLimit: 10, - tags: ['string'], - }); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } - }); - it('should update example', async () => { example1 = await service.updateExample( user1.id, @@ -121,42 +89,6 @@ describe('ContributerService', () => { }, ); }); - - it('should throw if problem does not exist', async () => { - try { - await service.updateExample(user1.id, 999, example1.id, { - input: '2 3 4', - output: '5 6 7', - isPublic: true, - }); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } - }); - - it('should throw if example does not exist', async () => { - try { - await service.updateExample(user1.id, problem1.id, 100000, { - input: '2 3 4', - output: '5 6 7', - isPublic: true, - }); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } - }); - - it('should throw if other contributer tries to update example', async () => { - try { - await service.updateExample(user2.id, problem1.id, example1.id, { - input: '2 3 4', - output: '5 6 7', - isPublic: true, - }); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } - }); }); describe('listProblem() & readProblem()', () => { diff --git a/src/judge/contributer/contributer.service.ts b/src/judge/contributer/contributer.service.ts index 098a5b3..4c92fec 100644 --- a/src/judge/contributer/contributer.service.ts +++ b/src/judge/contributer/contributer.service.ts @@ -25,7 +25,7 @@ export class ContributerService { } async readProblem(uid: string, pid: number) { - const problem = await this.prisma.problem.findUnique({ + return await this.prisma.problem.findUnique({ where: { id: pid, contributerId: uid, @@ -34,10 +34,6 @@ export class ContributerService { examples: true, }, }); - if (!problem) { - throw new ForbiddenException('FORBIDDEN_REQUEST'); - } - return problem; } async createProblem(uid: string) { @@ -69,10 +65,6 @@ export class ContributerService { dto.memoryLimit = 128; } - if (!findProblem) { - throw new ForbiddenException('FORBIDDEN_REQUEST'); - } - const problem = await this.prisma.problem.update({ where: { id: pid, @@ -89,15 +81,6 @@ export class ContributerService { } async deleteProblem(uid: string, pid: number) { - const findProblem = await this.prisma.problem.findUnique({ - where: { - id: pid, - contributerId: uid, - }, - }); - if (!findProblem) { - throw new ForbiddenException('FORBIDDEN_REQUEST'); - } const updatedProblem = await this.prisma.problem.update({ where: { id: pid, diff --git a/src/judge/contributer/decorator/contributer-problem.guard.ts b/src/judge/contributer/decorator/contributer-problem.guard.ts new file mode 100644 index 0000000..2e318c5 --- /dev/null +++ b/src/judge/contributer/decorator/contributer-problem.guard.ts @@ -0,0 +1,43 @@ +import { + BadRequestException, + CanActivate, + ExecutionContext, + ForbiddenException, + Inject, + Injectable, +} from '@nestjs/common'; +import { PrismaService } from 'app/prisma/prisma.service'; +import { Request } from 'express'; + +/** + * Contributer problem Id Checker + * + * Assuem that user id already check from controller level Auth Guard + * + * Return 403 Forbidden Error if problem with contributer ID not found + */ + +@Injectable() +export class ContributerProblemGuard implements CanActivate { + constructor(@Inject(PrismaService) private prisma: PrismaService) {} + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + + // Get User info + const userId = request.user['id']; + // Get Problem Id + const problemId = request.params['pid']; + + try { + await this.prisma.problem.findUniqueOrThrow({ + where: { + id: parseInt(problemId), + contributerId: userId, + }, + }); + return true; + } catch (err) { + throw new ForbiddenException('FORBIDDEN_REQUEST'); + } + } +} diff --git a/src/judge/decorator/problem.guard.ts b/src/judge/decorator/problem.guard.ts index c0eebb8..6cc8586 100644 --- a/src/judge/decorator/problem.guard.ts +++ b/src/judge/decorator/problem.guard.ts @@ -11,6 +11,8 @@ import { Request } from 'express'; /** * Problem Id Checker * Only use for problem id required routers + * + * Return 400 BadRequest Error if problem does not exist */ @Injectable() @@ -21,14 +23,9 @@ export class ProblemGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const problemId = request.params['pid']; - // If path parameter problem id is missing - if (!problemId) { - throw new BadRequestException('PROBLEM_ID_MISSING'); - } - // Check if problem in DB try { - const problem = await this.prisma.problem.findUnique({ + await this.prisma.problem.findUniqueOrThrow({ where: { id: parseInt(problemId), }, diff --git a/src/judge/judge.service.ts b/src/judge/judge.service.ts index 2ab0141..adea984 100644 --- a/src/judge/judge.service.ts +++ b/src/judge/judge.service.ts @@ -1,12 +1,11 @@ import { BadRequestException, Injectable } from '@nestjs/common'; +import { PaginateObject } from 'app/decorator'; import { PrismaService } from 'app/prisma/prisma.service'; import { Judge0Service } from 'judge/judge0'; -import { GetLanguagesResponse } from './response/get-languages.response'; -import { PaginateObject } from 'app/decorator'; import { JudgeFilterObject } from './decorator/judge-filter.decorator'; -import { RunProblemDto, SubmitProblemDto } from './dto'; -import { Problem } from '@prisma/client'; import { SubmissionFilterObject } from './decorator/submission-filter.decorator'; +import { RunProblemDto, SubmitProblemDto } from './dto'; +import { GetLanguagesResponse } from './response/get-languages.response'; @Injectable() export class JudgeService { @@ -85,23 +84,18 @@ export class JudgeService { } async readProblem(pid: number) { - try { - const problem = await this.prisma.problem.findUniqueOrThrow({ - where: { - id: pid, - }, - include: { - examples: { - where: { - isPublic: true, - }, + return await this.prisma.problem.findUniqueOrThrow({ + where: { + id: pid, + }, + include: { + examples: { + where: { + isPublic: true, }, }, - }); - return problem; - } catch (err) { - throw new BadRequestException('PROBLEM_NOT_FOUND'); - } + }, + }); } async runProblem(pid: number, dto: RunProblemDto) {