From fa8ca0569d915bbbed62097891caef82b94f8064 Mon Sep 17 00:00:00 2001 From: Mir Arif Hasan Date: Tue, 21 Mar 2023 17:15:50 +0600 Subject: [PATCH] feat: introducing rate-limiting on queries, mutations and most of the REST endpoints (HBE-111) (#46) * feat: rate-limiting guard added and configured in app module * feat: rate-limit annotation added in controllers and resolvers (query, mutation, not subscription) * docs: added comments --- packages/hoppscotch-backend/.env.example | 4 ++++ packages/hoppscotch-backend/package.json | 1 + packages/hoppscotch-backend/src/app.module.ts | 5 +++++ .../hoppscotch-backend/src/auth/auth.controller.ts | 10 +++++++--- .../src/guards/gql-throttler.guard.ts | 12 ++++++++++++ .../src/guards/throttler-behind-proxy.guard.ts | 10 ++++++++++ .../src/shortcode/shortcode.resolver.ts | 5 +++++ .../src/team-collection/team-collection.resolver.ts | 8 ++++++++ .../team-environments/team-environments.resolver.ts | 6 ++++++ .../src/team-invitation/team-invitation.resolver.ts | 5 +++++ .../src/team-request/team-request.resolver.ts | 8 ++++++++ .../hoppscotch-backend/src/team/team.resolver.ts | 7 ++++++- .../src/user-collection/user-collection.resolver.ts | 8 ++++++++ .../user-environment/user-environments.resolver.ts | 7 +++++++ .../src/user-history/user-history.resolver.ts | 7 +++++++ .../user-request/resolvers/user-request.resolver.ts | 7 +++++++ .../src/user-settings/user-settings.resolver.ts | 5 +++++ .../hoppscotch-backend/src/user/user.resolver.ts | 4 ++++ 18 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 packages/hoppscotch-backend/src/guards/gql-throttler.guard.ts create mode 100644 packages/hoppscotch-backend/src/guards/throttler-behind-proxy.guard.ts diff --git a/packages/hoppscotch-backend/.env.example b/packages/hoppscotch-backend/.env.example index 3396b2240..c2fcf5264 100644 --- a/packages/hoppscotch-backend/.env.example +++ b/packages/hoppscotch-backend/.env.example @@ -5,6 +5,10 @@ DATABASE_URL=postgresql://postgres:testpass@dev-db:5432/hoppscotch MAILER_SMTP_URL="smtps://user@domain.com:pass@smtp.domain.com" MAILER_ADDRESS_FROM='"From Name Here" ' +# Rate Limit Config +RATE_LIMIT_TTL=60 # In seconds +RATE_LIMIT_MAX=100 # Max requests per IP + # Auth Tokens Config JWT_SECRET='add some secret here' TOKEN_SALT_COMPLEXITY=10 diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index 130f3a635..91349bd6d 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -31,6 +31,7 @@ "@nestjs/jwt": "^10.0.1", "@nestjs/passport": "^9.0.0", "@nestjs/platform-express": "^9.2.1", + "@nestjs/throttler": "^4.0.0", "@prisma/client": "^4.7.1", "apollo-server-express": "^3.11.1", "apollo-server-plugin-base": "^3.7.1", diff --git a/packages/hoppscotch-backend/src/app.module.ts b/packages/hoppscotch-backend/src/app.module.ts index 58d20fec1..a52d7984b 100644 --- a/packages/hoppscotch-backend/src/app.module.ts +++ b/packages/hoppscotch-backend/src/app.module.ts @@ -18,6 +18,7 @@ import { AdminModule } from './admin/admin.module'; import { UserCollectionModule } from './user-collection/user-collection.module'; import { ShortcodeModule } from './shortcode/shortcode.module'; import { COOKIES_NOT_FOUND } from './errors'; +import { ThrottlerModule } from '@nestjs/throttler'; @Module({ imports: [ @@ -60,6 +61,10 @@ import { COOKIES_NOT_FOUND } from './errors'; }), driver: ApolloDriver, }), + ThrottlerModule.forRoot({ + ttl: +process.env.RATE_LIMIT_TTL, + limit: +process.env.RATE_LIMIT_MAX, + }), UserModule, AuthModule, AdminModule, diff --git a/packages/hoppscotch-backend/src/auth/auth.controller.ts b/packages/hoppscotch-backend/src/auth/auth.controller.ts index 716faf8ed..0309eabd2 100644 --- a/packages/hoppscotch-backend/src/auth/auth.controller.ts +++ b/packages/hoppscotch-backend/src/auth/auth.controller.ts @@ -2,8 +2,6 @@ import { Body, Controller, Get, - HttpException, - HttpStatus, Post, Req, Request, @@ -20,11 +18,14 @@ import { JwtAuthGuard } from './guards/jwt-auth.guard'; import { GqlUser } from 'src/decorators/gql-user.decorator'; import { AuthUser } from 'src/types/AuthUser'; import { RTCookie } from 'src/decorators/rt-cookie.decorator'; -import { AuthGuard } from '@nestjs/passport'; import { authCookieHandler, throwHTTPErr } from './helper'; import { GoogleSSOGuard } from './guards/google-sso.guard'; import { GithubSSOGuard } from './guards/github-sso.guard'; import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard'; +import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard'; +import { SkipThrottle } from '@nestjs/throttler'; + +@UseGuards(ThrottlerBehindProxyGuard) @Controller({ path: 'auth', version: '1' }) export class AuthController { constructor(private authService: AuthService) {} @@ -82,6 +83,7 @@ export class AuthController { * @see https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow#how-it-works */ @Get('google/callback') + @SkipThrottle() @UseGuards(GoogleSSOGuard) async googleAuthRedirect(@Request() req, @Res() res) { const authTokens = await this.authService.generateAuthTokens(req.user.uid); @@ -106,6 +108,7 @@ export class AuthController { * @see https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow#how-it-works */ @Get('github/callback') + @SkipThrottle() @UseGuards(GithubSSOGuard) async githubAuthRedirect(@Request() req, @Res() res) { const authTokens = await this.authService.generateAuthTokens(req.user.uid); @@ -130,6 +133,7 @@ export class AuthController { * @see https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow#how-it-works */ @Get('microsoft/callback') + @SkipThrottle() @UseGuards(MicrosoftSSOGuard) async microsoftAuthRedirect(@Request() req, @Res() res) { const authTokens = await this.authService.generateAuthTokens(req.user.uid); diff --git a/packages/hoppscotch-backend/src/guards/gql-throttler.guard.ts b/packages/hoppscotch-backend/src/guards/gql-throttler.guard.ts new file mode 100644 index 000000000..143165866 --- /dev/null +++ b/packages/hoppscotch-backend/src/guards/gql-throttler.guard.ts @@ -0,0 +1,12 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { GqlExecutionContext } from '@nestjs/graphql'; +import { ThrottlerGuard } from '@nestjs/throttler'; + +@Injectable() +export class GqlThrottlerGuard extends ThrottlerGuard { + getRequestResponse(context: ExecutionContext) { + const gqlCtx = GqlExecutionContext.create(context); + const ctx = gqlCtx.getContext(); + return { req: ctx.req, res: ctx.res }; + } +} diff --git a/packages/hoppscotch-backend/src/guards/throttler-behind-proxy.guard.ts b/packages/hoppscotch-backend/src/guards/throttler-behind-proxy.guard.ts new file mode 100644 index 000000000..33e94ecbe --- /dev/null +++ b/packages/hoppscotch-backend/src/guards/throttler-behind-proxy.guard.ts @@ -0,0 +1,10 @@ +import { ThrottlerGuard } from '@nestjs/throttler'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ThrottlerBehindProxyGuard extends ThrottlerGuard { + protected getTracker(req: Record): string { + return req.ips.length ? req.ips[0] : req.ip; // individualize IP extraction to meet your own needs + // learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#directives + } +} diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.resolver.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.resolver.ts index 33376b5ac..e357a9074 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.resolver.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.resolver.ts @@ -20,7 +20,10 @@ import { PubSubService } from 'src/pubsub/pubsub.service'; import { AuthUser } from '../types/AuthUser'; import { JwtService } from '@nestjs/jwt'; import { PaginationArgs } from 'src/types/input-types.args'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => Shortcode) export class ShortcodeResolver { constructor( @@ -105,6 +108,7 @@ export class ShortcodeResolver { description: 'Listen for shortcode creation', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) myShortcodesCreated(@GqlUser() user: AuthUser) { return this.pubsub.asyncIterator(`shortcode/${user.uid}/created`); @@ -114,6 +118,7 @@ export class ShortcodeResolver { description: 'Listen for shortcode deletion', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) myShortcodesRevoked(@GqlUser() user: AuthUser): AsyncIterator { return this.pubsub.asyncIterator(`shortcode/${user.uid}/revoked`); diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts index ca729110c..6eeabc945 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts @@ -29,7 +29,10 @@ import { } from './input-type.args'; import * as E from 'fp-ts/Either'; import { throwErr } from 'src/utils'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => TeamCollection) export class TeamCollectionResolver { constructor( @@ -312,6 +315,7 @@ export class TeamCollectionResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamCollectionAdded( @Args({ @@ -333,6 +337,7 @@ export class TeamCollectionResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamCollectionUpdated( @Args({ @@ -354,6 +359,7 @@ export class TeamCollectionResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamCollectionRemoved( @Args({ @@ -375,6 +381,7 @@ export class TeamCollectionResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamCollectionMoved( @Args({ @@ -396,6 +403,7 @@ export class TeamCollectionResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) collectionOrderUpdated( @Args({ diff --git a/packages/hoppscotch-backend/src/team-environments/team-environments.resolver.ts b/packages/hoppscotch-backend/src/team-environments/team-environments.resolver.ts index 303783e7e..6775fb4c3 100644 --- a/packages/hoppscotch-backend/src/team-environments/team-environments.resolver.ts +++ b/packages/hoppscotch-backend/src/team-environments/team-environments.resolver.ts @@ -1,8 +1,10 @@ import { UseGuards } from '@nestjs/common'; import { Resolver, Mutation, Args, Subscription, ID } from '@nestjs/graphql'; +import { SkipThrottle } from '@nestjs/throttler'; import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither'; import { GqlAuthGuard } from 'src/guards/gql-auth.guard'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { PubSubService } from 'src/pubsub/pubsub.service'; import { RequiresTeamRole } from 'src/team/decorators/requires-team-role.decorator'; import { GqlTeamMemberGuard } from 'src/team/guards/gql-team-member.guard'; @@ -12,6 +14,7 @@ import { GqlTeamEnvTeamGuard } from './gql-team-env-team.guard'; import { TeamEnvironment } from './team-environments.model'; import { TeamEnvironmentsService } from './team-environments.service'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => 'TeamEnvironment') export class TeamEnvironmentsResolver { constructor( @@ -144,6 +147,7 @@ export class TeamEnvironmentsResolver { description: 'Listen for Team Environment Updates', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.OWNER, @@ -165,6 +169,7 @@ export class TeamEnvironmentsResolver { description: 'Listen for Team Environment Creation Messages', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.OWNER, @@ -186,6 +191,7 @@ export class TeamEnvironmentsResolver { description: 'Listen for Team Environment Deletion Messages', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.OWNER, diff --git a/packages/hoppscotch-backend/src/team-invitation/team-invitation.resolver.ts b/packages/hoppscotch-backend/src/team-invitation/team-invitation.resolver.ts index b2948a9f1..1e02a96d9 100644 --- a/packages/hoppscotch-backend/src/team-invitation/team-invitation.resolver.ts +++ b/packages/hoppscotch-backend/src/team-invitation/team-invitation.resolver.ts @@ -34,7 +34,10 @@ import { TeamInviteViewerGuard } from './team-invite-viewer.guard'; import { TeamInviteTeamOwnerGuard } from './team-invite-team-owner.guard'; import { UserService } from 'src/user/user.service'; import { PubSubService } from 'src/pubsub/pubsub.service'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => TeamInvitation) export class TeamInvitationResolver { constructor( @@ -199,6 +202,7 @@ export class TeamInvitationResolver { description: 'Listens to when a Team Invitation is added', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.OWNER, @@ -220,6 +224,7 @@ export class TeamInvitationResolver { description: 'Listens to when a Team Invitation is removed', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.OWNER, diff --git a/packages/hoppscotch-backend/src/team-request/team-request.resolver.ts b/packages/hoppscotch-backend/src/team-request/team-request.resolver.ts index e2dcfb3bf..52b652df8 100644 --- a/packages/hoppscotch-backend/src/team-request/team-request.resolver.ts +++ b/packages/hoppscotch-backend/src/team-request/team-request.resolver.ts @@ -30,7 +30,10 @@ import { PubSubService } from 'src/pubsub/pubsub.service'; import * as E from 'fp-ts/Either'; import * as O from 'fp-ts/Option'; import { throwErr } from 'src/utils'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => TeamRequest) export class TeamRequestResolver { constructor( @@ -242,6 +245,7 @@ export class TeamRequestResolver { description: 'Emits when a new request is added to a team', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.VIEWER, @@ -263,6 +267,7 @@ export class TeamRequestResolver { description: 'Emitted when a request has been updated', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.VIEWER, @@ -285,6 +290,7 @@ export class TeamRequestResolver { 'Emitted when a request has been deleted. Only the id of the request is emitted.', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.VIEWER, @@ -307,6 +313,7 @@ export class TeamRequestResolver { 'Emitted when a requests position has been changed in its collection', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.VIEWER, @@ -329,6 +336,7 @@ export class TeamRequestResolver { 'Emitted when a request has been moved from one collection into another', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) @RequiresTeamRole( TeamMemberRole.VIEWER, diff --git a/packages/hoppscotch-backend/src/team/team.resolver.ts b/packages/hoppscotch-backend/src/team/team.resolver.ts index f9ffe2f9e..94c3ce29b 100644 --- a/packages/hoppscotch-backend/src/team/team.resolver.ts +++ b/packages/hoppscotch-backend/src/team/team.resolver.ts @@ -11,7 +11,6 @@ import { ID, } from '@nestjs/graphql'; import { TeamService } from './team.service'; -import { User } from '../user/user.model'; import { GqlAuthGuard } from '../guards/gql-auth.guard'; import { GqlUser } from '../decorators/gql-user.decorator'; import { UseGuards } from '@nestjs/common'; @@ -21,7 +20,10 @@ import { PubSubService } from '../pubsub/pubsub.service'; import * as E from 'fp-ts/Either'; import { throwErr } from 'src/utils'; import { AuthUser } from 'src/types/AuthUser'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => Team) export class TeamResolver { constructor( @@ -271,6 +273,7 @@ export class TeamResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamMemberAdded( @Args({ @@ -293,6 +296,7 @@ export class TeamResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamMemberUpdated( @Args({ @@ -315,6 +319,7 @@ export class TeamResolver { TeamMemberRole.EDITOR, TeamMemberRole.VIEWER, ) + @SkipThrottle() @UseGuards(GqlAuthGuard, GqlTeamMemberGuard) teamMemberRemoved( @Args({ diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts index 0ffc9bca8..ae8182cbf 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts @@ -31,7 +31,10 @@ import { } from './input-type.args'; import { ReqType } from 'src/types/RequestTypes'; import * as E from 'fp-ts/Either'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => UserCollection) export class UserCollectionResolver { constructor( @@ -352,6 +355,7 @@ export class UserCollectionResolver { description: 'Listen for User Collection Creation', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userCollectionCreated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/created`); @@ -361,6 +365,7 @@ export class UserCollectionResolver { description: 'Listen to when a User Collection has been updated.', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userCollectionUpdated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/updated`); @@ -370,6 +375,7 @@ export class UserCollectionResolver { description: 'Listen to when a User Collection has been deleted', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userCollectionRemoved(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/deleted`); @@ -379,6 +385,7 @@ export class UserCollectionResolver { description: 'Listen to when a User Collection has been moved', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userCollectionMoved(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/moved`); @@ -388,6 +395,7 @@ export class UserCollectionResolver { description: 'Listen to when a User Collections position has changed', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userCollectionOrderUpdated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/order_updated`); diff --git a/packages/hoppscotch-backend/src/user-environment/user-environments.resolver.ts b/packages/hoppscotch-backend/src/user-environment/user-environments.resolver.ts index dba74eefd..875780dd0 100644 --- a/packages/hoppscotch-backend/src/user-environment/user-environments.resolver.ts +++ b/packages/hoppscotch-backend/src/user-environment/user-environments.resolver.ts @@ -8,7 +8,10 @@ import { User } from '../user/user.model'; import { UserEnvironmentsService } from './user-environments.service'; import * as E from 'fp-ts/Either'; import { throwErr } from 'src/utils'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver() export class UserEnvironmentsResolver { constructor( @@ -157,6 +160,7 @@ export class UserEnvironmentsResolver { description: 'Listen for User Environment Creation', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userEnvironmentCreated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_environment/${user.uid}/created`); @@ -166,6 +170,7 @@ export class UserEnvironmentsResolver { description: 'Listen for User Environment updates', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userEnvironmentUpdated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_environment/${user.uid}/updated`); @@ -175,6 +180,7 @@ export class UserEnvironmentsResolver { description: 'Listen for User Environment deletion', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userEnvironmentDeleted(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_environment/${user.uid}/deleted`); @@ -184,6 +190,7 @@ export class UserEnvironmentsResolver { description: 'Listen for User Environment DeleteMany', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userEnvironmentDeleteMany(@GqlUser() user: User) { return this.pubsub.asyncIterator( diff --git a/packages/hoppscotch-backend/src/user-history/user-history.resolver.ts b/packages/hoppscotch-backend/src/user-history/user-history.resolver.ts index 1df39ecf6..f3741a7aa 100644 --- a/packages/hoppscotch-backend/src/user-history/user-history.resolver.ts +++ b/packages/hoppscotch-backend/src/user-history/user-history.resolver.ts @@ -8,7 +8,10 @@ import { GqlUser } from '../decorators/gql-user.decorator'; import { User } from '../user/user.model'; import { throwErr } from '../utils'; import * as E from 'fp-ts/Either'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver() export class UserHistoryResolver { constructor( @@ -115,6 +118,7 @@ export class UserHistoryResolver { description: 'Listen for User History Creation', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userHistoryCreated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_history/${user.uid}/created`); @@ -124,6 +128,7 @@ export class UserHistoryResolver { description: 'Listen for User History update', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userHistoryUpdated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_history/${user.uid}/updated`); @@ -133,6 +138,7 @@ export class UserHistoryResolver { description: 'Listen for User History deletion', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userHistoryDeleted(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_history/${user.uid}/deleted`); @@ -142,6 +148,7 @@ export class UserHistoryResolver { description: 'Listen for User History deleted many', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userHistoryDeletedMany(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_history/${user.uid}/deleted_many`); diff --git a/packages/hoppscotch-backend/src/user-request/resolvers/user-request.resolver.ts b/packages/hoppscotch-backend/src/user-request/resolvers/user-request.resolver.ts index 412265250..06f76cab9 100644 --- a/packages/hoppscotch-backend/src/user-request/resolvers/user-request.resolver.ts +++ b/packages/hoppscotch-backend/src/user-request/resolvers/user-request.resolver.ts @@ -25,7 +25,10 @@ import { import { AuthUser } from 'src/types/AuthUser'; import { User } from 'src/user/user.model'; import { ReqType } from 'src/user-history/user-history.model'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => UserRequest) export class UserRequestResolver { constructor( @@ -232,6 +235,7 @@ export class UserRequestResolver { description: 'Listen for User Request Creation', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userRequestCreated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_request/${user.uid}/created`); @@ -241,6 +245,7 @@ export class UserRequestResolver { description: 'Listen for User Request Update', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userRequestUpdated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_request/${user.uid}/updated`); @@ -250,6 +255,7 @@ export class UserRequestResolver { description: 'Listen for User Request Deletion', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userRequestDeleted(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_request/${user.uid}/deleted`); @@ -259,6 +265,7 @@ export class UserRequestResolver { description: 'Listen for User Request Moved', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userRequestMoved(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_request/${user.uid}/moved`); diff --git a/packages/hoppscotch-backend/src/user-settings/user-settings.resolver.ts b/packages/hoppscotch-backend/src/user-settings/user-settings.resolver.ts index 772ddd797..58083b52e 100644 --- a/packages/hoppscotch-backend/src/user-settings/user-settings.resolver.ts +++ b/packages/hoppscotch-backend/src/user-settings/user-settings.resolver.ts @@ -9,7 +9,10 @@ import { UserSettings } from './user-settings.model'; import { UserSettingsService } from './user-settings.service'; import { PubSubService } from 'src/pubsub/pubsub.service'; import { AuthUser } from 'src/types/AuthUser'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver() export class UserSettingsResolver { constructor( @@ -63,6 +66,7 @@ export class UserSettingsResolver { description: 'Listen for user setting creation', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userSettingsCreated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_settings/${user.uid}/created`); @@ -72,6 +76,7 @@ export class UserSettingsResolver { description: 'Listen for user setting updates', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userSettingsUpdated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user_settings/${user.uid}/updated`); diff --git a/packages/hoppscotch-backend/src/user/user.resolver.ts b/packages/hoppscotch-backend/src/user/user.resolver.ts index 120c80221..c5b865957 100644 --- a/packages/hoppscotch-backend/src/user/user.resolver.ts +++ b/packages/hoppscotch-backend/src/user/user.resolver.ts @@ -10,7 +10,10 @@ import * as TE from 'fp-ts/TaskEither'; import { pipe } from 'fp-ts/function'; import { PubSubService } from 'src/pubsub/pubsub.service'; import { AuthUser } from 'src/types/AuthUser'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { SkipThrottle } from '@nestjs/throttler'; +@UseGuards(GqlThrottlerGuard) @Resolver(() => User) export class UserResolver { constructor( @@ -73,6 +76,7 @@ export class UserResolver { description: 'Listen for user updates', resolve: (value) => value, }) + @SkipThrottle() @UseGuards(GqlAuthGuard) userUpdated(@GqlUser() user: User) { return this.pubsub.asyncIterator(`user/${user.uid}/updated`);