diff --git a/packages/apps/human-app/server/jest.config.ts b/packages/apps/human-app/server/jest.config.ts index ce3be09dd0..b4808588b1 100644 --- a/packages/apps/human-app/server/jest.config.ts +++ b/packages/apps/human-app/server/jest.config.ts @@ -1,15 +1,17 @@ process.env['GIT_HASH'] = 'test_value_hardcoded_in_jest_config'; +import { createDefaultPreset } from 'ts-jest'; + +const jestTsPreset = createDefaultPreset({}); + module.exports = { + ...jestTsPreset, coverageDirectory: '../coverage', collectCoverageFrom: ['**/*.(t|j)s'], moduleFileExtensions: ['js', 'json', 'ts'], rootDir: 'src', testEnvironment: 'node', testRegex: '.*\\.spec\\.ts$', - transform: { - '^.+\\.(t|j)s$': 'ts-jest', - }, moduleNameMapper: { '^uuid$': require.resolve('uuid'), }, diff --git a/packages/apps/human-app/server/scripts/generate-env-doc.ts b/packages/apps/human-app/server/scripts/generate-env-doc.ts index 2b1817c9ce..d477e8d47c 100644 --- a/packages/apps/human-app/server/scripts/generate-env-doc.ts +++ b/packages/apps/human-app/server/scripts/generate-env-doc.ts @@ -112,7 +112,6 @@ function processConfigFiles() { 'common-config.module.ts', 'gateway-config.service.ts', 'gateway-config.types.ts', - 'params-decorators.ts', 'spec', ].includes(file), ); diff --git a/packages/apps/human-app/server/src/app.controller.ts b/packages/apps/human-app/server/src/app.controller.ts index f6cc9bb67c..bfe63b0c9c 100644 --- a/packages/apps/human-app/server/src/app.controller.ts +++ b/packages/apps/human-app/server/src/app.controller.ts @@ -1,9 +1,11 @@ import { Controller, Get, Redirect } from '@nestjs/common'; import { ApiExcludeEndpoint } from '@nestjs/swagger'; +import { Public } from './common/decorators'; @Controller() export class AppController { @Get('/') + @Public() @Redirect('/swagger', 301) @ApiExcludeEndpoint() public swagger(): string { diff --git a/packages/apps/human-app/server/src/app.module.ts b/packages/apps/human-app/server/src/app.module.ts index 37292b384a..6099bfa28e 100644 --- a/packages/apps/human-app/server/src/app.module.ts +++ b/packages/apps/human-app/server/src/app.module.ts @@ -1,51 +1,54 @@ -import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { ConfigModule } from '@nestjs/config'; -import { HttpModule } from '@nestjs/axios'; -import { WorkerModule } from './modules/user-worker/worker.module'; -import { ReputationOracleModule } from './integrations/reputation-oracle/reputation-oracle.module'; -import { AutomapperModule } from '@automapper/nestjs'; import { classes } from '@automapper/classes'; -import { OperatorModule } from './modules/user-operator/operator.module'; -import { OperatorController } from './modules/user-operator/operator.controller'; -import { WorkerController } from './modules/user-worker/worker.controller'; -import { CommonConfigModule } from './common/config/common-config.module'; -import { CacheFactoryConfig } from './common/config/cache-factory.config'; +import { AutomapperModule } from '@automapper/nestjs'; +import { ChainId } from '@human-protocol/sdk'; +import { HttpModule } from '@nestjs/axios'; import { CacheModule } from '@nestjs/cache-manager'; -import { OracleDiscoveryController } from './modules/oracle-discovery/oracle-discovery.controller'; -import { OracleDiscoveryModule } from './modules/oracle-discovery/oracle-discovery.module'; -import { JobsDiscoveryModule } from './modules/jobs-discovery/jobs-discovery.module'; -import { JobsDiscoveryController } from './modules/jobs-discovery/jobs-discovery.controller'; -import { JobAssignmentController } from './modules/job-assignment/job-assignment.controller'; -import { JobAssignmentModule } from './modules/job-assignment/job-assignment.module'; -import { StatisticsModule } from './modules/statistics/statistics.module'; -import { StatisticsController } from './modules/statistics/statistics.controller'; +import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { APP_GUARD } from '@nestjs/core'; +import Joi from 'joi'; +import { AppController } from './app.controller'; +import { CacheFactoryConfig } from './common/config/cache-factory.config'; +import { CommonConfigModule } from './common/config/common-config.module'; +import { EnvironmentConfigService } from './common/config/environment-config.service'; +import { JwtAuthGuard } from './common/guards/jwt.auth'; +import { JwtHttpStrategy } from './common/guards/strategy'; +import { InterceptorModule } from './common/interceptors/interceptor.module'; +import { ForbidUnauthorizedHostMiddleware } from './common/middleware/host-check.middleware'; +import { EscrowUtilsModule } from './integrations/escrow/escrow-utils.module'; import { ExchangeOracleModule } from './integrations/exchange-oracle/exchange-oracle.module'; +import { HCaptchaLabelingModule } from './integrations/h-captcha-labeling/h-captcha-labeling.module'; import { KvStoreModule } from './integrations/kv-store/kv-store.module'; +import { ReputationOracleModule } from './integrations/reputation-oracle/reputation-oracle.module'; +import { AbuseController } from './modules/abuse/abuse.controller'; +import { AbuseModule } from './modules/abuse/abuse.module'; +import { CronJobModule } from './modules/cron-job/cron-job.module'; import { EmailConfirmationModule } from './modules/email-confirmation/email-confirmation.module'; -import { PasswordResetModule } from './modules/password-reset/password-reset.module'; +import { HCaptchaController } from './modules/h-captcha/h-captcha.controller'; +import { HCaptchaModule } from './modules/h-captcha/h-captcha.module'; +import { HealthModule } from './modules/health/health.module'; +import { JobAssignmentController } from './modules/job-assignment/job-assignment.controller'; +import { JobAssignmentModule } from './modules/job-assignment/job-assignment.module'; +import { JobsDiscoveryController } from './modules/jobs-discovery/jobs-discovery.controller'; +import { JobsDiscoveryModule } from './modules/jobs-discovery/jobs-discovery.module'; import { KycProcedureModule } from './modules/kyc-procedure/kyc-procedure.module'; +import { NDAController } from './modules/nda/nda.controller'; +import { NDAModule } from './modules/nda/nda.module'; +import { OracleDiscoveryController } from './modules/oracle-discovery/oracle-discovery.controller'; +import { OracleDiscoveryModule } from './modules/oracle-discovery/oracle-discovery.module'; +import { PasswordResetModule } from './modules/password-reset/password-reset.module'; import { PrepareSignatureModule } from './modules/prepare-signature/prepare-signature.module'; -import { HCaptchaModule } from './modules/h-captcha/h-captcha.module'; -import { HCaptchaLabelingModule } from './integrations/h-captcha-labeling/h-captcha-labeling.module'; -import { HCaptchaController } from './modules/h-captcha/h-captcha.controller'; -import { EscrowUtilsModule } from './integrations/escrow/escrow-utils.module'; -import Joi from 'joi'; -import { ChainId } from '@human-protocol/sdk'; import { RegisterAddressController } from './modules/register-address/register-address.controller'; import { RegisterAddressModule } from './modules/register-address/register-address.module'; -import { InterceptorModule } from './common/interceptors/interceptor.module'; -import { TokenRefreshModule } from './modules/token-refresh/token-refresh.module'; +import { StatisticsController } from './modules/statistics/statistics.controller'; +import { StatisticsModule } from './modules/statistics/statistics.module'; import { TokenRefreshController } from './modules/token-refresh/token-refresh.controller'; -import { CronJobModule } from './modules/cron-job/cron-job.module'; -import { EnvironmentConfigService } from './common/config/environment-config.service'; -import { ForbidUnauthorizedHostMiddleware } from './common/middleware/host-check.middleware'; -import { HealthModule } from './modules/health/health.module'; +import { TokenRefreshModule } from './modules/token-refresh/token-refresh.module'; import { UiConfigurationModule } from './modules/ui-configuration/ui-configuration.module'; -import { NDAModule } from './modules/nda/nda.module'; -import { NDAController } from './modules/nda/nda.controller'; -import { AbuseController } from './modules/abuse/abuse.controller'; -import { AbuseModule } from './modules/abuse/abuse.module'; +import { OperatorController } from './modules/user-operator/operator.controller'; +import { OperatorModule } from './modules/user-operator/operator.module'; +import { WorkerController } from './modules/user-worker/worker.controller'; +import { WorkerModule } from './modules/user-worker/worker.module'; const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false'); @@ -146,7 +149,14 @@ const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false'); AbuseController, ], exports: [HttpModule], - providers: [EnvironmentConfigService], + providers: [ + EnvironmentConfigService, + JwtHttpStrategy, + { + provide: APP_GUARD, + useClass: JwtAuthGuard, + }, + ], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { diff --git a/packages/apps/human-app/server/src/common/config/params-decorators.ts b/packages/apps/human-app/server/src/common/config/params-decorators.ts deleted file mode 100644 index 52f2e46b90..0000000000 --- a/packages/apps/human-app/server/src/common/config/params-decorators.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - BadRequestException, - createParamDecorator, - ExecutionContext, - UnauthorizedException, - Logger, -} from '@nestjs/common'; -import { jwtDecode } from 'jwt-decode'; -import { JwtUserData } from '../utils/jwt-token.model'; - -const logger = new Logger('JwtPayloadDecorator'); - -export const Authorization = createParamDecorator( - (_data: unknown, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest(); - const token = request.headers['authorization']; - if (token) { - return token; - } - throw new UnauthorizedException(); - }, -); - -export const JwtPayload = createParamDecorator( - (_data: unknown, ctx: ExecutionContext): any => { - const request = ctx.switchToHttp().getRequest(); - const token = request.headers['authorization']?.split(' ')[1]; - if (!token) { - throw new UnauthorizedException(); - } - try { - const decoded = jwtDecode(token); - return decoded as JwtUserData; - } catch (error) { - logger.error(`Error in decoding token: ${token}`, error); - throw new BadRequestException(); - } - }, -); diff --git a/packages/apps/human-app/server/src/common/constants/cache.ts b/packages/apps/human-app/server/src/common/constants/cache.ts index 9e3d3a6931..004f9bf37b 100644 --- a/packages/apps/human-app/server/src/common/constants/cache.ts +++ b/packages/apps/human-app/server/src/common/constants/cache.ts @@ -4,3 +4,4 @@ export const ORACLE_URL_CACHE_KEY = 'oracle:url'; export const DAILY_HMT_SPENT_CACHE_KEY = 'daily:hmt-spent'; export const ORACLE_STATISTICS_CACHE_KEY = 'statistics:oracle'; export const WORKER_STATISTICS_CACHE_KEY = 'statistics:worker'; +export const REPUTATION_ORACLE_PUBLIC_KEY = 'reputation:pubkey'; diff --git a/packages/apps/human-app/server/src/common/constants/index.ts b/packages/apps/human-app/server/src/common/constants/index.ts new file mode 100644 index 0000000000..7de7973ce8 --- /dev/null +++ b/packages/apps/human-app/server/src/common/constants/index.ts @@ -0,0 +1 @@ +export const JWT_KVSTORE_KEY = 'jwt_public_key'; diff --git a/packages/apps/human-app/server/src/common/decorators/index.ts b/packages/apps/human-app/server/src/common/decorators/index.ts index 14790857ae..fec5f72991 100644 --- a/packages/apps/human-app/server/src/common/decorators/index.ts +++ b/packages/apps/human-app/server/src/common/decorators/index.ts @@ -1 +1,12 @@ +import { Reflector } from '@nestjs/core'; + export * from './enums'; + +/** + * Decorator for HTTP endpoints to bypass JWT auth guard + * where JWT auth not needed + */ +export const Public = Reflector.createDecorator({ + key: 'isPublic', + transform: () => true, +}); diff --git a/packages/apps/human-app/server/src/common/guards/jwt.auth.ts b/packages/apps/human-app/server/src/common/guards/jwt.auth.ts new file mode 100644 index 0000000000..0dec177d5b --- /dev/null +++ b/packages/apps/human-app/server/src/common/guards/jwt.auth.ts @@ -0,0 +1,40 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { JwtUserData } from '../utils/jwt-token.model'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt-http') implements CanActivate { + constructor(private readonly reflector: Reflector) { + super(); + } + + public async canActivate(context: ExecutionContext): Promise { + // Check for public routes first + const isPublic = this.reflector.getAllAndOverride('isPublic', [ + context.getHandler(), + context.getClass(), + ]); + + if (isPublic) { + return true; + } + + // Try to authenticate with JWT + await super.canActivate(context); + + const request = context.switchToHttp().getRequest(); + const user = request.user as JwtUserData; + if (!user) { + throw new UnauthorizedException('User not found in request'); + } + request.token = request.headers['authorization']; + + return true; + } +} diff --git a/packages/apps/human-app/server/src/common/guards/strategy/index.ts b/packages/apps/human-app/server/src/common/guards/strategy/index.ts new file mode 100644 index 0000000000..e38b86c866 --- /dev/null +++ b/packages/apps/human-app/server/src/common/guards/strategy/index.ts @@ -0,0 +1 @@ +export * from './jwt.http'; diff --git a/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts b/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts new file mode 100644 index 0000000000..56e70eb47e --- /dev/null +++ b/packages/apps/human-app/server/src/common/guards/strategy/jwt.http.ts @@ -0,0 +1,67 @@ +import { Injectable, Req, UnauthorizedException } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import * as jwt from 'jsonwebtoken'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { EnvironmentConfigService } from '../../../common/config/environment-config.service'; +import { JwtUserData } from '../../../common/utils/jwt-token.model'; +import { KvStoreGateway } from '../../../integrations/kv-store/kv-store.gateway'; + +@Injectable() +export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') { + constructor( + private readonly configService: EnvironmentConfigService, + private readonly kvStoreGateway: KvStoreGateway, + ) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKeyProvider: async ( + _request: any, + rawJwtToken: any, + done: any, + ) => { + try { + const payload = jwt.decode(rawJwtToken); + const chainId = this.configService.chainIdsEnabled[0]; + const address = (payload as any).reputation_network; + const pubKey = await this.kvStoreGateway.getReputationOraclePublicKey( + chainId, + address, + ); + done(null, pubKey); + } catch (error) { + console.error(error); + done(error); + } + }, + passReqToCallback: true, + }); + } + + public async validate( + @Req() _request: any, + payload: { + user_id: string; + status: string; + wallet_address: string; + reputation_network: string; + qualifications?: string[]; + site_key?: string; + email?: string; + }, + ): Promise { + if (!payload.user_id) { + throw new UnauthorizedException('Invalid token: missing user id'); + } + + return { + user_id: payload.user_id, + wallet_address: payload.wallet_address, + status: payload.status, + reputation_network: payload.reputation_network, + qualifications: payload.qualifications, + site_key: payload.site_key, + email: payload.email, + }; + } +} diff --git a/packages/apps/human-app/server/src/common/interfaces/jwt.ts b/packages/apps/human-app/server/src/common/interfaces/jwt.ts new file mode 100644 index 0000000000..e5cfcce604 --- /dev/null +++ b/packages/apps/human-app/server/src/common/interfaces/jwt.ts @@ -0,0 +1,6 @@ +import { JwtUserData } from '../utils/jwt-token.model'; + +export interface RequestWithUser extends Request { + user: JwtUserData; + token: string; +} diff --git a/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts b/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts index bad884b4b6..85da60b3c1 100644 --- a/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts +++ b/packages/apps/human-app/server/src/common/utils/jwt-token.model.ts @@ -6,17 +6,13 @@ export class JwtUserData { @AutoMap() wallet_address: string; @AutoMap() - email: string; - @AutoMap() - kyc_status: 'approved' | 'none'; + status: string; @AutoMap() reputation_network: string; @AutoMap() - qualifications: string[]; - @AutoMap() - site_key: string; + email?: string; @AutoMap() - iat: number; + qualifications?: string[]; @AutoMap() - exp: number; + site_key?: string; } diff --git a/packages/apps/human-app/server/src/integrations/kv-store/kv-store.gateway.ts b/packages/apps/human-app/server/src/integrations/kv-store/kv-store.gateway.ts index f15928a830..5341d6f44a 100644 --- a/packages/apps/human-app/server/src/integrations/kv-store/kv-store.gateway.ts +++ b/packages/apps/human-app/server/src/integrations/kv-store/kv-store.gateway.ts @@ -1,10 +1,19 @@ -import { HttpException, Inject, Injectable } from '@nestjs/common'; -import { EnvironmentConfigService } from '../../common/config/environment-config.service'; -import { ethers } from 'ethers'; -import { ChainId, KVStoreKeys, KVStoreUtils } from '@human-protocol/sdk'; +import { + ChainId, + KVStoreKeys, + KVStoreUtils, + StorageClient, +} from '@human-protocol/sdk'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { HttpException, Inject, Injectable } from '@nestjs/common'; import { Cache } from 'cache-manager'; -import { ORACLE_URL_CACHE_KEY } from '../../common/constants/cache'; +import { ethers } from 'ethers'; +import { EnvironmentConfigService } from '../../common/config/environment-config.service'; +import { JWT_KVSTORE_KEY } from '../../common/constants'; +import { + ORACLE_URL_CACHE_KEY, + REPUTATION_ORACLE_PUBLIC_KEY, +} from '../../common/constants/cache'; @Injectable() export class KvStoreGateway { @@ -88,4 +97,45 @@ export class KvStoreGateway { return jobTypes; } } + + async getReputationOraclePublicKey( + chainId: ChainId, + address: string, + ): Promise { + const key = `${REPUTATION_ORACLE_PUBLIC_KEY}:${chainId}:${address}`; + const cachedData: string | undefined = await this.cacheManager.get(key); + if (cachedData) { + return cachedData; + } + + let publicKey: string; + try { + const url = await KVStoreUtils.getFileUrlAndVerifyHash( + chainId, + address, + JWT_KVSTORE_KEY, + ); + publicKey = (await StorageClient.downloadFileFromUrl(url)) as string; + } catch (e) { + if (e.toString().includes('Error: Invalid address')) { + throw new HttpException( + `Unable to retrieve public key from address: ${address}`, + 400, + ); + } else { + throw new Error(`Error while fetching public key from kv-store: ${e}`); + } + } + + if (!publicKey || publicKey === '') { + throw new HttpException( + `Unable to retrieve public key from address: ${address}`, + 400, + ); + } else { + // Guardar en caché sin TTL (persistente) + await this.cacheManager.set(key, publicKey, 0); + return publicKey; + } + } } diff --git a/packages/apps/human-app/server/src/modules/abuse/abuse.controller.ts b/packages/apps/human-app/server/src/modules/abuse/abuse.controller.ts index a2f336a3f6..3af5366983 100644 --- a/packages/apps/human-app/server/src/modules/abuse/abuse.controller.ts +++ b/packages/apps/human-app/server/src/modules/abuse/abuse.controller.ts @@ -1,25 +1,18 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; -import { - Body, - Controller, - Get, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Body, Controller, Get, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags, } from '@nestjs/swagger'; -import { Authorization } from '../../common/config/params-decorators'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { AbuseService } from './abuse.service'; import { - ReportedAbuseResponse, ReportAbuseCommand, ReportAbuseDto, + ReportedAbuseResponse, } from './model/abuse.model'; @ApiBearerAuth() @@ -39,17 +32,16 @@ export class AbuseController { status: 200, description: 'Abuse report successfully submitted', }) - @UsePipes(new ValidationPipe()) public async reportAbuse( @Body() AbuseDto: ReportAbuseDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const AbuseCommand = this.mapper.map( AbuseDto, ReportAbuseDto, ReportAbuseCommand, ); - AbuseCommand.token = token; + AbuseCommand.token = req.token; return this.service.reportAbuse(AbuseCommand); } @@ -63,8 +55,8 @@ export class AbuseController { type: ReportedAbuseResponse, }) public async getUserAbuseReports( - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { - return this.service.getUserAbuseReports(token); + return this.service.getUserAbuseReports(req.token); } } diff --git a/packages/apps/human-app/server/src/modules/abuse/spec/abuse.controller.spec.ts b/packages/apps/human-app/server/src/modules/abuse/spec/abuse.controller.spec.ts index 29ced5ac79..2a97e4970b 100644 --- a/packages/apps/human-app/server/src/modules/abuse/spec/abuse.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/abuse/spec/abuse.controller.spec.ts @@ -12,6 +12,7 @@ import { TOKEN, } from './abuse.fixtures'; import { AbuseProfile } from '../abuse.mapper.profile'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; describe('AbuseController', () => { let controller: AbuseController; @@ -42,7 +43,9 @@ describe('AbuseController', () => { const dto = reportAbuseDtoFixture; const command = reportAbuseCommandFixture; - await controller.reportAbuse(dto, TOKEN); + await controller.reportAbuse(dto, { + token: TOKEN, + } as RequestWithUser); expect(abuseServiceMock.reportAbuse).toHaveBeenCalledWith(command); }); @@ -56,7 +59,9 @@ describe('AbuseController', () => { reportedAbuseResponseFixture, ); - const result = await controller.getUserAbuseReports(token); + const result = await controller.getUserAbuseReports({ + token: TOKEN, + } as RequestWithUser); expect(abuseServiceMock.getUserAbuseReports).toHaveBeenCalledWith(token); expect(result).toEqual(reportedAbuseResponseFixture); diff --git a/packages/apps/human-app/server/src/modules/email-confirmation/email-confirmation.controller.ts b/packages/apps/human-app/server/src/modules/email-confirmation/email-confirmation.controller.ts index d3ef5eb0f7..ca6367b918 100644 --- a/packages/apps/human-app/server/src/modules/email-confirmation/email-confirmation.controller.ts +++ b/packages/apps/human-app/server/src/modules/email-confirmation/email-confirmation.controller.ts @@ -1,14 +1,10 @@ -import { - Body, - Controller, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { EmailConfirmationService } from './email-confirmation.service'; -import { InjectMapper } from '@automapper/nestjs'; import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; +import { Body, Controller, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '../../common/decorators'; +import { RequestWithUser } from '../../common/interfaces/jwt'; +import { EmailConfirmationService } from './email-confirmation.service'; import { EmailVerificationCommand, EmailVerificationDto, @@ -17,8 +13,8 @@ import { ResendEmailVerificationCommand, ResendEmailVerificationDto, } from './model/resend-email-verification.model'; -import { Authorization } from '../../common/config/params-decorators'; +@ApiTags('Email-Confirmation') @Controller('/email-confirmation') export class EmailConfirmationController { constructor( @@ -26,12 +22,11 @@ export class EmailConfirmationController { @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('Email-Confirmation') - @Post('/email-verification') @ApiOperation({ summary: 'Endpoint to verify the user email address', }) - @UsePipes(new ValidationPipe()) + @Public() + @Post('/email-verification') public async verifyEmail( @Body() emailVerificationDto: EmailVerificationDto, ): Promise { @@ -43,23 +38,21 @@ export class EmailConfirmationController { return this.service.processEmailVerification(emailVerificationCommand); } - @ApiTags('Email-Confirmation') @Post('/resend-email-verification') @ApiOperation({ summary: 'Endpoint to resend the email verification link', }) @ApiBearerAuth() - @UsePipes(new ValidationPipe()) public async resendEmailVerification( @Body() resendEmailVerificationDto: ResendEmailVerificationDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const resendEmailVerificationCommand = this.mapper.map( resendEmailVerificationDto, ResendEmailVerificationDto, ResendEmailVerificationCommand, ); - resendEmailVerificationCommand.token = token; + resendEmailVerificationCommand.token = req.token; return this.service.processResendEmailVerification( resendEmailVerificationCommand, ); diff --git a/packages/apps/human-app/server/src/modules/email-confirmation/spec/email-verification.controller.spec.ts b/packages/apps/human-app/server/src/modules/email-confirmation/spec/email-verification.controller.spec.ts index d8c359c2de..8a00a7a1fe 100644 --- a/packages/apps/human-app/server/src/modules/email-confirmation/spec/email-verification.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/email-confirmation/spec/email-verification.controller.spec.ts @@ -17,6 +17,7 @@ import { emailVerificationCommandFixture, emailVerificationDtoFixture, } from './email-verification.fixtures'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; describe('EmailConfirmationController', () => { let controller: EmailConfirmationController; @@ -62,7 +63,9 @@ describe('EmailConfirmationController', () => { it('should call the processResendEmailVerification method of the service with the correct arguments', async () => { const dto = resendEmailVerificationDtoFixture; const command = resendEmailVerificationCommandFixture; - await controller.resendEmailVerification(dto, emailVerificationToken); + await controller.resendEmailVerification(dto, { + token: emailVerificationToken, + } as RequestWithUser); expect(service.processResendEmailVerification).toHaveBeenCalledWith( command, ); diff --git a/packages/apps/human-app/server/src/modules/h-captcha/h-captcha.controller.ts b/packages/apps/human-app/server/src/modules/h-captcha/h-captcha.controller.ts index 7624c5d2eb..74c41e8ca0 100644 --- a/packages/apps/human-app/server/src/modules/h-captcha/h-captcha.controller.ts +++ b/packages/apps/human-app/server/src/modules/h-captcha/h-captcha.controller.ts @@ -1,98 +1,85 @@ -import { - Body, - Controller, - Get, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { InjectMapper } from '@automapper/nestjs'; import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; +import { Body, Controller, Get, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { RequestWithUser } from '../../common/interfaces/jwt'; +import { JwtUserData } from '../../common/utils/jwt-token.model'; import { HCaptchaService } from './h-captcha.service'; -import { - VerifyTokenCommand, - VerifyTokenDto, - VerifyTokenResponse, -} from './model/verify-token.model'; import { DailyHmtSpentCommand, DailyHmtSpentResponse, } from './model/daily-hmt-spent.model'; -import { - Authorization, - JwtPayload, -} from '../../common/config/params-decorators'; -import { JwtUserData } from '../../common/utils/jwt-token.model'; import { EnableLabelingCommand, EnableLabelingResponse, } from './model/enable-labeling.model'; import { UserStatsCommand, UserStatsResponse } from './model/user-stats.model'; +import { + VerifyTokenCommand, + VerifyTokenDto, + VerifyTokenResponse, +} from './model/verify-token.model'; +@ApiTags('h-captcha') +@ApiBearerAuth() @Controller('/labeling/h-captcha') export class HCaptchaController { constructor( private readonly service: HCaptchaService, @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('h-captcha') + @Post('/enable') @ApiOperation({ summary: 'Enables h-captcha labeling' }) - @ApiBearerAuth() - @UsePipes(new ValidationPipe()) public async enableLabeling( - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const command = { - token: token, + token: req.token, } as EnableLabelingCommand; return this.service.enableLabeling(command); } - @ApiTags('h-captcha') @Post('/verify') @ApiOperation({ summary: 'Sends solution for verification' }) - @ApiBearerAuth() - @UsePipes(new ValidationPipe()) public async verifyToken( @Body() dto: VerifyTokenDto, - @JwtPayload() jwtPayload: JwtUserData, - @Authorization() jwtToken: string, + @Request() req: RequestWithUser, ): Promise { - const command = this.mapper.map( - jwtPayload, - JwtUserData, - VerifyTokenCommand, - ); + if (!req.user.site_key) { + throw new Error('Missing site key'); + } + const command = this.mapper.map(req.user, JwtUserData, VerifyTokenCommand); command.response = dto.token; - command.jwtToken = jwtToken; + command.jwtToken = req.token; return await this.service.verifyToken(command); } - @ApiTags('h-captcha') + @Get('/daily-hmt-spent') - @ApiBearerAuth() @ApiOperation({ summary: 'Gets global daily HMT spent' }) - @UsePipes(new ValidationPipe()) public async getDailyHmtSpent( - @JwtPayload() jwtPayload: JwtUserData, + @Request() req: RequestWithUser, ): Promise { + if (!req.user.site_key) { + throw new Error('Missing site key'); + } const command = this.mapper.map( - jwtPayload, + req.user, JwtUserData, DailyHmtSpentCommand, ); return this.service.getDailyHmtSpent(command); } - @ApiTags('h-captcha') + @Get('/user-stats') - @ApiBearerAuth() @ApiOperation({ summary: 'Gets stats per user' }) - @UsePipes(new ValidationPipe()) public async getUserStats( - @JwtPayload() jwtPayload: JwtUserData, + @Request() req: RequestWithUser, ): Promise { - const command = this.mapper.map(jwtPayload, JwtUserData, UserStatsCommand); + if (!req.user.email || !req.user.site_key) { + throw new Error('Missing email or site key'); + } + const command = this.mapper.map(req.user, JwtUserData, UserStatsCommand); return this.service.getUserStats(command); } } diff --git a/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.controller.spec.ts b/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.controller.spec.ts index 1fef61d5b4..6ccb55a9cf 100644 --- a/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.controller.spec.ts @@ -14,6 +14,7 @@ import { verifyTokenCommandFixture, verifyTokenDtoFixture, } from './h-captcha.fixtures'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; describe('HCaptchaController', () => { let controller: HCaptchaController; @@ -41,7 +42,7 @@ describe('HCaptchaController', () => { expect(controller).toBeDefined(); }); it('should call getUserStats with proper arguments', async () => { - const dto = jwtUserDataFixture; + const dto = { user: jwtUserDataFixture } as RequestWithUser; const command = hCaptchaUserStatsCommandFixture; await controller.getUserStats(dto); expect(service.getUserStats).toHaveBeenCalledWith(command); @@ -49,14 +50,20 @@ describe('HCaptchaController', () => { it('should call verifyToken with proper arguments', async () => { const dto = verifyTokenDtoFixture; - const jwtPayload = jwtUserDataFixture; + const jwtPayload = { + user: jwtUserDataFixture, + token: JWT_TOKEN, + } as RequestWithUser; const command = verifyTokenCommandFixture; - await controller.verifyToken(dto, jwtPayload, JWT_TOKEN); + await controller.verifyToken(dto, jwtPayload); expect(service.verifyToken).toHaveBeenCalledWith(command); }); it('should call getDailyHmtSpent with proper arguments', async () => { - const dto = jwtUserDataFixture; + const dto = { + user: jwtUserDataFixture, + token: JWT_TOKEN, + } as RequestWithUser; const command = dailyHmtSpentCommandFixture; await controller.getDailyHmtSpent(dto); expect(service.getDailyHmtSpent).toHaveBeenCalledWith(command); @@ -64,7 +71,9 @@ describe('HCaptchaController', () => { it('should call enableLabeling with proper arguments', async () => { const command = enableLabelingCommandFixture; - await controller.enableLabeling(JWT_TOKEN); + await controller.enableLabeling({ + token: JWT_TOKEN, + } as RequestWithUser); expect(service.enableLabeling).toHaveBeenCalledWith(command); }); }); diff --git a/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts b/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts index 5696a6d2cd..d114d7c6b1 100644 --- a/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts +++ b/packages/apps/human-app/server/src/modules/h-captcha/spec/h-captcha.fixtures.ts @@ -19,13 +19,12 @@ import { EnableLabelingCommand, EnableLabelingResponse, } from '../model/enable-labeling.model'; +const STATUS = 'active'; const EMAIL = 'some_email@example.com'; const ID = 'jwt_token_id'; const H_CAPTCHA_SITE_KEY = 'some_h_captcha_site_key'; const TOKEN_TO_VERIFY = 'some_hcaptcha_token'; const REPUTATION_NETWORK = 'some_reputation_network_address'; -const IAT = 2137; -const EXP = 7312; const POLYGON_WALLET_ADDR = '0xAf6E2cB084314Fbe50228e697d2B1b8553DDEd25'; const DAILY_HMT_SPENT = 100; const SOLVED = 10; @@ -73,12 +72,10 @@ export const jwtUserDataFixture: JwtUserData = { user_id: ID, wallet_address: POLYGON_WALLET_ADDR, email: EMAIL, - kyc_status: 'approved', + status: STATUS, qualifications: [], site_key: H_CAPTCHA_SITE_KEY, reputation_network: REPUTATION_NETWORK, - iat: IAT, - exp: EXP, }; export const hCaptchaUserStatsCommandFixture: UserStatsCommand = { diff --git a/packages/apps/human-app/server/src/modules/health/health.controller.ts b/packages/apps/human-app/server/src/modules/health/health.controller.ts index 96f3837db7..48d8dab2f7 100644 --- a/packages/apps/human-app/server/src/modules/health/health.controller.ts +++ b/packages/apps/human-app/server/src/modules/health/health.controller.ts @@ -10,10 +10,12 @@ import { } from '@nestjs/terminus'; import { EnvironmentConfigService } from '../../common/config/environment-config.service'; +import { Public } from '../../common/decorators'; import { PingResponseDto } from './dto/ping-response.dto'; import { CacheManagerHealthIndicator } from './indicators/cache-manager.health'; @ApiTags('Health') +@Public() @Controller('health') export class HealthController { constructor( diff --git a/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts b/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts index bb4caa6a8d..72c1a2d3e3 100644 --- a/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts +++ b/packages/apps/human-app/server/src/modules/job-assignment/job-assignment.controller.ts @@ -1,3 +1,5 @@ +import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; import { Body, Controller, @@ -5,27 +7,25 @@ import { Post, Put, Query, - UsePipes, - ValidationPipe, + Request, } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { InjectMapper } from '@automapper/nestjs'; -import { Mapper } from '@automapper/core'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { JobAssignmentService } from './job-assignment.service'; import { - JobAssignmentDto, JobAssignmentCommand, + JobAssignmentDto, JobAssignmentResponse, - JobsFetchParamsDto, JobsFetchParamsCommand, + JobsFetchParamsDto, JobsFetchResponse, - ResignJobDto, - ResignJobCommand, RefreshJobDto, + ResignJobCommand, + ResignJobDto, } from './model/job-assignment.model'; -import { Authorization } from '../../common/config/params-decorators'; @ApiTags('Job-Assignment') +@ApiBearerAuth() @Controller('/assignment') export class JobAssignmentController { constructor( @@ -37,66 +37,61 @@ export class JobAssignmentController { @ApiOperation({ summary: 'Request to assign a job to a logged user', }) - @ApiBearerAuth() - @UsePipes(new ValidationPipe()) public async assignJob( @Body() jobAssignmentDto: JobAssignmentDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const jobAssignmentCommand = this.mapper.map( jobAssignmentDto, JobAssignmentDto, JobAssignmentCommand, ); - jobAssignmentCommand.token = token; + jobAssignmentCommand.token = req.token; return this.service.processJobAssignment(jobAssignmentCommand); } @Get('/job') - @ApiBearerAuth() @ApiOperation({ summary: 'Request to get jobs assigned to a logged user', }) public async getAssignedJobs( @Query() jobsAssignmentParamsDto: JobsFetchParamsDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const jobsAssignmentParamsCommand = this.mapper.map( jobsAssignmentParamsDto, JobsFetchParamsDto, JobsFetchParamsCommand, ); - jobsAssignmentParamsCommand.token = token; + jobsAssignmentParamsCommand.token = req.token; return this.service.processGetAssignedJobs(jobsAssignmentParamsCommand); } @Post('/resign-job') - @ApiBearerAuth() @ApiOperation({ summary: 'Request to resign from assigment', }) public async resignAssigment( @Body() dto: ResignJobDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ) { const command = this.mapper.map(dto, ResignJobDto, ResignJobCommand); - command.token = token; + command.token = req.token; return this.service.resignJob(command); } @Put('/refresh') - @ApiBearerAuth() @ApiOperation({ summary: 'Request to refresh assigments data', }) public async refreshAssigments( @Body() dto: RefreshJobDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ) { const command = new JobsFetchParamsCommand(); command.oracleAddress = dto.oracle_address; - command.token = token; + command.token = req.token; return this.service.updateAssignmentsCache(command); } } diff --git a/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts b/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts index 313ab9e14b..ae69ce4e12 100644 --- a/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/job-assignment/spec/job-assignment.controller.spec.ts @@ -1,7 +1,11 @@ -import { JobAssignmentService } from '../job-assignment.service'; -import { JobAssignmentController } from '../job-assignment.controller'; +import { classes } from '@automapper/classes'; +import { AutomapperModule } from '@automapper/nestjs'; +import { HttpService } from '@nestjs/axios'; import { Test, TestingModule } from '@nestjs/testing'; -import { jobAssignmentServiceMock } from './job-assignment.service.mock'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; +import { JobAssignmentController } from '../job-assignment.controller'; +import { JobAssignmentProfile } from '../job-assignment.mapper.profile'; +import { JobAssignmentService } from '../job-assignment.service'; import { JobAssignmentCommand, JobAssignmentDto, @@ -10,21 +14,18 @@ import { RefreshJobDto, } from '../model/job-assignment.model'; import { - jobAssignmentDtoFixture, + EXCHANGE_ORACLE_ADDRESS, jobAssignmentCommandFixture, + jobAssignmentDtoFixture, jobAssignmentResponseFixture, - jobsFetchParamsDtoFixture, + jobAssignmentToken, jobsFetchParamsCommandFixture, + jobsFetchParamsDtoFixture, jobsFetchResponseFixture, - jobAssignmentToken, refreshJobDtoFixture, - EXCHANGE_ORACLE_ADDRESS, TOKEN, } from './job-assignment.fixtures'; -import { AutomapperModule } from '@automapper/nestjs'; -import { classes } from '@automapper/classes'; -import { JobAssignmentProfile } from '../job-assignment.mapper.profile'; -import { HttpService } from '@nestjs/axios'; +import { jobAssignmentServiceMock } from './job-assignment.service.mock'; const httpServiceMock = { request: jest.fn().mockImplementation((options) => { @@ -74,7 +75,9 @@ describe('JobAssignmentController', () => { it('should call service processJobAssignment method with proper fields set', async () => { const dto: JobAssignmentDto = jobAssignmentDtoFixture; const command: JobAssignmentCommand = jobAssignmentCommandFixture; - await controller.assignJob(dto, jobAssignmentToken); + await controller.assignJob(dto, { + token: jobAssignmentToken, + } as RequestWithUser); expect(jobAssignmentService.processJobAssignment).toHaveBeenCalledWith( command, ); @@ -83,7 +86,9 @@ describe('JobAssignmentController', () => { it('should return the result of service processJobAssignment method', async () => { const dto: JobAssignmentDto = jobAssignmentDtoFixture; const command: JobAssignmentCommand = jobAssignmentCommandFixture; - const result = await controller.assignJob(dto, jobAssignmentToken); + const result = await controller.assignJob(dto, { + token: jobAssignmentToken, + } as RequestWithUser); expect(result).toEqual( jobAssignmentServiceMock.processJobAssignment(command), ); @@ -92,7 +97,9 @@ describe('JobAssignmentController', () => { it('should call service processGetAssignedJobs method with proper fields set', async () => { const dto: JobsFetchParamsDto = jobsFetchParamsDtoFixture; const command: JobsFetchParamsCommand = jobsFetchParamsCommandFixture; - await controller.getAssignedJobs(dto, jobAssignmentToken); + await controller.getAssignedJobs(dto, { + token: jobAssignmentToken, + } as RequestWithUser); expect(jobAssignmentService.processGetAssignedJobs).toHaveBeenCalledWith( command, ); @@ -100,7 +107,9 @@ describe('JobAssignmentController', () => { it('should call service refreshAssigments method with proper fields set', async () => { const dto: RefreshJobDto = refreshJobDtoFixture; - await controller.refreshAssigments(dto, jobAssignmentToken); + await controller.refreshAssigments(dto, { + token: jobAssignmentToken, + } as RequestWithUser); expect(jobAssignmentService.updateAssignmentsCache).toHaveBeenCalledWith({ oracleAddress: EXCHANGE_ORACLE_ADDRESS, token: TOKEN, diff --git a/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts b/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts index d74d817285..75a951eb8b 100644 --- a/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts +++ b/packages/apps/human-app/server/src/modules/jobs-discovery/jobs-discovery.controller.ts @@ -1,9 +1,12 @@ +import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; import { Controller, Get, HttpException, HttpStatus, Query, + Request, } from '@nestjs/common'; import { ApiBearerAuth, @@ -11,22 +14,17 @@ import { ApiOperation, ApiTags, } from '@nestjs/swagger'; -import { InjectMapper } from '@automapper/nestjs'; -import { Mapper } from '@automapper/core'; +import { EnvironmentConfigService } from '../../common/config/environment-config.service'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { JobsDiscoveryService } from './jobs-discovery.service'; import { JobsDiscoveryParamsCommand, JobsDiscoveryParamsDto, JobsDiscoveryResponse, } from './model/jobs-discovery.model'; -import { - Authorization, - JwtPayload, -} from '../../common/config/params-decorators'; -import { JwtUserData } from '../../common/utils/jwt-token.model'; -import { EnvironmentConfigService } from '../../common/config/environment-config.service'; @Controller() +@ApiBearerAuth() @ApiTags('Jobs-Discovery') export class JobsDiscoveryController { constructor( @@ -36,15 +34,13 @@ export class JobsDiscoveryController { ) {} @Get('/jobs') - @ApiBearerAuth() @ApiOperation({ summary: 'Retrieve a list of jobs for given Exchange Oracle', }) @ApiOkResponse({ type: JobsDiscoveryResponse, description: 'List of jobs' }) public async getJobs( @Query() jobsDiscoveryParamsDto: JobsDiscoveryParamsDto, - @JwtPayload() jwtPayload: JwtUserData, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { if (!this.environmentConfigService.jobsDiscoveryFlag) { throw new HttpException( @@ -58,8 +54,8 @@ export class JobsDiscoveryController { JobsDiscoveryParamsDto, JobsDiscoveryParamsCommand, ); - jobsDiscoveryParamsCommand.token = token; - jobsDiscoveryParamsCommand.data.qualifications = jwtPayload.qualifications; + jobsDiscoveryParamsCommand.token = req.token; + jobsDiscoveryParamsCommand.data.qualifications = req.user.qualifications; return await this.service.processJobsDiscovery(jobsDiscoveryParamsCommand); } } diff --git a/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts b/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts index 87a0ddfc7e..fb4e14b4bf 100644 --- a/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/jobs-discovery/spec/jobs-discovery.controller.spec.ts @@ -1,22 +1,21 @@ -import { JobsDiscoveryService } from '../jobs-discovery.service'; -import { JobsDiscoveryController } from '../jobs-discovery.controller'; +import { classes } from '@automapper/classes'; +import { AutomapperModule } from '@automapper/nestjs'; +import { ChainId } from '@human-protocol/sdk'; +import { HttpService } from '@nestjs/axios'; +import { HttpException, HttpStatus } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { jobsDiscoveryServiceMock } from './jobs-discovery.service.mock'; +import { CommonConfigModule } from '../../../common/config/common-config.module'; +import { EnvironmentConfigService } from '../../../common/config/environment-config.service'; +import { JobsDiscoveryController } from '../jobs-discovery.controller'; +import { JobsDiscoveryProfile } from '../jobs-discovery.mapper.profile'; +import { JobsDiscoveryService } from '../jobs-discovery.service'; import { - jobsDiscoveryParamsCommandFixture, dtoFixture, - jobDiscoveryToken, + jobsDiscoveryParamsCommandFixture, responseFixture, } from './jobs-discovery.fixtures'; -import { AutomapperModule } from '@automapper/nestjs'; -import { classes } from '@automapper/classes'; -import { JobsDiscoveryProfile } from '../jobs-discovery.mapper.profile'; -import { HttpService } from '@nestjs/axios'; -import { CommonConfigModule } from '../../../common/config/common-config.module'; -import { ConfigModule } from '@nestjs/config'; -import { EnvironmentConfigService } from '../../../common/config/environment-config.service'; -import { HttpException, HttpStatus } from '@nestjs/common'; -import { ChainId } from '@human-protocol/sdk'; +import { jobsDiscoveryServiceMock } from './jobs-discovery.service.mock'; describe('JobsDiscoveryController', () => { let controller: JobsDiscoveryController; @@ -73,11 +72,10 @@ describe('JobsDiscoveryController', () => { it('should call service processJobsDiscovery method with proper fields set', async () => { const dto = dtoFixture; const command = jobsDiscoveryParamsCommandFixture; - await controller.getJobs( - dto, - { qualifications: [] } as any, - jobDiscoveryToken, - ); + await controller.getJobs(dto, { + user: { qualifications: [] }, + token: command.token, + } as any); command.data.qualifications = []; expect(jobsDiscoveryService.processJobsDiscovery).toHaveBeenCalledWith( command, @@ -88,11 +86,7 @@ describe('JobsDiscoveryController', () => { const dto = dtoFixture; (configServiceMock as any).jobsDiscoveryFlag = false; await expect( - controller.getJobs( - dto, - { qualifications: [] } as any, - jobDiscoveryToken, - ), + controller.getJobs(dto, { user: { qualifications: [] } } as any), ).rejects.toThrow( new HttpException('Jobs discovery is disabled', HttpStatus.FORBIDDEN), ); diff --git a/packages/apps/human-app/server/src/modules/kyc-procedure/kyc-procedure.controller.ts b/packages/apps/human-app/server/src/modules/kyc-procedure/kyc-procedure.controller.ts index a5e721fb47..f763113987 100644 --- a/packages/apps/human-app/server/src/modules/kyc-procedure/kyc-procedure.controller.ts +++ b/packages/apps/human-app/server/src/modules/kyc-procedure/kyc-procedure.controller.ts @@ -1,31 +1,30 @@ -import { Controller, Get, Post } from '@nestjs/common'; -import { KycProcedureService } from './kyc-procedure.service'; +import { Controller, Get, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { RequestWithUser } from '../../common/interfaces/jwt'; +import { KycProcedureService } from './kyc-procedure.service'; import { KycProcedureStartResponse } from './model/kyc-start.model'; -import { Authorization } from '../../common/config/params-decorators'; +@ApiTags('Kyc-Procedure') +@ApiBearerAuth() @Controller('/kyc') export class KycProcedureController { constructor(private readonly service: KycProcedureService) {} - @ApiTags('Kyc-Procedure') @Post('/start') - @ApiBearerAuth() @ApiOperation({ summary: 'Endpoint to start Kyc process for the user', }) public async startKycProcedure( - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { - return this.service.processStartKycProcedure(token); + return this.service.processStartKycProcedure(req.token); } - @ApiTags('Kyc-Procedure') + @Get('/on-chain') - @ApiBearerAuth() @ApiOperation({ summary: 'Endpoint to get a signed address for the KYC process.', }) - public async onChainKyc(@Authorization() token: string): Promise { - return this.service.processKycOnChain(token); + public async onChainKyc(@Request() req: RequestWithUser): Promise { + return this.service.processKycOnChain(req.token); } } diff --git a/packages/apps/human-app/server/src/modules/kyc-procedure/spec/kyc-procedure.controller.spec.ts b/packages/apps/human-app/server/src/modules/kyc-procedure/spec/kyc-procedure.controller.spec.ts index 49f57935b7..4ccf70859e 100644 --- a/packages/apps/human-app/server/src/modules/kyc-procedure/spec/kyc-procedure.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/kyc-procedure/spec/kyc-procedure.controller.spec.ts @@ -1,8 +1,9 @@ +import { expect, it, jest } from '@jest/globals'; +import { Test, TestingModule } from '@nestjs/testing'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; import { KycProcedureController } from '../kyc-procedure.controller'; import { KycProcedureService } from '../kyc-procedure.service'; -import { Test, TestingModule } from '@nestjs/testing'; import { serviceMock } from './kyc-procedure.service.mock'; -import { expect, it, jest } from '@jest/globals'; describe('KycProcedureController', () => { let controller: KycProcedureController; @@ -30,12 +31,12 @@ describe('KycProcedureController', () => { service, 'processStartKycProcedure', ); - await controller.startKycProcedure('token'); + await controller.startKycProcedure({ token: 'token' } as RequestWithUser); expect(startKycProcedureSpy).toHaveBeenCalledWith('token'); }); it('should call processKycOnChain method of KycProcedureService', async () => { const kycService = jest.spyOn(service, 'processKycOnChain'); - await controller.onChainKyc('token'); + await controller.onChainKyc({ token: 'token' } as RequestWithUser); expect(kycService).toHaveBeenCalledWith('token'); }); }); diff --git a/packages/apps/human-app/server/src/modules/nda/nda.controller.ts b/packages/apps/human-app/server/src/modules/nda/nda.controller.ts index ed886df9b1..f91a015ee4 100644 --- a/packages/apps/human-app/server/src/modules/nda/nda.controller.ts +++ b/packages/apps/human-app/server/src/modules/nda/nda.controller.ts @@ -1,22 +1,13 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; -import { - Body, - Controller, - Get, - HttpCode, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Body, Controller, Get, HttpCode, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Authorization } from '../../common/config/params-decorators'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { GetNDACommand, SignNDACommand, SignNDADto } from './model/nda.model'; import { NDAService } from './nda.service'; @Controller('/nda') @ApiTags('NDA') -@UsePipes(new ValidationPipe()) @ApiBearerAuth() export class NDAController { @InjectMapper() private readonly mapper: Mapper; @@ -34,9 +25,9 @@ export class NDAController { 'Retrieves the latest NDA URL that users must sign to join the oracle', }) @Get('/') - async getLatestNDA(@Authorization() token: string) { + async getLatestNDA(@Request() req: RequestWithUser) { const command = new GetNDACommand(); - command.token = token; + command.token = req.token; return this.ndaService.getLatestNDA(command); } @@ -47,9 +38,9 @@ export class NDAController { }) @HttpCode(200) @Post('sign') - async signNDA(@Body() dto: SignNDADto, @Authorization() token: string) { + async signNDA(@Body() dto: SignNDADto, @Request() req: RequestWithUser) { const command = this.mapper.map(dto, SignNDADto, SignNDACommand); - command.token = token; + command.token = req.token; await this.ndaService.signNDA(command); return { message: 'NDA signed successfully' }; } diff --git a/packages/apps/human-app/server/src/modules/nda/spec/nda.controller.spec.ts b/packages/apps/human-app/server/src/modules/nda/spec/nda.controller.spec.ts index 333e9c79ca..8e9ebf5685 100644 --- a/packages/apps/human-app/server/src/modules/nda/spec/nda.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/nda/spec/nda.controller.spec.ts @@ -1,16 +1,17 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AutomapperModule } from '@automapper/nestjs'; import { classes } from '@automapper/classes'; +import { AutomapperModule } from '@automapper/nestjs'; +import { Test, TestingModule } from '@nestjs/testing'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; import { NDAController } from '../nda.controller'; +import { SignNDAProfile } from '../nda.mapper.profile'; import { NDAService } from '../nda.service'; -import { ndaServiceMock } from './nda.service.mock'; import { NDA_TOKEN, signNDACommandFixture, signNDADtoFixture, } from './nda.fixtures'; -import { SignNDAProfile } from '../nda.mapper.profile'; +import { ndaServiceMock } from './nda.service.mock'; describe('NDAController', () => { let controller: NDAController; @@ -40,13 +41,13 @@ describe('NDAController', () => { it('should call service signNDA method with proper fields set', async () => { const dto = signNDADtoFixture; const command = signNDACommandFixture; - await controller.signNDA(dto, NDA_TOKEN); + await controller.signNDA(dto, { token: NDA_TOKEN } as RequestWithUser); expect(ndaServiceMock.signNDA).toHaveBeenCalledWith(command); }); it('should call service getLatestNDA method with proper fields set', async () => { const token = NDA_TOKEN; - await controller.getLatestNDA(token); + await controller.getLatestNDA({ token: NDA_TOKEN } as RequestWithUser); expect(ndaServiceMock.getLatestNDA).toHaveBeenCalledWith({ token }); }); }); diff --git a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts index 2e315ad0b1..5b062b6b8f 100644 --- a/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts +++ b/packages/apps/human-app/server/src/modules/oracle-discovery/oracle-discovery.controller.ts @@ -1,30 +1,24 @@ +import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; import { Controller, Get, HttpException, HttpStatus, Query, - UsePipes, - ValidationPipe, + Request, } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { EnvironmentConfigService } from '../../common/config/environment-config.service'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { - ApiBearerAuth, - ApiOkResponse, - ApiOperation, - ApiTags, -} from '@nestjs/swagger'; -import { OracleDiscoveryService } from './oracle-discovery.service'; -import { + DiscoveredOracle, GetOraclesCommand, GetOraclesQuery, - DiscoveredOracle, } from './model/oracle-discovery.model'; -import { InjectMapper } from '@automapper/nestjs'; -import { Mapper } from '@automapper/core'; -import { EnvironmentConfigService } from '../../common/config/environment-config.service'; -import { JwtPayload } from '../../common/config/params-decorators'; -import { JwtUserData } from '../../common/utils/jwt-token.model'; +import { OracleDiscoveryService } from './oracle-discovery.service'; +@ApiTags('Oracle-Discovery') @Controller() export class OracleDiscoveryController { constructor( @@ -33,17 +27,14 @@ export class OracleDiscoveryController { @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('Oracle-Discovery') - @ApiBearerAuth() @Get('/oracles') @ApiOperation({ summary: 'Oracles discovery' }) @ApiOkResponse({ type: Array, description: 'List of oracles', }) - @UsePipes(new ValidationPipe()) public async getOracles( - @JwtPayload() jwtPayload: JwtUserData, + @Request() req: RequestWithUser, @Query() query: GetOraclesQuery, ): Promise { if (!this.environmentConfigService.jobsDiscoveryFlag) { @@ -55,8 +46,9 @@ export class OracleDiscoveryController { const command = this.mapper.map(query, GetOraclesQuery, GetOraclesCommand); const oracles = await this.oracleDiscoveryService.getOracles(command); - const isAudinoAvailableForUser = - jwtPayload.qualifications.includes('audino'); + const isAudinoAvailableForUser = (req?.user?.qualifications ?? []).includes( + 'audino', + ); /** * TODO: remove filtering logic when Audino available for everyone diff --git a/packages/apps/human-app/server/src/modules/password-reset/password-reset.controller.ts b/packages/apps/human-app/server/src/modules/password-reset/password-reset.controller.ts index f593d87ee7..80fcce0f5a 100644 --- a/packages/apps/human-app/server/src/modules/password-reset/password-reset.controller.ts +++ b/packages/apps/human-app/server/src/modules/password-reset/password-reset.controller.ts @@ -1,14 +1,8 @@ -import { - Body, - Controller, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { PasswordResetService } from './password-reset.service'; -import { InjectMapper } from '@automapper/nestjs'; import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; +import { Body, Controller, Post } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '../../common/decorators'; import { ForgotPasswordCommand, ForgotPasswordDto, @@ -17,7 +11,10 @@ import { RestorePasswordCommand, RestorePasswordDto, } from './model/restore-password.model'; +import { PasswordResetService } from './password-reset.service'; +@ApiTags('Password-Reset') +@Public() @Controller('/password-reset') export class PasswordResetController { constructor( @@ -25,12 +22,10 @@ export class PasswordResetController { @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('Password-Reset') @Post('/forgot-password') @ApiOperation({ summary: 'Endpoint to initiate the password reset process', }) - @UsePipes(new ValidationPipe()) public async forgotPassword( @Body() forgotPasswordDto: ForgotPasswordDto, ): Promise { @@ -42,12 +37,10 @@ export class PasswordResetController { return await this.service.processForgotPassword(forgotPasswordCommand); } - @ApiTags('Password-Reset') @Post('/restore-password') @ApiOperation({ summary: 'Endpoint to restore the user password after reset', }) - @UsePipes(new ValidationPipe()) public async restorePassword(@Body() dto: RestorePasswordDto): Promise { const command = this.mapper.map( dto, diff --git a/packages/apps/human-app/server/src/modules/prepare-signature/prepare-signature.controller.ts b/packages/apps/human-app/server/src/modules/prepare-signature/prepare-signature.controller.ts index 26fd177e9f..9e8bfbb76c 100644 --- a/packages/apps/human-app/server/src/modules/prepare-signature/prepare-signature.controller.ts +++ b/packages/apps/human-app/server/src/modules/prepare-signature/prepare-signature.controller.ts @@ -1,13 +1,8 @@ -import { - Body, - Controller, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { InjectMapper } from '@automapper/nestjs'; import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; +import { Body, Controller, Post } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '../../common/decorators'; import { PrepareSignatureCommand, PrepareSignatureDto, @@ -15,6 +10,8 @@ import { } from './model/prepare-signature.model'; import { PrepareSignatureService } from './prepare-signature.service'; +@ApiTags('Prepare-Signature') +@Public() @Controller('/prepare-signature') export class PrepareSignatureController { constructor( @@ -22,13 +19,11 @@ export class PrepareSignatureController { @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('Prepare-Signature') @Post('/') @ApiOperation({ summary: 'Endpoint for generating typed structured data objects compliant with EIP-712. The generated object should be convertible to a string format to ensure compatibility with signature mechanisms', }) - @UsePipes(new ValidationPipe()) public async prepareSignature( @Body() prepareSignatureDto: PrepareSignatureDto, ): Promise { diff --git a/packages/apps/human-app/server/src/modules/register-address/register-address.controller.ts b/packages/apps/human-app/server/src/modules/register-address/register-address.controller.ts index bb6f706f71..3a569e42df 100644 --- a/packages/apps/human-app/server/src/modules/register-address/register-address.controller.ts +++ b/packages/apps/human-app/server/src/modules/register-address/register-address.controller.ts @@ -1,21 +1,17 @@ -import { - Body, - Controller, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; +import { Body, Controller, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Authorization } from '../../common/config/params-decorators'; -import { RegisterAddressService } from './register-address.service'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { RegisterAddressCommand, RegisterAddressDto, RegisterAddressResponse, } from './model/register-address.model'; -import { Mapper } from '@automapper/core'; +import { RegisterAddressService } from './register-address.service'; +@ApiTags('Register Address') +@ApiBearerAuth() @Controller('/user/register-address') export class RegisterAddressController { @InjectMapper() private readonly mapper: Mapper; @@ -27,23 +23,20 @@ export class RegisterAddressController { this.mapper = mapper; } - @ApiTags('Register Address') @Post('/') @ApiOperation({ summary: 'Register Blockchain Address', }) - @ApiBearerAuth() - @UsePipes(new ValidationPipe()) public async registerAddress( @Body() dto: RegisterAddressDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const command = this.mapper.map( dto, RegisterAddressDto, RegisterAddressCommand, ); - command.token = token; + command.token = req.token; return this.service.registerBlockchainAddress(command); } } diff --git a/packages/apps/human-app/server/src/modules/register-address/spec/register-address.controller.spec.ts b/packages/apps/human-app/server/src/modules/register-address/spec/register-address.controller.spec.ts index 4748e24a25..c28bed1646 100644 --- a/packages/apps/human-app/server/src/modules/register-address/spec/register-address.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/register-address/spec/register-address.controller.spec.ts @@ -12,6 +12,7 @@ import { registerAddressResponseFixture, } from './register-address.fixtures'; import { RegisterAddressProfile } from '../register-address.mapper.profile'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; describe('RegisterAddressController', () => { let controller: RegisterAddressController; @@ -58,7 +59,9 @@ describe('RegisterAddressController', () => { it('should call service registerBlockchainAddress method with proper fields set', async () => { const dto = registerAddressDtoFixture; const command = registerAddressCommandFixture; - await controller.registerAddress(dto, REGISTER_ADDRESS_TOKEN); + await controller.registerAddress(dto, { + token: REGISTER_ADDRESS_TOKEN, + } as RequestWithUser); expect(service.registerBlockchainAddress).toHaveBeenCalledWith(command); }); }); diff --git a/packages/apps/human-app/server/src/modules/statistics/spec/statistics.controller.spec.ts b/packages/apps/human-app/server/src/modules/statistics/spec/statistics.controller.spec.ts index ea49d85f42..184ccd3e81 100644 --- a/packages/apps/human-app/server/src/modules/statistics/spec/statistics.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/statistics/spec/statistics.controller.spec.ts @@ -16,6 +16,7 @@ import { AutomapperModule } from '@automapper/nestjs'; import { classes } from '@automapper/classes'; import { StatisticsProfile } from '../statistics.mapper.profile'; import { jwtUserDataFixture } from '../../h-captcha/spec/h-captcha.fixtures'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; describe('StatisticsController', () => { let controller: StatisticsController; @@ -63,11 +64,10 @@ describe('StatisticsController', () => { const dto: UserStatisticsDto = { oracle_address: statisticsExchangeOracleAddress, }; - const result = await controller.getUserStatistics( - dto, - jwtUserDataFixture, - statisticsToken, - ); + const result = await controller.getUserStatistics(dto, { + user: jwtUserDataFixture, + token: statisticsToken, + } as RequestWithUser); expect(statisticsServiceMock.getUserStats).toHaveBeenCalledWith( generalUserStatsCommandFixture, diff --git a/packages/apps/human-app/server/src/modules/statistics/statistics.controller.ts b/packages/apps/human-app/server/src/modules/statistics/statistics.controller.ts index 2941ccb81c..e20631f483 100644 --- a/packages/apps/human-app/server/src/modules/statistics/statistics.controller.ts +++ b/packages/apps/human-app/server/src/modules/statistics/statistics.controller.ts @@ -1,12 +1,8 @@ -import { - Controller, - Get, - Query, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; +import { Controller, Get, Query, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { StatisticsService } from './statistics.service'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { OracleStatisticsCommand, OracleStatisticsDto, @@ -17,24 +13,20 @@ import { UserStatisticsDto, UserStatisticsResponse, } from './model/user-statistics.model'; -import { - Authorization, - JwtPayload, -} from '../../common/config/params-decorators'; -import { InjectMapper } from '@automapper/nestjs'; -import { Mapper } from '@automapper/core'; -import { JwtUserData } from '../../common/utils/jwt-token.model'; +import { StatisticsService } from './statistics.service'; +import { Public } from '../../common/decorators'; +@ApiTags('Statistics') @Controller() export class StatisticsController { constructor( private readonly service: StatisticsService, @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('Statistics') - @Get('/stats') + @ApiOperation({ summary: 'General Oracle Statistics' }) - @UsePipes(new ValidationPipe()) + @Public() + @Get('/stats') public getOracleStatistics( @Query() dto: OracleStatisticsDto, ): Promise { @@ -47,22 +39,20 @@ export class StatisticsController { } @ApiTags('Statistics') - @Get('stats/assignment') @ApiOperation({ summary: 'Statistics for requesting user' }) @ApiBearerAuth() - @UsePipes(new ValidationPipe()) + @Get('stats/assignment') public getUserStatistics( @Query() dto: UserStatisticsDto, - @JwtPayload() payload: JwtUserData, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const command = this.mapper.map( dto, UserStatisticsDto, UserStatisticsCommand, ); - command.token = token; - command.walletAddress = payload.wallet_address; + command.token = req.token; + command.walletAddress = req.user.wallet_address; return this.service.getUserStats(command); } } diff --git a/packages/apps/human-app/server/src/modules/token-refresh/token-refresh.controller.ts b/packages/apps/human-app/server/src/modules/token-refresh/token-refresh.controller.ts index 5e87a77606..bb7892dd68 100644 --- a/packages/apps/human-app/server/src/modules/token-refresh/token-refresh.controller.ts +++ b/packages/apps/human-app/server/src/modules/token-refresh/token-refresh.controller.ts @@ -1,20 +1,17 @@ -import { - Body, - Controller, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { InjectMapper } from '@automapper/nestjs'; import { Mapper } from '@automapper/core'; -import { TokenRefreshService } from './token-refresh.service'; +import { InjectMapper } from '@automapper/nestjs'; +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Public } from '../../common/decorators'; import { TokenRefreshCommand, TokenRefreshDto, + TokenRefreshResponse, } from './model/token-refresh.model'; -import { TokenRefreshResponse } from './model/token-refresh.model'; +import { TokenRefreshService } from './token-refresh.service'; +@ApiTags('Refresh-Token') +@Public() @Controller() export class TokenRefreshController { constructor( @@ -22,10 +19,8 @@ export class TokenRefreshController { @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('Refresh-Token') @Post('/auth/refresh') @ApiOperation({ summary: 'Refresh token' }) - @UsePipes(new ValidationPipe()) public refreshToken( @Body() dto: TokenRefreshDto, ): Promise { diff --git a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts index 8c419c5fcf..291bb66b9e 100644 --- a/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts +++ b/packages/apps/human-app/server/src/modules/ui-configuration/ui-configuration.controller.ts @@ -1,14 +1,17 @@ import { Controller, Get } from '@nestjs/common'; import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; import { EnvironmentConfigService } from '../../common/config/environment-config.service'; +import { Public } from '../../common/decorators'; import { UiConfigResponseDto } from './ui-configuration.dto'; @Controller() +@Public() @ApiTags('UI-Configuration') export class UiConfigurationController { constructor( private readonly environmentConfigService: EnvironmentConfigService, ) {} + @Get('/ui-config') @ApiOperation({ summary: 'Retrieve UI configuration' }) @ApiOkResponse({ diff --git a/packages/apps/human-app/server/src/modules/user-operator/operator.controller.ts b/packages/apps/human-app/server/src/modules/user-operator/operator.controller.ts index 6b3f62c246..d66c3c0550 100644 --- a/packages/apps/human-app/server/src/modules/user-operator/operator.controller.ts +++ b/packages/apps/human-app/server/src/modules/user-operator/operator.controller.ts @@ -1,18 +1,17 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; -import { - Body, - Controller, - HttpCode, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Body, Controller, HttpCode, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; - -import { Authorization } from '../../common/config/params-decorators'; - -import { OperatorService } from './operator.service'; +import { Public } from '../../common/decorators'; +import { RequestWithUser } from '../../common/interfaces/jwt'; +import { + DisableOperatorCommand, + DisableOperatorDto, +} from './model/disable-operator.model'; +import { + EnableOperatorCommand, + EnableOperatorDto, +} from './model/enable-operator.model'; import { SignupOperatorCommand, SignupOperatorDto, @@ -23,26 +22,20 @@ import { SigninOperatorResponse, SignupOperatorResponse, } from './model/operator-signin.model'; -import { - DisableOperatorCommand, - DisableOperatorDto, -} from './model/disable-operator.model'; -import { - EnableOperatorCommand, - EnableOperatorDto, -} from './model/enable-operator.model'; +import { OperatorService } from './operator.service'; +@ApiTags('User-Operator') @Controller() export class OperatorController { constructor( private readonly service: OperatorService, @InjectMapper() private readonly mapper: Mapper, ) {} - @ApiTags('User-Operator') + @Post('/auth/web3/signup') @HttpCode(200) @ApiOperation({ summary: 'Operator signup' }) - @UsePipes(new ValidationPipe()) + @Public() async signupOperator( @Body() signupOperatorDto: SignupOperatorDto, ): Promise { @@ -54,11 +47,10 @@ export class OperatorController { return this.service.signupOperator(signupOperatorCommand); } - @ApiTags('User-Operator') @Post('/auth/web3/signin') @HttpCode(200) @ApiOperation({ summary: 'Operator signin' }) - @UsePipes(new ValidationPipe()) + @Public() async signinOperator( @Body() dto: SigninOperatorDto, ): Promise { @@ -70,45 +62,41 @@ export class OperatorController { return this.service.signinOperator(command); } - @ApiTags('User-Operator') @Post('/disable-operator') @HttpCode(200) @ApiOperation({ summary: 'Endpoint to disable an operator', }) @ApiBearerAuth() - @UsePipes(new ValidationPipe()) async disableOperator( @Body() disableOperatorDto: DisableOperatorDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const disableOperatorCommand = this.mapper.map( disableOperatorDto, DisableOperatorDto, DisableOperatorCommand, ); - disableOperatorCommand.token = token; + disableOperatorCommand.token = req.token; await this.service.disableOperator(disableOperatorCommand); } - @ApiTags('User-Operator') @Post('/enable-operator') @HttpCode(200) @ApiOperation({ summary: 'Endpoint to enable an operator', }) @ApiBearerAuth() - @UsePipes(new ValidationPipe()) async enable( @Body() enableOperatorDto: EnableOperatorDto, - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { const enableOperatorCommand = this.mapper.map( enableOperatorDto, EnableOperatorDto, EnableOperatorCommand, ); - enableOperatorCommand.token = token; + enableOperatorCommand.token = req.token; await this.service.enableOperator(enableOperatorCommand); } } diff --git a/packages/apps/human-app/server/src/modules/user-worker/spec/worker.controller.spec.ts b/packages/apps/human-app/server/src/modules/user-worker/spec/worker.controller.spec.ts index 1d2b71caab..6505d929ed 100644 --- a/packages/apps/human-app/server/src/modules/user-worker/spec/worker.controller.spec.ts +++ b/packages/apps/human-app/server/src/modules/user-worker/spec/worker.controller.spec.ts @@ -1,17 +1,18 @@ -import { WorkerController } from '../worker.controller'; -import { WorkerService } from '../worker.service'; +import { classes } from '@automapper/classes'; +import { AutomapperModule } from '@automapper/nestjs'; +import { Test, TestingModule } from '@nestjs/testing'; +import { RequestWithUser } from '../../../common/interfaces/jwt'; import { RegistrationInExchangeOracleDto, SignupWorkerCommand, SignupWorkerDto, } from '../model/worker-registration.model'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AutomapperModule } from '@automapper/nestjs'; -import { classes } from '@automapper/classes'; -import { WorkerProfile } from '../worker.mapper.profile'; -import { workerServiceMock } from './worker.service.mock'; import { SigninWorkerDto } from '../model/worker-signin.model'; +import { WorkerController } from '../worker.controller'; +import { WorkerProfile } from '../worker.mapper.profile'; +import { WorkerService } from '../worker.service'; import { workerToken } from './worker.fixtures'; +import { workerServiceMock } from './worker.service.mock'; describe('WorkerController', () => { let controller: WorkerController; @@ -79,7 +80,9 @@ describe('WorkerController', () => { oracle_address: '0x34df642', h_captcha_token: 'h_captcha_token', }; - await controller.createRegistrationInExchangeOracle(dto, workerToken); + await controller.createRegistrationInExchangeOracle(dto, { + token: workerToken, + } as RequestWithUser); const expectedCommand = { oracleAddress: dto.oracle_address, hCaptchaToken: dto.h_captcha_token, diff --git a/packages/apps/human-app/server/src/modules/user-worker/worker.controller.ts b/packages/apps/human-app/server/src/modules/user-worker/worker.controller.ts index 86ebfbebef..875c856451 100644 --- a/packages/apps/human-app/server/src/modules/user-worker/worker.controller.ts +++ b/packages/apps/human-app/server/src/modules/user-worker/worker.controller.ts @@ -1,19 +1,13 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; -import { - Body, - Controller, - Get, - Post, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Body, Controller, Get, Post, Request } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Authorization } from '../../common/config/params-decorators'; +import { Public } from '../../common/decorators'; +import { RequestWithUser } from '../../common/interfaces/jwt'; import { - RegistrationInExchangeOracleResponse, RegistrationInExchangeOracleCommand, RegistrationInExchangeOracleDto, + RegistrationInExchangeOracleResponse, RegistrationInExchangeOraclesResponse, SignupWorkerCommand, SignupWorkerDto, @@ -32,9 +26,10 @@ export class WorkerController { private readonly service: WorkerService, @InjectMapper() private readonly mapper: Mapper, ) {} + @Post('/auth/signup') @ApiOperation({ summary: 'Worker signup' }) - @UsePipes(new ValidationPipe()) + @Public() public signupWorker(@Body() signupWorkerDto: SignupWorkerDto): Promise { const signupWorkerCommand = this.mapper.map( signupWorkerDto, @@ -46,7 +41,7 @@ export class WorkerController { @Post('/auth/signin') @ApiOperation({ summary: 'Worker signin' }) - @UsePipes(new ValidationPipe()) + @Public() public signinWorker( @Body() signinWorkerDto: SigninWorkerDto, ): Promise { @@ -61,17 +56,17 @@ export class WorkerController { @ApiBearerAuth() @Post('/exchange-oracle-registration') @ApiOperation({ summary: 'Registers a worker in Exchange Oracle' }) - @UsePipes(new ValidationPipe()) public createRegistrationInExchangeOracle( @Body() registrationInExchangeOracleDto: RegistrationInExchangeOracleDto, - @Authorization() token: string, + + @Request() req: RequestWithUser, ): Promise { const registrationInExchangeOracle = this.mapper.map( registrationInExchangeOracleDto, RegistrationInExchangeOracleDto, RegistrationInExchangeOracleCommand, ); - registrationInExchangeOracle.token = token; + registrationInExchangeOracle.token = req.token; return this.service.registrationInExchangeOracle( registrationInExchangeOracle, @@ -81,10 +76,9 @@ export class WorkerController { @ApiBearerAuth() @Get('/exchange-oracle-registration') @ApiOperation({ summary: 'Retrieves oracles registered by the worker' }) - @UsePipes(new ValidationPipe()) public getRegistrationInExchangeOracles( - @Authorization() token: string, + @Request() req: RequestWithUser, ): Promise { - return this.service.getRegistrationInExchangeOracles(token); + return this.service.getRegistrationInExchangeOracles(req.token); } }