Skip to content

Commit

Permalink
refactor(security): global error handler
Browse files Browse the repository at this point in the history
- Prevent exposing internal error details
- Remove duplicate code by applying guards and api doc decorators at
  controller level
  • Loading branch information
crazyoptimist committed Jun 18, 2024
1 parent e625bbc commit b4aeddc
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class AuthController {
}

@Post('logout')
@ApiBearerAuth()
@ApiResponse({ status: 201, description: 'Successful Logout' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
async logout(@Request() request: IRequest): Promise<any> {
Expand Down
22 changes: 18 additions & 4 deletions src/modules/common/exception-filter/all-exceptions.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,24 @@ export class AllExceptionsFilter implements ExceptionFilter {
statusCode = exception.getStatus();
responseBody = exception.getResponse() as ResponseBody;
} else if (exception instanceof QueryFailedError) {
statusCode = HttpStatus.BAD_REQUEST;
this.logger.error((exception as Error).stack);

const isDuplicateKeyError = exception.message?.includes(
'duplicate key value violates unique constraint',
);

statusCode = isDuplicateKeyError
? HttpStatus.BAD_REQUEST
: HttpStatus.INTERNAL_SERVER_ERROR;

const message = isDuplicateKeyError
? exception.message
: 'Service Unavailable';

responseBody = {
statusCode,
message: exception.message,
error: 'Database Query Failed Error',
message,
error: 'Query Failed Error',
};
} else if (exception instanceof EntityNotFoundError) {
statusCode = HttpStatus.NOT_FOUND;
Expand All @@ -51,10 +64,11 @@ export class AllExceptionsFilter implements ExceptionFilter {
};
} else {
this.logger.error((exception as Error).stack);

statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
responseBody = {
statusCode,
message: (exception as Error).message,
message: 'Service Unavailable',
error: 'Internal Server Error',
};
}
Expand Down
12 changes: 2 additions & 10 deletions src/modules/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ import { Action } from '../infrastructure/casl/action.enum';
import { User } from './user.entity';

@Controller('api/users')
@UseGuards(PoliciesGuard)
@ApiTags('users')
@ApiBearerAuth()
export class UserController {
constructor(private readonly userService: UserService) {}

@Post()
@UseGuards(PoliciesGuard)
@CheckPolicies((ability) => ability.can(Action.Create, User))
@ApiBearerAuth()
@ApiResponse({ status: 201, description: 'New User Created' })
@ApiResponse({ status: 400, description: 'Bad Request' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
Expand All @@ -44,9 +44,7 @@ export class UserController {
}

@Get()
@UseGuards(PoliciesGuard)
@CheckPolicies((ability) => ability.can(Action.Read, User))
@ApiBearerAuth()
@ApiResponse({ status: 200, description: 'All Users' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
async findAll(
Expand All @@ -69,9 +67,7 @@ export class UserController {
}

@Get(':id')
@UseGuards(PoliciesGuard)
@CheckPolicies((ability) => ability.can(Action.Read, User))
@ApiBearerAuth()
@ApiResponse({ status: 200, description: 'User For Given ID' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
@ApiResponse({ status: 404, description: 'Not Found' })
Expand All @@ -80,9 +76,7 @@ export class UserController {
}

@Patch(':id')
@UseGuards(PoliciesGuard)
@CheckPolicies((ability) => ability.can(Action.Update, User))
@ApiBearerAuth()
@ApiResponse({ status: 200, description: 'Successful Update' })
@ApiResponse({ status: 400, description: 'Bad Request' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
Expand All @@ -95,9 +89,7 @@ export class UserController {
}

@Delete(':id')
@UseGuards(PoliciesGuard)
@CheckPolicies((ability) => ability.can(Action.Delete, User))
@ApiBearerAuth()
@ApiResponse({ status: 200, description: 'Successful Delete' })
@ApiResponse({ status: 400, description: 'Bad Request' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
Expand Down

0 comments on commit b4aeddc

Please sign in to comment.