Skip to content

Commit

Permalink
Improve error handling in reputation oracle (humanprotocol#2018)
Browse files Browse the repository at this point in the history
  • Loading branch information
flopez7 authored May 16, 2024
1 parent 5a089fe commit 4a1e9e9
Show file tree
Hide file tree
Showing 49 changed files with 470 additions and 382 deletions.
4 changes: 2 additions & 2 deletions packages/apps/reputation-oracle/server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { KycModule } from './modules/kyc/kyc.module';
import { CronJobModule } from './modules/cron-job/cron-job.module';
import { PayoutModule } from './modules/payout/payout.module';
import { EnvConfigModule } from './common/config/config.module';
import { DatabaseExceptionFilter } from './common/exceptions/database.filter';
import { ExceptionFilter } from './common/exceptions/exception.filter';

@Module({
providers: [
Expand All @@ -32,7 +32,7 @@ import { DatabaseExceptionFilter } from './common/exceptions/database.filter';
},
{
provide: APP_FILTER,
useClass: DatabaseExceptionFilter,
useClass: ExceptionFilter,
},
],
imports: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { HttpStatus } from '@nestjs/common';

export class ControlledError extends Error {
status: HttpStatus;

constructor(message: string, status: HttpStatus, stack?: string) {
super(message);
this.name = this.constructor.name;
this.status = status;
if (stack) this.stack = stack;
else Error.captureStackTrace(this, this.constructor);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { QueryFailedError } from 'typeorm';
import { PostgresErrorCodes } from './database.enum';
import { PostgresErrorCodes } from '../enums/database';

export class DatabaseError extends Error {
constructor(message: string, stack: string) {
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
ArgumentsHost,
Catch,
ExceptionFilter as IExceptionFilter,
HttpStatus,
Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { DatabaseError } from '../errors/database';
import { ControlledError } from '../errors/controlled';

@Catch()
export class ExceptionFilter implements IExceptionFilter {
private logger = new Logger(ExceptionFilter.name);

catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();

let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal server error';

if (exception instanceof ControlledError) {
status = exception.status;
message = exception.message;

this.logger.error(`Reputation Oracle error: ${message}`, exception.stack);
} else if (exception instanceof DatabaseError) {
status = HttpStatus.UNPROCESSABLE_ENTITY;
message = `Database error: ${exception.message}`;

this.logger.error(message, exception.stack);
} else {
this.logger.error(
`Unhandled exception: ${exception.message}`,
exception.stack,
);
}

response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
message: message,
path: request.url,
});
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
CanActivate,
ExecutionContext,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ServerConfigService } from '../config/server-config.service';
import { ControlledError } from '../errors/controlled';

@Injectable()
export class CronAuthGuard implements CanActivate {
Expand All @@ -20,6 +21,6 @@ export class CronAuthGuard implements CanActivate {
return true;
}

throw new UnauthorizedException('Unauthorized');
throw new ControlledError('Unauthorized', HttpStatus.UNAUTHORIZED);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {
CanActivate,
ExecutionContext,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { ControlledError } from '../errors/controlled';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt-http') implements CanActivate {
Expand All @@ -27,7 +28,7 @@ export class JwtAuthGuard extends AuthGuard('jwt-http') implements CanActivate {
}

console.error(e);
throw new UnauthorizedException('Unauthorized');
throw new ControlledError('Unauthorized', HttpStatus.UNAUTHORIZED);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { ExecutionContext, HttpStatus } from '@nestjs/common';
import { SignatureAuthGuard } from './signature.auth';
import { verifySignature } from '../utils/signature';
import { ChainId, EscrowUtils } from '@human-protocol/sdk';
import { MOCK_ADDRESS } from '../../../test/constants';
import { Role } from '../enums/role';
import { ControlledError } from '../errors/controlled';

jest.mock('../../common/utils/signature');

Expand Down Expand Up @@ -81,14 +82,14 @@ describe('SignatureAuthGuard', () => {
(verifySignature as jest.Mock).mockReturnValue(false);

await expect(guard.canActivate(context as any)).rejects.toThrow(
UnauthorizedException,
new ControlledError('Unauthorized', HttpStatus.UNAUTHORIZED),
);
});

it('should throw unauthorized exception for unrecognized oracle type', async () => {
mockRequest.originalUrl = '/some/random/path';
await expect(guard.canActivate(context as any)).rejects.toThrow(
UnauthorizedException,
new ControlledError('Unauthorized', HttpStatus.UNAUTHORIZED),
);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {
CanActivate,
ExecutionContext,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { verifySignature } from '../utils/signature';
import { HEADER_SIGNATURE_KEY } from '../constants';
import { EscrowUtils } from '@human-protocol/sdk';
import { Role } from '../enums/role';
import { ControlledError } from '../errors/controlled';

@Injectable()
export class SignatureAuthGuard implements CanActivate {
Expand Down Expand Up @@ -46,6 +47,6 @@ export class SignatureAuthGuard implements CanActivate {
console.error(error);
}

throw new UnauthorizedException('Unauthorized');
throw new ControlledError('Unauthorized', HttpStatus.UNAUTHORIZED);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
BadRequestException,
HttpStatus,
Injectable,
PipeTransform,
ValidationError,
Expand All @@ -8,6 +9,7 @@ import {
} from '@nestjs/common';
import { ValidatePasswordDto } from '../../modules/auth/auth.dto';
import { ErrorAuth } from '../constants/errors';
import { ControlledError } from '../errors/controlled';

@Injectable()
export class HttpValidationPipe extends ValidationPipe {
Expand All @@ -28,7 +30,10 @@ export class HttpValidationPipe extends ValidationPipe {
export class PasswordValidationPipe implements PipeTransform {
transform(value: ValidatePasswordDto) {
if (!this.isValidPassword(value.password)) {
throw new BadRequestException(ErrorAuth.PasswordIsNotStrongEnough);
throw new ControlledError(
ErrorAuth.PasswordIsNotStrongEnough,
HttpStatus.BAD_REQUEST,
);
}

return value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Readable } from 'stream';
import { ErrorManifest } from '../constants/errors';
import { CvatManifestDto, FortuneManifestDto } from '../dto/manifest';
import { JobRequestType } from '../enums';
import { ControlledError } from '../errors/controlled';
import { HttpStatus } from '@nestjs/common';

export function hashStream(stream: Readable): Promise<string> {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -32,7 +34,10 @@ export function getRequestType(
null;

if (!requestType) {
throw new Error(ErrorManifest.UnsupportedManifestType);
throw new ControlledError(
ErrorManifest.UnsupportedManifestType,
HttpStatus.BAD_REQUEST,
);
}

return requestType;
Expand Down
Loading

0 comments on commit 4a1e9e9

Please sign in to comment.