From a7369af8384fbde1618848baa39b85304f3966ee Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Tue, 6 Aug 2024 15:05:45 +0200 Subject: [PATCH] Use filters --- .../engine/core-modules/auth/auth.resolver.ts | 264 +++++++----------- .../google-apis-auth.controller.ts | 11 +- .../controllers/google-auth.controller.ts | 17 +- .../controllers/microsoft-auth.controller.ts | 11 +- .../controllers/verify-auth.controller.ts | 8 +- .../auth-graphql-api-exception.filter.ts} | 21 +- .../filters/auth-rest-api-exception.filter.ts | 33 +++ 7 files changed, 180 insertions(+), 185 deletions(-) rename packages/twenty-server/src/engine/core-modules/auth/{utils/auth-graphql-api-exception-handler.util.ts => filters/auth-graphql-api-exception.filter.ts} (58%) create mode 100644 packages/twenty-server/src/engine/core-modules/auth/filters/auth-rest-api-exception.filter.ts diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index abbe55077dec9..06c8f100e5d4a 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -1,9 +1,7 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - import { ApiKeyTokenInput } from 'src/engine/core-modules/auth/dto/api-key-token.input'; import { AppTokenInput } from 'src/engine/core-modules/auth/dto/app-token.input'; import { AuthorizeApp } from 'src/engine/core-modules/auth/dto/authorize-app.entity'; @@ -18,7 +16,7 @@ import { TransientToken } from 'src/engine/core-modules/auth/dto/transient-token import { UpdatePasswordViaResetTokenInput } from 'src/engine/core-modules/auth/dto/update-password-via-reset-token.input'; import { ValidatePasswordResetToken } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.entity'; import { ValidatePasswordResetTokenInput } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.input'; -import { authGraphqlApiExceptionHandler } from 'src/engine/core-modules/auth/utils/auth-graphql-api-exception-handler.util'; +import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; import { UserService } from 'src/engine/core-modules/user/services/user.service'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -42,10 +40,10 @@ import { AuthService } from './services/auth.service'; import { TokenService } from './services/token.service'; @Resolver() +@UseFilters(AuthGraphqlApiExceptionFilter) export class AuthResolver { constructor( @InjectRepository(Workspace, 'core') - private readonly workspaceRepository: Repository, private authService: AuthService, private tokenService: TokenService, private userService: UserService, @@ -55,89 +53,63 @@ export class AuthResolver { @Query(() => UserExists) async checkUserExists( @Args() checkUserExistsInput: CheckUserExistsInput, - ): Promise { - try { - const { exists } = await this.authService.checkUserExists( - checkUserExistsInput.email, - ); - - return { exists }; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + const { exists } = await this.authService.checkUserExists( + checkUserExistsInput.email, + ); + + return { exists }; } @Query(() => WorkspaceInviteHashValid) async checkWorkspaceInviteHashIsValid( @Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput, - ): Promise { - try { - return await this.authService.checkWorkspaceInviteHashIsValid( - workspaceInviteHashValidInput.inviteHash, - ); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + return await this.authService.checkWorkspaceInviteHashIsValid( + workspaceInviteHashValidInput.inviteHash, + ); } @Query(() => Workspace) async findWorkspaceFromInviteHash( @Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput, - ): Promise { - try { - return await this.authService.findWorkspaceFromInviteHash( - workspaceInviteHashValidInput.inviteHash, - ); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + return await this.authService.findWorkspaceFromInviteHash( + workspaceInviteHashValidInput.inviteHash, + ); } @UseGuards(CaptchaGuard) @Mutation(() => LoginToken) - async challenge( - @Args() challengeInput: ChallengeInput, - ): Promise { - try { - const user = await this.authService.challenge(challengeInput); - const loginToken = await this.tokenService.generateLoginToken(user.email); - - return { loginToken }; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + async challenge(@Args() challengeInput: ChallengeInput): Promise { + const user = await this.authService.challenge(challengeInput); + const loginToken = await this.tokenService.generateLoginToken(user.email); + + return { loginToken }; } @UseGuards(CaptchaGuard) @Mutation(() => LoginToken) - async signUp(@Args() signUpInput: SignUpInput): Promise { - try { - const user = await this.authService.signInUp({ - ...signUpInput, - fromSSO: false, - }); - - const loginToken = await this.tokenService.generateLoginToken(user.email); - - return { loginToken }; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + async signUp(@Args() signUpInput: SignUpInput): Promise { + const user = await this.authService.signInUp({ + ...signUpInput, + fromSSO: false, + }); + + const loginToken = await this.tokenService.generateLoginToken(user.email); + + return { loginToken }; } @Mutation(() => ExchangeAuthCode) async exchangeAuthorizationCode( @Args() exchangeAuthCodeInput: ExchangeAuthCodeInput, ) { - try { - const tokens = await this.tokenService.verifyAuthorizationCode( - exchangeAuthCodeInput, - ); - - return tokens; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + const tokens = await this.tokenService.verifyAuthorizationCode( + exchangeAuthCodeInput, + ); + + return tokens; } @Mutation(() => TransientToken) @@ -145,37 +117,29 @@ export class AuthResolver { async generateTransientToken( @AuthUser() user: User, ): Promise { - try { - const workspaceMember = await this.userService.loadWorkspaceMember(user); - - if (!workspaceMember) { - return; - } - const transientToken = await this.tokenService.generateTransientToken( - workspaceMember.id, - user.id, - user.defaultWorkspace.id, - ); - - return { transientToken }; - } catch (error) { - authGraphqlApiExceptionHandler(error); + const workspaceMember = await this.userService.loadWorkspaceMember(user); + + if (!workspaceMember) { + return; } + const transientToken = await this.tokenService.generateTransientToken( + workspaceMember.id, + user.id, + user.defaultWorkspace.id, + ); + + return { transientToken }; } @Mutation(() => Verify) - async verify(@Args() verifyInput: VerifyInput): Promise { - try { - const email = await this.tokenService.verifyLoginToken( - verifyInput.loginToken, - ); + async verify(@Args() verifyInput: VerifyInput): Promise { + const email = await this.tokenService.verifyLoginToken( + verifyInput.loginToken, + ); - const result = await this.authService.verify(email); + const result = await this.authService.verify(email); - return result; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + return result; } @Mutation(() => AuthorizeApp) @@ -183,17 +147,13 @@ export class AuthResolver { async authorizeApp( @Args() authorizeAppInput: AuthorizeAppInput, @AuthUser() user: User, - ): Promise { - try { - const authorizedApp = await this.authService.generateAuthorizationCode( - authorizeAppInput, - user, - ); - - return authorizedApp; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + const authorizedApp = await this.authService.generateAuthorizationCode( + authorizeAppInput, + user, + ); + + return authorizedApp; } @Mutation(() => AuthTokens) @@ -201,30 +161,22 @@ export class AuthResolver { async generateJWT( @AuthUser() user: User, @Args() args: GenerateJwtInput, - ): Promise { - try { - const token = await this.tokenService.generateSwitchWorkspaceToken( - user, - args.workspaceId, - ); - - return token; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + const token = await this.tokenService.generateSwitchWorkspaceToken( + user, + args.workspaceId, + ); + + return token; } @Mutation(() => AuthTokens) - async renewToken(@Args() args: AppTokenInput): Promise { - try { - const tokens = await this.tokenService.generateTokensFromRefreshToken( - args.appToken, - ); - - return { tokens: tokens }; - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + async renewToken(@Args() args: AppTokenInput): Promise { + const tokens = await this.tokenService.generateTokensFromRefreshToken( + args.appToken, + ); + + return { tokens: tokens }; } @UseGuards(JwtAuthGuard) @@ -232,12 +184,8 @@ export class AuthResolver { async impersonate( @Args() impersonateInput: ImpersonateInput, @AuthUser() user: User, - ): Promise { - try { - return await this.authService.impersonate(impersonateInput.userId, user); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + return await this.authService.impersonate(impersonateInput.userId, user); } @UseGuards(JwtAuthGuard) @@ -246,62 +194,46 @@ export class AuthResolver { @Args() args: ApiKeyTokenInput, @AuthWorkspace() { id: workspaceId }: Workspace, ): Promise { - try { - return await this.tokenService.generateApiKeyToken( - workspaceId, - args.apiKeyId, - args.expiresAt, - ); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + return await this.tokenService.generateApiKeyToken( + workspaceId, + args.apiKeyId, + args.expiresAt, + ); } @Mutation(() => EmailPasswordResetLink) async emailPasswordResetLink( @Args() emailPasswordResetInput: EmailPasswordResetLinkInput, - ): Promise { - try { - const resetToken = await this.tokenService.generatePasswordResetToken( - emailPasswordResetInput.email, - ); - - return await this.tokenService.sendEmailPasswordResetLink( - resetToken, - emailPasswordResetInput.email, - ); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + const resetToken = await this.tokenService.generatePasswordResetToken( + emailPasswordResetInput.email, + ); + + return await this.tokenService.sendEmailPasswordResetLink( + resetToken, + emailPasswordResetInput.email, + ); } @Mutation(() => InvalidatePassword) async updatePasswordViaResetToken( @Args() { passwordResetToken, newPassword }: UpdatePasswordViaResetTokenInput, - ): Promise { - try { - const { id } = - await this.tokenService.validatePasswordResetToken(passwordResetToken); + ): Promise { + const { id } = + await this.tokenService.validatePasswordResetToken(passwordResetToken); - await this.authService.updatePassword(id, newPassword); + await this.authService.updatePassword(id, newPassword); - return await this.tokenService.invalidatePasswordResetToken(id); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + return await this.tokenService.invalidatePasswordResetToken(id); } @Query(() => ValidatePasswordResetToken) async validatePasswordResetToken( @Args() args: ValidatePasswordResetTokenInput, - ): Promise { - try { - return this.tokenService.validatePasswordResetToken( - args.passwordResetToken, - ); - } catch (error) { - authGraphqlApiExceptionHandler(error); - } + ): Promise { + return this.tokenService.validatePasswordResetToken( + args.passwordResetToken, + ); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts index a2baa0a90445a..de2fe47504b4d 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts @@ -1,4 +1,11 @@ -import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; +import { + Controller, + Get, + Req, + Res, + UseFilters, + UseGuards, +} from '@nestjs/common'; import { Response } from 'express'; @@ -6,6 +13,7 @@ import { AuthException, AuthExceptionCode, } from 'src/engine/core-modules/auth/auth.exception'; +import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter'; import { GoogleAPIsOauthExchangeCodeForTokenGuard } from 'src/engine/core-modules/auth/guards/google-apis-oauth-exchange-code-for-token.guard'; import { GoogleAPIsOauthRequestCodeGuard } from 'src/engine/core-modules/auth/guards/google-apis-oauth-request-code.guard'; import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google-apis.service'; @@ -16,6 +24,7 @@ import { EnvironmentService } from 'src/engine/integrations/environment/environm import { LoadServiceWithWorkspaceContext } from 'src/engine/twenty-orm/context/load-service-with-workspace.context'; @Controller('auth/google-apis') +@UseFilters(AuthRestApiExceptionFilter) export class GoogleAPIsAuthController { constructor( private readonly googleAPIsService: GoogleAPIsService, diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts index 28943d2e75dbe..42674953ed313 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-auth.controller.ts @@ -1,14 +1,23 @@ -import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; +import { + Controller, + Get, + Req, + Res, + UseFilters, + UseGuards, +} from '@nestjs/common'; import { Response } from 'express'; -import { GoogleRequest } from 'src/engine/core-modules/auth/strategies/google.auth.strategy'; -import { TokenService } from 'src/engine/core-modules/auth/services/token.service'; -import { GoogleProviderEnabledGuard } from 'src/engine/core-modules/auth/guards/google-provider-enabled.guard'; +import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter'; import { GoogleOauthGuard } from 'src/engine/core-modules/auth/guards/google-oauth.guard'; +import { GoogleProviderEnabledGuard } from 'src/engine/core-modules/auth/guards/google-provider-enabled.guard'; import { AuthService } from 'src/engine/core-modules/auth/services/auth.service'; +import { TokenService } from 'src/engine/core-modules/auth/services/token.service'; +import { GoogleRequest } from 'src/engine/core-modules/auth/strategies/google.auth.strategy'; @Controller('auth/google') +@UseFilters(AuthRestApiExceptionFilter) export class GoogleAuthController { constructor( private readonly tokenService: TokenService, diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts index 798f8ed184198..62f3364b6bcab 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-auth.controller.ts @@ -1,8 +1,16 @@ -import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; +import { + Controller, + Get, + Req, + Res, + UseFilters, + UseGuards, +} from '@nestjs/common'; import { Response } from 'express'; import { TypeORMService } from 'src/database/typeorm/typeorm.service'; +import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter'; import { MicrosoftOAuthGuard } from 'src/engine/core-modules/auth/guards/microsoft-oauth.guard'; import { MicrosoftProviderEnabledGuard } from 'src/engine/core-modules/auth/guards/microsoft-provider-enabled.guard'; import { AuthService } from 'src/engine/core-modules/auth/services/auth.service'; @@ -10,6 +18,7 @@ import { TokenService } from 'src/engine/core-modules/auth/services/token.servic import { MicrosoftRequest } from 'src/engine/core-modules/auth/strategies/microsoft.auth.strategy'; @Controller('auth/microsoft') +@UseFilters(AuthRestApiExceptionFilter) export class MicrosoftAuthController { constructor( private readonly tokenService: TokenService, diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/verify-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/verify-auth.controller.ts index 68d680845011a..40869c5f7db4b 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/verify-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/verify-auth.controller.ts @@ -1,11 +1,13 @@ -import { Body, Controller, Post } from '@nestjs/common'; +import { Body, Controller, Post, UseFilters } from '@nestjs/common'; -import { AuthService } from 'src/engine/core-modules/auth/services/auth.service'; -import { VerifyInput } from 'src/engine/core-modules/auth/dto/verify.input'; import { Verify } from 'src/engine/core-modules/auth/dto/verify.entity'; +import { VerifyInput } from 'src/engine/core-modules/auth/dto/verify.input'; +import { AuthRestApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-rest-api-exception.filter'; +import { AuthService } from 'src/engine/core-modules/auth/services/auth.service'; import { TokenService } from 'src/engine/core-modules/auth/services/token.service'; @Controller('auth/verify') +@UseFilters(AuthRestApiExceptionFilter) export class VerifyAuthController { constructor( private readonly authService: AuthService, diff --git a/packages/twenty-server/src/engine/core-modules/auth/utils/auth-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts similarity index 58% rename from packages/twenty-server/src/engine/core-modules/auth/utils/auth-graphql-api-exception-handler.util.ts rename to packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts index 81c52945a769a..ebabba88f1270 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/utils/auth-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts @@ -1,3 +1,5 @@ +import { Catch } from '@nestjs/common'; + import { AuthException, AuthExceptionCode, @@ -9,22 +11,21 @@ import { UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; -export const authGraphqlApiExceptionHandler = (error: Error) => { - if (error instanceof AuthException) { - switch (error.code) { +@Catch(AuthException) +export class AuthGraphqlApiExceptionFilter { + catch(exception: AuthException) { + switch (exception.code) { case AuthExceptionCode.USER_NOT_FOUND: case AuthExceptionCode.CLIENT_NOT_FOUND: - throw new NotFoundError(error.message); + throw new NotFoundError(exception.message); case AuthExceptionCode.INVALID_INPUT: - throw new UserInputError(error.message); + throw new UserInputError(exception.message); case AuthExceptionCode.FORBIDDEN_EXCEPTION: - throw new ForbiddenError(error.message); + throw new ForbiddenError(exception.message); case AuthExceptionCode.INVALID_DATA: case AuthExceptionCode.INTERNAL_SERVER_ERROR: default: - throw new InternalServerError(error.message); + throw new InternalServerError(exception.message); } } - - throw error; -}; +} diff --git a/packages/twenty-server/src/engine/core-modules/auth/filters/auth-rest-api-exception.filter.ts b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-rest-api-exception.filter.ts new file mode 100644 index 0000000000000..6d29e997bda84 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-rest-api-exception.filter.ts @@ -0,0 +1,33 @@ +import { + ArgumentsHost, + BadRequestException, + Catch, + ExceptionFilter, + InternalServerErrorException, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; + +import { + AuthException, + AuthExceptionCode, +} from 'src/engine/core-modules/auth/auth.exception'; + +@Catch(AuthException) +export class AuthRestApiExceptionFilter implements ExceptionFilter { + catch(exception: AuthException, _: ArgumentsHost) { + switch (exception.code) { + case AuthExceptionCode.USER_NOT_FOUND: + case AuthExceptionCode.CLIENT_NOT_FOUND: + throw new NotFoundException(exception.message); + case AuthExceptionCode.INVALID_INPUT: + throw new BadRequestException(exception.message); + case AuthExceptionCode.FORBIDDEN_EXCEPTION: + throw new UnauthorizedException(exception.message); + case AuthExceptionCode.INVALID_DATA: + case AuthExceptionCode.INTERNAL_SERVER_ERROR: + default: + throw new InternalServerErrorException(exception.message); + } + } +}