From 001ffcb4c6474afd9c663f0e9d3bebcebc2d7af1 Mon Sep 17 00:00:00 2001 From: vh13294 Date: Wed, 8 Sep 2021 18:32:29 +0700 Subject: [PATCH] Add warning if target signerUrl doesn't have guard --- README.md | 4 ++-- lib/helpers.ts | 26 +++++++++++++++++----- lib/interfaces.ts | 10 ++++++--- lib/url-generator.service.ts | 3 +++ sample/package-lock.json | 4 ++-- sample/src/emailModule/auth.guard.ts | 12 ++++++++++ sample/src/emailModule/email.controller.ts | 3 ++- 7 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 sample/src/emailModule/auth.guard.ts diff --git a/README.md b/README.md index 8c99122..636b7e5 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,6 @@ require('crypto').randomBytes(64, (err, buf) => { ### TODO -- [ ] Create unit test (expiration, tampered, with or without globalPrefix, request with or without query & param) +- [ ] Create unit test (expiration, tampered, with or without globalPrefix, request with or without query & param, if target for signerUrl doesn't have guard) + - [ ] Automate CI, npm run build, push, npm publish -- [ ] Add warning if target for signerUrl doesn't have guard diff --git a/lib/helpers.ts b/lib/helpers.ts index a6e3ccf..4ce29f4 100644 --- a/lib/helpers.ts +++ b/lib/helpers.ts @@ -1,11 +1,11 @@ import { createHmac, timingSafeEqual } from 'crypto'; import { stringify as qsStringify } from 'qs'; -import { PATH_METADATA } from '@nestjs/common/constants'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; - import { BadRequestException } from '@nestjs/common'; -import { ControllerMethod, Query, Params } from './interfaces'; +import { GUARDS_METADATA, PATH_METADATA } from '@nestjs/common/constants'; + +import { ControllerMethod, Query, Params, ControllerClass } from './interfaces'; +import { SignedUrlGuard } from './signed-url-guard'; export function generateUrl( appUrl: string, @@ -26,7 +26,7 @@ export function stringifyQuery(query?: Query): string { } export function getControllerMethodRoute( - controller: Controller, + controller: ControllerClass, controllerMethod: ControllerMethod, ): string { const controllerRoute = Reflect.getMetadata(PATH_METADATA, controller); @@ -34,6 +34,22 @@ export function getControllerMethodRoute( return joinRoutes(controllerRoute, methodRoute); } +export function checkIfMethodHasSignedGuardDecorator( + controller: ControllerClass, + controllerMethod: ControllerMethod, +): void { + const arrOfClasses = Reflect.getMetadata(GUARDS_METADATA, controllerMethod); + const errorMessage = `Please add SignedUrlGuard to ${controller.name}.${controllerMethod.name}`; + if (!arrOfClasses) { + throw new BadRequestException(errorMessage); + } + + const guardExist = arrOfClasses.includes(SignedUrlGuard); + if (!guardExist) { + throw new BadRequestException(errorMessage); + } +} + export function generateHmac(url: string, secret?: string): string { if (!secret) { throw new BadRequestException('Secret key is needed for signing URL'); diff --git a/lib/interfaces.ts b/lib/interfaces.ts index 19a7643..f949226 100644 --- a/lib/interfaces.ts +++ b/lib/interfaces.ts @@ -1,5 +1,9 @@ import { Request } from 'express'; -import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface'; +import { Controller } from '@nestjs/common/interfaces'; + +export interface ControllerClass extends Controller { + name: string; +} export type ControllerMethod = (...args: any[]) => Promise | any; @@ -28,7 +32,7 @@ export interface RequestWithSignature extends Request { } export interface GenerateUrlFromControllerArgs { - controller: Controller; + controller: ControllerClass; controllerMethod: ControllerMethod; query?: Query; params?: Params; @@ -41,7 +45,7 @@ export interface GenerateUrlFromPathArgs { } export interface SignControllerUrlArgs { - controller: Controller; + controller: ControllerClass; controllerMethod: ControllerMethod; expirationDate?: Date; query?: NotReservedQuery; diff --git a/lib/url-generator.service.ts b/lib/url-generator.service.ts index 1b70629..f6821c9 100644 --- a/lib/url-generator.service.ts +++ b/lib/url-generator.service.ts @@ -12,6 +12,7 @@ import { stringifyQuery, generateUrl, appendQuery, + checkIfMethodHasSignedGuardDecorator, } from './helpers'; import { @@ -86,6 +87,8 @@ export class UrlGeneratorService { query, params, }: SignControllerUrlArgs): string { + checkIfMethodHasSignedGuardDecorator(controller, controllerMethod); + const controllerMethodFullRoute = getControllerMethodRoute( controller, controllerMethod, diff --git a/sample/package-lock.json b/sample/package-lock.json index 11005dd..3f3baa8 100644 --- a/sample/package-lock.json +++ b/sample/package-lock.json @@ -7336,7 +7336,7 @@ "node_modules/nestjs-url-generator": { "version": "1.0.0", "resolved": "file:nestjs-url-generator-1.0.0.tgz", - "integrity": "sha512-WpHepo3FLGnzQry+d2f3PJbj+msX/3ODXoPH9+8cKu7I+FIXd1d0a9O24YSqJN59DkL5PTbs4K5wgsDUegIalg==", + "integrity": "sha512-SlM6xiaBiU60++U+ITiRF9VIb1JJL/Bl+DtlDDGHVMZE2evG6s0Pd/k7awSUQt37NgLs/7y4CehmrH2VhSAF4Q==", "license": "MIT", "dependencies": { "qs": "^6.10.1" @@ -15727,7 +15727,7 @@ }, "nestjs-url-generator": { "version": "file:nestjs-url-generator-1.0.0.tgz", - "integrity": "sha512-WpHepo3FLGnzQry+d2f3PJbj+msX/3ODXoPH9+8cKu7I+FIXd1d0a9O24YSqJN59DkL5PTbs4K5wgsDUegIalg==", + "integrity": "sha512-SlM6xiaBiU60++U+ITiRF9VIb1JJL/Bl+DtlDDGHVMZE2evG6s0Pd/k7awSUQt37NgLs/7y4CehmrH2VhSAF4Q==", "requires": { "qs": "^6.10.1" }, diff --git a/sample/src/emailModule/auth.guard.ts b/sample/src/emailModule/auth.guard.ts new file mode 100644 index 0000000..71b2425 --- /dev/null +++ b/sample/src/emailModule/auth.guard.ts @@ -0,0 +1,12 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthGuard implements CanActivate { + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + return true; + } +} diff --git a/sample/src/emailModule/email.controller.ts b/sample/src/emailModule/email.controller.ts index a6b2773..b685e8c 100644 --- a/sample/src/emailModule/email.controller.ts +++ b/sample/src/emailModule/email.controller.ts @@ -1,6 +1,7 @@ import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common'; import { SignedUrlGuard, UrlGeneratorService } from 'nestjs-url-generator'; +import { AuthGuard } from './auth.guard'; import { EmailParams } from './params/email.params'; import { EmailQuery } from './query/email.query'; @@ -45,7 +46,7 @@ export class EmailController { } @Get('emailVerification/version/:version/user/:userId') - @UseGuards(SignedUrlGuard) + @UseGuards(SignedUrlGuard, AuthGuard) async emailVerification( @Param() emailParams: EmailParams, @Query() emailQuery: EmailQuery,