Skip to content

Commit

Permalink
Feat/Test: Add ContributerProblem Guard and remove unused test codes
Browse files Browse the repository at this point in the history
  • Loading branch information
J-Hoplin committed Jan 6, 2024
1 parent 518c906 commit 4221952
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 111 deletions.
8 changes: 8 additions & 0 deletions src/judge/contributer/contributer.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -38,6 +39,7 @@ export class ContributerController {
}

@Get('problems/:pid')
@UseGuards(ContributerProblemGuard)
@ContributerDocs.readProblem()
readProblem(
@GetUser('id') uid: string,
Expand All @@ -53,6 +55,7 @@ export class ContributerController {
}

@Patch('problems/:pid')
@UseGuards(ContributerProblemGuard)
@ContributerDocs.updateProblem()
updateProblem(
@GetUser('id') uid: string,
Expand All @@ -63,6 +66,8 @@ export class ContributerController {
}

@Delete('problems/:pid')
@UseGuards(ContributerProblemGuard)
@ContributerDocs.deleteProblem()
deleteProblem(
@GetUser('id') uid: string,
@Param('pid', ParseIntPipe) pid: number,
Expand All @@ -71,6 +76,7 @@ export class ContributerController {
}

@Post('problems/:pid/examples')
@UseGuards(ContributerProblemGuard)
@ContributerDocs.createExample()
createExample(
@GetUser('id') uid: string,
Expand All @@ -80,6 +86,7 @@ export class ContributerController {
}

@Patch('problems/:pid/examples/:eid')
@UseGuards(ContributerProblemGuard)
@ContributerDocs.updateExample()
updateExample(
@GetUser('id') uid: string,
Expand All @@ -91,6 +98,7 @@ export class ContributerController {
}

@Delete('problems/:pid/examples/:eid')
@UseGuards(ContributerProblemGuard)
@ContributerDocs.deleteExample()
deleteExample(
@GetUser('id') uid: string,
Expand Down
68 changes: 0 additions & 68 deletions src/judge/contributer/contributer.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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()', () => {
Expand Down
19 changes: 1 addition & 18 deletions src/judge/contributer/contributer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -34,10 +34,6 @@ export class ContributerService {
examples: true,
},
});
if (!problem) {
throw new ForbiddenException('FORBIDDEN_REQUEST');
}
return problem;
}

async createProblem(uid: string) {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
43 changes: 43 additions & 0 deletions src/judge/contributer/decorator/contributer-problem.guard.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
const request = context.switchToHttp().getRequest<Request>();

// 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');
}
}
}
9 changes: 3 additions & 6 deletions src/judge/decorator/problem.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -21,14 +23,9 @@ export class ProblemGuard implements CanActivate {
const request = context.switchToHttp().getRequest<Request>();
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),
},
Expand Down
32 changes: 13 additions & 19 deletions src/judge/judge.service.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 4221952

Please sign in to comment.