From 15b0f47bebab427f5b3d0c1ef19db50dc2059460 Mon Sep 17 00:00:00 2001 From: aariprom Date: Tue, 7 Jan 2025 04:39:41 +0900 Subject: [PATCH 1/4] add: prevent review submission for 2025-1 --- .../decorators/prohibit-review.decorator.ts | 5 ++ src/modules/auth/auth.chain.ts | 7 ++- src/modules/auth/auth.command.ts | 1 + src/modules/auth/auth.config.ts | 11 +++- src/modules/auth/auth.module.ts | 4 ++ .../command/isReviewProhibited.command.ts | 57 +++++++++++++++++++ src/modules/reviews/reviews.controller.ts | 2 + src/settings.ts | 4 +- 8 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 src/common/decorators/prohibit-review.decorator.ts create mode 100644 src/modules/auth/command/isReviewProhibited.command.ts diff --git a/src/common/decorators/prohibit-review.decorator.ts b/src/common/decorators/prohibit-review.decorator.ts new file mode 100644 index 0000000..def923a --- /dev/null +++ b/src/common/decorators/prohibit-review.decorator.ts @@ -0,0 +1,5 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_REVIEW_PROHIBITED_KEY = 'isReviewProhibited'; +export const ReviewProhibited = () => + SetMetadata(IS_REVIEW_PROHIBITED_KEY, true); diff --git a/src/modules/auth/auth.chain.ts b/src/modules/auth/auth.chain.ts index e9a5f10..c80abae 100644 --- a/src/modules/auth/auth.chain.ts +++ b/src/modules/auth/auth.chain.ts @@ -24,6 +24,7 @@ export class AuthChain { authorization: false, authentication: false, isPublic: false, + isReviewProhibited: false, }; for (const command of this.authChain) { result = await command.next(context, result); @@ -32,7 +33,11 @@ export class AuthChain { } private async handleException(result: AuthResult) { - if (result.isPublic) return true; + if (result.isReviewProhibited) + throw new ForbiddenException( + 'review submissions for the 2025-1 lectures are currently not allowed', + ); + else if (result.isPublic) return true; else { if (!result.authentication) throw new UnauthorizedException(); if (!result.authorization) throw new ForbiddenException(); diff --git a/src/modules/auth/auth.command.ts b/src/modules/auth/auth.command.ts index 33ff0ae..9d5e50d 100644 --- a/src/modules/auth/auth.command.ts +++ b/src/modules/auth/auth.command.ts @@ -4,6 +4,7 @@ export interface AuthResult { authentication: boolean; authorization: boolean; isPublic: boolean; + isReviewProhibited: boolean; } export interface AuthCommand { diff --git a/src/modules/auth/auth.config.ts b/src/modules/auth/auth.config.ts index b503037..35c0e01 100644 --- a/src/modules/auth/auth.config.ts +++ b/src/modules/auth/auth.config.ts @@ -4,6 +4,7 @@ import { IsPublicCommand } from './command/isPublic.command'; import { JwtCommand } from './command/jwt.command'; import { SidCommand } from './command/sid.command'; import { SyncApiKeyCommand } from './command/syncApiKey.command'; +import { IsReviewProhibitedCommand } from '@src/modules/auth/command/isReviewProhibited.command'; @Injectable() export class AuthConfig { @@ -13,6 +14,7 @@ export class AuthConfig { private readonly sidCommand: SidCommand, private readonly isPublicCommand: IsPublicCommand, private readonly syncApiKeyCommand: SyncApiKeyCommand, + private readonly isReviewProhibitedCommand: IsReviewProhibitedCommand, ) {} public async config(env: string) { @@ -27,7 +29,8 @@ export class AuthConfig { .register(this.isPublicCommand) .register(this.sidCommand) .register(this.jwtCommand) - .register(this.syncApiKeyCommand); + .register(this.syncApiKeyCommand) + .register(this.isReviewProhibitedCommand); }; private getDevGuardConfig = () => { @@ -35,13 +38,15 @@ export class AuthConfig { .register(this.isPublicCommand) .register(this.sidCommand) .register(this.jwtCommand) - .register(this.syncApiKeyCommand); + .register(this.syncApiKeyCommand) + .register(this.isReviewProhibitedCommand); }; private getProdGuardConfig = () => { return this.authChain .register(this.jwtCommand) .register(this.isPublicCommand) - .register(this.syncApiKeyCommand); + .register(this.syncApiKeyCommand) + .register(this.isReviewProhibitedCommand); }; } diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 2922bc8..c3d0125 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -14,6 +14,8 @@ import { JwtCommand } from './command/jwt.command'; import { SidCommand } from './command/sid.command'; import { SyncApiKeyCommand } from './command/syncApiKey.command'; import { JwtCookieStrategy } from './strategy/jwt-cookie.strategy'; +import { IsReviewProhibitedCommand } from '@src/modules/auth/command/isReviewProhibited.command'; +import { LecturesService } from '@src/modules/lectures/lectures.service'; @Module({ imports: [ @@ -28,11 +30,13 @@ import { JwtCookieStrategy } from './strategy/jwt-cookie.strategy'; JwtCookieStrategy, UserService, UserRepository, + LecturesService, AuthChain, IsPublicCommand, JwtCommand, SidCommand, SyncApiKeyCommand, + IsReviewProhibitedCommand, AuthConfig, ], exports: [AuthService, AuthConfig, AuthChain], diff --git a/src/modules/auth/command/isReviewProhibited.command.ts b/src/modules/auth/command/isReviewProhibited.command.ts new file mode 100644 index 0000000..3f93f32 --- /dev/null +++ b/src/modules/auth/command/isReviewProhibited.command.ts @@ -0,0 +1,57 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { IS_REVIEW_PROHIBITED_KEY } from '../../../common/decorators/prohibit-review.decorator'; +import { Reflector } from '@nestjs/core'; +import { AuthCommand, AuthResult } from '../auth.command'; +import { LecturesService } from '@src/modules/lectures/lectures.service'; +import { Request } from 'express'; + +@Injectable() +export class IsReviewProhibitedCommand implements AuthCommand { + constructor( + private reflector: Reflector, + private lectureService: LecturesService, + ) {} + + public async next( + context: ExecutionContext, + prevResult: AuthResult, + ): Promise { + const isReviewProhibited = this.reflector.getAllAndOverride( + IS_REVIEW_PROHIBITED_KEY, + [context.getHandler()], + ); + + const request = context.switchToHttp().getRequest(); + const reviewsBody = request.body; + + console.log('isReviewProhibited: ', isReviewProhibited); + + console.log(reviewsBody); + + if (isReviewProhibited) { + try { + if (!reviewsBody || !reviewsBody.lecture) { + throw new Error('lecture info are not found from request'); + } + const lecture = await this.lectureService.getLectureById( + reviewsBody.lecture, + ); + console.log( + 'lecture year', + lecture.year, + 'lecture semester', + lecture.semester, + ); + if (lecture.year == 2025 && lecture.semester == 1) { + prevResult.isReviewProhibited = true; + } + return Promise.resolve(prevResult); + } catch (e: any) { + console.log(e); + return Promise.resolve(prevResult); + } + } + + return Promise.resolve(prevResult); + } +} diff --git a/src/modules/reviews/reviews.controller.ts b/src/modules/reviews/reviews.controller.ts index a05c65a..1bad4b9 100644 --- a/src/modules/reviews/reviews.controller.ts +++ b/src/modules/reviews/reviews.controller.ts @@ -16,6 +16,7 @@ import { IReview } from 'src/common/interfaces/IReview'; import { ReviewsService } from './reviews.service'; import { EReview } from '../../common/entities/EReview'; import EReviewVote = EReview.EReviewVote; +import { ReviewProhibited } from '@src/common/decorators/prohibit-review.decorator'; @Controller('api/reviews') export class ReviewsController { @@ -37,6 +38,7 @@ export class ReviewsController { return result; } + @ReviewProhibited() @Post() async createReviews( @Body() reviewsBody: IReview.CreateDto, diff --git a/src/settings.ts b/src/settings.ts index 2ef8bfa..0bcabf1 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -65,8 +65,8 @@ const getPrismaConfig = (): Prisma.PrismaClientOptions => { errorFormat: 'pretty', log: [ // { - // emit: 'event', - // level: 'query', + // emit: 'event', + // level: 'query', // }, { emit: 'stdout', From 81273f9be887a4340cee26793bdd28e930f52ac4 Mon Sep 17 00:00:00 2001 From: aariprom Date: Tue, 7 Jan 2025 07:29:50 +0900 Subject: [PATCH 2/4] fix: removed redundant logic --- src/modules/auth/auth.chain.ts | 7 +------ src/modules/auth/auth.command.ts | 1 - .../auth/command/isReviewProhibited.command.ts | 17 ++++------------- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/modules/auth/auth.chain.ts b/src/modules/auth/auth.chain.ts index c80abae..e9a5f10 100644 --- a/src/modules/auth/auth.chain.ts +++ b/src/modules/auth/auth.chain.ts @@ -24,7 +24,6 @@ export class AuthChain { authorization: false, authentication: false, isPublic: false, - isReviewProhibited: false, }; for (const command of this.authChain) { result = await command.next(context, result); @@ -33,11 +32,7 @@ export class AuthChain { } private async handleException(result: AuthResult) { - if (result.isReviewProhibited) - throw new ForbiddenException( - 'review submissions for the 2025-1 lectures are currently not allowed', - ); - else if (result.isPublic) return true; + if (result.isPublic) return true; else { if (!result.authentication) throw new UnauthorizedException(); if (!result.authorization) throw new ForbiddenException(); diff --git a/src/modules/auth/auth.command.ts b/src/modules/auth/auth.command.ts index 9d5e50d..33ff0ae 100644 --- a/src/modules/auth/auth.command.ts +++ b/src/modules/auth/auth.command.ts @@ -4,7 +4,6 @@ export interface AuthResult { authentication: boolean; authorization: boolean; isPublic: boolean; - isReviewProhibited: boolean; } export interface AuthCommand { diff --git a/src/modules/auth/command/isReviewProhibited.command.ts b/src/modules/auth/command/isReviewProhibited.command.ts index 3f93f32..f5c598a 100644 --- a/src/modules/auth/command/isReviewProhibited.command.ts +++ b/src/modules/auth/command/isReviewProhibited.command.ts @@ -24,10 +24,6 @@ export class IsReviewProhibitedCommand implements AuthCommand { const request = context.switchToHttp().getRequest(); const reviewsBody = request.body; - console.log('isReviewProhibited: ', isReviewProhibited); - - console.log(reviewsBody); - if (isReviewProhibited) { try { if (!reviewsBody || !reviewsBody.lecture) { @@ -36,18 +32,13 @@ export class IsReviewProhibitedCommand implements AuthCommand { const lecture = await this.lectureService.getLectureById( reviewsBody.lecture, ); - console.log( - 'lecture year', - lecture.year, - 'lecture semester', - lecture.semester, - ); + // TODO: implement logic to replace hardcoded values if (lecture.year == 2025 && lecture.semester == 1) { - prevResult.isReviewProhibited = true; + prevResult.authorization = false; } return Promise.resolve(prevResult); - } catch (e: any) { - console.log(e); + } catch (e) { + console.error(e); return Promise.resolve(prevResult); } } From 89feafcef0942548991e230b5e63dc1c40fb01af Mon Sep 17 00:00:00 2001 From: aariprom Date: Tue, 7 Jan 2025 12:36:53 +0900 Subject: [PATCH 3/4] fix: minor status fix --- src/modules/auth/command/isReviewProhibited.command.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/auth/command/isReviewProhibited.command.ts b/src/modules/auth/command/isReviewProhibited.command.ts index f5c598a..b898fe5 100644 --- a/src/modules/auth/command/isReviewProhibited.command.ts +++ b/src/modules/auth/command/isReviewProhibited.command.ts @@ -34,11 +34,11 @@ export class IsReviewProhibitedCommand implements AuthCommand { ); // TODO: implement logic to replace hardcoded values if (lecture.year == 2025 && lecture.semester == 1) { - prevResult.authorization = false; + prevResult.authentication = false; } return Promise.resolve(prevResult); } catch (e) { - console.error(e); + console.log(e); return Promise.resolve(prevResult); } } From 5d27457e0d6a6e355e36dd1cf6803e75d3294da5 Mon Sep 17 00:00:00 2001 From: aariprom Date: Wed, 8 Jan 2025 20:40:42 +0900 Subject: [PATCH 4/4] fix: auth result fix --- src/modules/auth/command/isReviewProhibited.command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/auth/command/isReviewProhibited.command.ts b/src/modules/auth/command/isReviewProhibited.command.ts index b898fe5..667b906 100644 --- a/src/modules/auth/command/isReviewProhibited.command.ts +++ b/src/modules/auth/command/isReviewProhibited.command.ts @@ -34,7 +34,7 @@ export class IsReviewProhibitedCommand implements AuthCommand { ); // TODO: implement logic to replace hardcoded values if (lecture.year == 2025 && lecture.semester == 1) { - prevResult.authentication = false; + prevResult.authorization = false; } return Promise.resolve(prevResult); } catch (e) {