-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Build exceptions and handler #6459
Changes from 5 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { CustomException } from 'src/utils/custom-exception'; | ||
|
||
export class AuthException extends CustomException { | ||
code: AuthExceptionCode; | ||
constructor(message: string, code: AuthExceptionCode) { | ||
super(message, code); | ||
} | ||
} | ||
|
||
export enum AuthExceptionCode { | ||
USER_NOT_FOUND = 'USER_NOT_FOUND', | ||
CLIENT_NOT_FOUND = 'CLIENT_NOT_FOUND', | ||
INVALID_INPUT = 'INVALID_INPUT', | ||
FORBIDDEN_EXCEPTION = 'FORBIDDEN_EXCEPTION', | ||
INVALID_DATA = 'INVALID_DATA', | ||
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,7 @@ | ||
import { | ||
BadRequestException, | ||
ForbiddenException, | ||
InternalServerErrorException, | ||
NotFoundException, | ||
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'; | ||
|
@@ -24,14 +16,14 @@ 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 { 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'; | ||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; | ||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; | ||
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard'; | ||
import { CaptchaGuard } from 'src/engine/integrations/captcha/captcha.guard'; | ||
import { assert } from 'src/utils/assert'; | ||
|
||
import { ChallengeInput } from './dto/challenge.input'; | ||
import { ImpersonateInput } from './dto/impersonate.input'; | ||
|
@@ -48,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<Workspace>, | ||
private authService: AuthService, | ||
private tokenService: TokenService, | ||
private userService: UserService, | ||
|
@@ -81,16 +73,10 @@ export class AuthResolver { | |
@Query(() => Workspace) | ||
async findWorkspaceFromInviteHash( | ||
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput, | ||
) { | ||
const workspace = await this.workspaceRepository.findOneBy({ | ||
inviteHash: workspaceInviteHashValidInput.inviteHash, | ||
}); | ||
|
||
if (!workspace) { | ||
throw new BadRequestException('Workspace does not exist'); | ||
} | ||
|
||
return workspace; | ||
): Promise<Workspace> { | ||
return await this.authService.findWorkspaceFromInviteHashOrFail( | ||
workspaceInviteHashValidInput.inviteHash, | ||
); | ||
} | ||
|
||
@UseGuards(CaptchaGuard) | ||
|
@@ -151,8 +137,6 @@ export class AuthResolver { | |
verifyInput.loginToken, | ||
); | ||
|
||
assert(email, 'Invalid token', ForbiddenException); | ||
|
||
const result = await this.authService.verify(email); | ||
|
||
return result; | ||
|
@@ -188,10 +172,6 @@ export class AuthResolver { | |
|
||
@Mutation(() => AuthTokens) | ||
async renewToken(@Args() args: AppTokenInput): Promise<AuthTokens> { | ||
if (!args.appToken) { | ||
throw new BadRequestException('Refresh token is mendatory'); | ||
} | ||
|
||
const tokens = await this.tokenService.generateTokensFromRefreshToken( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
args.appToken, | ||
); | ||
|
@@ -205,10 +185,7 @@ export class AuthResolver { | |
@Args() impersonateInput: ImpersonateInput, | ||
@AuthUser() user: User, | ||
): Promise<Verify> { | ||
// Check if user can impersonate | ||
assert(user.canImpersonate, 'User cannot impersonate', ForbiddenException); | ||
|
||
return this.authService.impersonate(impersonateInput.userId); | ||
return await this.authService.impersonate(impersonateInput.userId, user); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
|
||
@UseGuards(JwtAuthGuard) | ||
|
@@ -240,20 +217,13 @@ export class AuthResolver { | |
|
||
@Mutation(() => InvalidatePassword) | ||
async updatePasswordViaResetToken( | ||
@Args() args: UpdatePasswordViaResetTokenInput, | ||
@Args() | ||
{ passwordResetToken, newPassword }: UpdatePasswordViaResetTokenInput, | ||
): Promise<InvalidatePassword> { | ||
const { id } = await this.tokenService.validatePasswordResetToken( | ||
args.passwordResetToken, | ||
); | ||
|
||
assert(id, 'User not found', NotFoundException); | ||
|
||
const { success } = await this.authService.updatePassword( | ||
id, | ||
args.newPassword, | ||
); | ||
const { id } = | ||
await this.tokenService.validatePasswordResetToken(passwordResetToken); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
assert(success, 'Password update failed', InternalServerErrorException); | ||
await this.authService.updatePassword(id, newPassword); | ||
|
||
return await this.tokenService.invalidatePasswordResetToken(id); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Catch } from '@nestjs/common'; | ||
|
||
import { | ||
AuthException, | ||
AuthExceptionCode, | ||
} from 'src/engine/core-modules/auth/auth.exception'; | ||
import { | ||
ForbiddenError, | ||
InternalServerError, | ||
NotFoundError, | ||
UserInputError, | ||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; | ||
|
||
@Catch(AuthException) | ||
export class AuthGraphqlApiExceptionFilter { | ||
catch(exception: AuthException) { | ||
switch (exception.code) { | ||
case AuthExceptionCode.USER_NOT_FOUND: | ||
case AuthExceptionCode.CLIENT_NOT_FOUND: | ||
throw new NotFoundError(exception.message); | ||
case AuthExceptionCode.INVALID_INPUT: | ||
throw new UserInputError(exception.message); | ||
case AuthExceptionCode.FORBIDDEN_EXCEPTION: | ||
throw new ForbiddenError(exception.message); | ||
case AuthExceptionCode.INVALID_DATA: | ||
case AuthExceptionCode.INTERNAL_SERVER_ERROR: | ||
default: | ||
throw new InternalServerError(exception.message); | ||
thomtrp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
thomtrp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the check to the function bellow