From b10d6d7014d4a4c43fc9a4debfca0915cb77c750 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 3 Oct 2024 13:41:51 +0530 Subject: [PATCH 1/7] feat: userid from token --- .../applications/applications.service.ts | 34 +++++++++++++------ .../dto/create-application.input.ts | 4 --- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/api/src/modules/applications/applications.service.ts b/apps/api/src/modules/applications/applications.service.ts index 8aeafa00..90d0b664 100644 --- a/apps/api/src/modules/applications/applications.service.ts +++ b/apps/api/src/modules/applications/applications.service.ts @@ -10,6 +10,7 @@ import { QueryOptionsDto } from 'src/common/graphql/dtos/query-options.dto'; import { CoreService } from 'src/common/graphql/services/core.service'; import { JwtService } from '@nestjs/jwt'; import { ConfigService } from '@nestjs/config'; +import { User } from '../users/entities/user.entity'; @Injectable() export class ApplicationsService extends CoreService { @@ -41,17 +42,33 @@ export class ApplicationsService extends CoreService { throw new Error('Access Denied. Not an ADMIN.'); } - const userExists = await this.usersService.findByUserId(applicationInput.userId); + const userEntry = await this.getUserEntryFromToken(authorizationHeader); - if (!userExists) { - throw new Error('This user does not exist.'); - } + const newApplicationObject = new Application({ + name: applicationInput.name, + userId: userEntry.userId, + }); - const application = this.applicationsRepository.create(applicationInput); + const application = this.applicationsRepository.create(newApplicationObject); return this.applicationsRepository.save(application); } async checkAdminUser(authHeader: Request): Promise { + try { + const userEntry = await this.getUserEntryFromToken(authHeader); + + // Check if the user has the ADMIN role + if (userEntry.userRole === UserRoles.ADMIN) { + return true; + } + + return false; + } catch (error) { + throw error; + } + } + + async getUserEntryFromToken(authHeader: Request): Promise { try { const bearerToken = authHeader.toString(); @@ -76,12 +93,7 @@ export class ApplicationsService extends CoreService { throw new UnauthorizedException('User not found'); } - // Check if the user has the ADMIN role - if (userEntry.userRole === UserRoles.ADMIN) { - return true; - } - - return false; + return userEntry; } catch (error) { throw error; } diff --git a/apps/api/src/modules/applications/dto/create-application.input.ts b/apps/api/src/modules/applications/dto/create-application.input.ts index ba340971..9bd0dfb0 100644 --- a/apps/api/src/modules/applications/dto/create-application.input.ts +++ b/apps/api/src/modules/applications/dto/create-application.input.ts @@ -6,8 +6,4 @@ export class CreateApplicationInput { @Field() @IsNotEmpty() name: string; - - @Field() - @IsNotEmpty() - userId: number; } From 52974bd2f1bb9e41d2381039931d615da40d56c5 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 3 Oct 2024 13:42:03 +0530 Subject: [PATCH 2/7] docs: update collection --- apps/api/OsmoX-API.postman_collection.json | 2 +- apps/api/OsmoX.postman_collection.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/api/OsmoX-API.postman_collection.json b/apps/api/OsmoX-API.postman_collection.json index 5a3ff5d4..0874ace1 100644 --- a/apps/api/OsmoX-API.postman_collection.json +++ b/apps/api/OsmoX-API.postman_collection.json @@ -781,7 +781,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n name: \"\",\r\n userId: 2,\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", + "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n name: \"\",\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", "variables": "" } }, diff --git a/apps/api/OsmoX.postman_collection.json b/apps/api/OsmoX.postman_collection.json index dafe6178..95313e9e 100644 --- a/apps/api/OsmoX.postman_collection.json +++ b/apps/api/OsmoX.postman_collection.json @@ -3430,7 +3430,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n name: \"sampleTestXApp\",\r\n userId: 2,\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", + "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n name: \"sampleTestXApp\",\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", "variables": "" } }, @@ -3493,7 +3493,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n name: \"sampleTestXApp\",\r\n userId: 2,\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", + "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n name: \"sampleTestXApp\",\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", "variables": "" } }, @@ -3551,7 +3551,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n unknownValue: \"Some unknown parameter\"\r\n name: \"sampleFoundationXApp\",\r\n userId: 2,\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", + "query": "mutation CreateApplication {\r\n application(createApplicationInput: {\r\n unknownValue: \"Some unknown parameter\"\r\n name: \"sampleFoundationXApp\",\r\n }) {\r\n applicationId\r\n name\r\n userId\r\n createdOn\r\n updatedOn\r\n status\r\n }\r\n}", "variables": "" } }, From 889189f19f4cd035db010f7b4d4558029f0eee62 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 3 Oct 2024 13:50:51 +0530 Subject: [PATCH 3/7] docs: update request --- apps/api/docs/api-documentation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/api/docs/api-documentation.md b/apps/api/docs/api-documentation.md index 3db682e8..647172c4 100644 --- a/apps/api/docs/api-documentation.md +++ b/apps/api/docs/api-documentation.md @@ -307,7 +307,6 @@ Allows the user with `Admin` role to create a new application. mutation CreateApplication { application(createApplicationInput: { name: "newSampleApp", - userId: 2, }) { applicationId name @@ -348,7 +347,7 @@ curl --location 'http://localhost:3000/graphql' \ Allows the user to fetch all applications based on the passed query parameters. Requires passing bearer token for authorization. -Note: The API will return a successful response when the `server-api-key` passed uses `application_id` that is associated with an `Admin`. +Note: The API will return a successful response when the Bearer `authorization-token` passed is associated with an `Admin`. The different options that can be used while fetching notifications are as follows: From 313992daf89853258a42d5385f380843d2cff262 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 3 Oct 2024 16:38:15 +0530 Subject: [PATCH 4/7] feat: add role guard --- .../src/common/decorators/roles.decorator.ts | 8 +++ apps/api/src/common/guards/role.guard.ts | 49 +++++++++++++++++++ .../applications/applications.resolver.ts | 11 +++-- .../applications/applications.service.ts | 34 +------------ .../modules/providers/providers.resolver.ts | 18 +++---- .../modules/providers/providers.service.ts | 22 +-------- 6 files changed, 75 insertions(+), 67 deletions(-) create mode 100644 apps/api/src/common/decorators/roles.decorator.ts create mode 100644 apps/api/src/common/guards/role.guard.ts diff --git a/apps/api/src/common/decorators/roles.decorator.ts b/apps/api/src/common/decorators/roles.decorator.ts new file mode 100644 index 00000000..8afdabe3 --- /dev/null +++ b/apps/api/src/common/decorators/roles.decorator.ts @@ -0,0 +1,8 @@ +import { SetMetadata } from '@nestjs/common'; +import { UserRoles } from '../constants/database'; + +// Type definition for allowed values (only values from UserRoles) +type UserRoleValues = (typeof UserRoles)[keyof typeof UserRoles]; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const Roles = (...roles: UserRoleValues[]) => SetMetadata('roles', roles); diff --git a/apps/api/src/common/guards/role.guard.ts b/apps/api/src/common/guards/role.guard.ts new file mode 100644 index 00000000..594cebd7 --- /dev/null +++ b/apps/api/src/common/guards/role.guard.ts @@ -0,0 +1,49 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { GqlExecutionContext } from '@nestjs/graphql'; +import { JwtService } from '@nestjs/jwt'; +import { ConfigService } from '@nestjs/config'; +import { UserRoles } from 'src/common/constants/database'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor( + private reflector: Reflector, + private jwtService: JwtService, + private configService: ConfigService, + ) {} + + canActivate(context: ExecutionContext): boolean { + // Fetch required roles from the metadata + const requiredRoles = this.reflector.getAllAndOverride<(keyof typeof UserRoles)[]>('roles', [ + context.getHandler(), + context.getClass(), + ]); + + if (!requiredRoles || requiredRoles.length === 0) { + return true; // Allow access if no roles are specified + } + + const ctx = GqlExecutionContext.create(context); + const req = ctx.getContext().req; + const authorizationHeader = req.headers.authorization; + + if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) { + return false; // No or invalid authorization token + } + + const token = authorizationHeader.split(' ')[1]; + const secret = this.configService.getOrThrow('JWT_SECRET'); + + try { + // Decode the JWT token to get the user information + const decodedToken = this.jwtService.verify(token, { secret }); + const userRoleId = decodedToken.role; + + // Check if the user's role matches any of the required roles + return requiredRoles.includes(userRoleId); + } catch (error) { + return false; // Invalid token or other error + } + } +} diff --git a/apps/api/src/modules/applications/applications.resolver.ts b/apps/api/src/modules/applications/applications.resolver.ts index d205291e..4df24610 100644 --- a/apps/api/src/modules/applications/applications.resolver.ts +++ b/apps/api/src/modules/applications/applications.resolver.ts @@ -6,9 +6,13 @@ import { Application } from './entities/application.entity'; import { QueryOptionsDto } from 'src/common/graphql/dtos/query-options.dto'; import { ApplicationResponse } from './dto/application-response.dto'; import { GqlAuthGuard } from 'src/common/guards/api-key/gql-auth.guard'; +import { Roles } from 'src/common/decorators/roles.decorator'; +import { RolesGuard } from 'src/common/guards/role.guard'; +import { UserRoles } from 'src/common/constants/database'; @Resolver(() => Application) -@UseGuards(GqlAuthGuard) +@Roles(UserRoles.ADMIN) +@UseGuards(GqlAuthGuard, RolesGuard) export class ApplicationsResolver { constructor(private readonly applicationsService: ApplicationsService) {} @@ -27,12 +31,9 @@ export class ApplicationsResolver { @Query(() => ApplicationResponse, { name: 'applications' }) async findAll( - @Context() context, @Args('options', { type: () => QueryOptionsDto, nullable: true, defaultValue: {} }) options: QueryOptionsDto, ): Promise { - const request: Request = context.req; - const authorizationHeader = request.headers['authorization']; - return this.applicationsService.getAllApplications(options, authorizationHeader); + return this.applicationsService.getAllApplications(options); } } diff --git a/apps/api/src/modules/applications/applications.service.ts b/apps/api/src/modules/applications/applications.service.ts index 90d0b664..cbffb7d8 100644 --- a/apps/api/src/modules/applications/applications.service.ts +++ b/apps/api/src/modules/applications/applications.service.ts @@ -4,7 +4,7 @@ import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; import { CreateApplicationInput } from './dto/create-application.input'; import { UsersService } from '../users/users.service'; -import { Status, UserRoles } from 'src/common/constants/database'; +import { Status } from 'src/common/constants/database'; import { ApplicationResponse } from './dto/application-response.dto'; import { QueryOptionsDto } from 'src/common/graphql/dtos/query-options.dto'; import { CoreService } from 'src/common/graphql/services/core.service'; @@ -36,12 +36,6 @@ export class ApplicationsService extends CoreService { applicationInput: CreateApplicationInput, authorizationHeader: Request, ): Promise { - const isAdmin = await this.checkAdminUser(authorizationHeader); - - if (!isAdmin) { - throw new Error('Access Denied. Not an ADMIN.'); - } - const userEntry = await this.getUserEntryFromToken(authorizationHeader); const newApplicationObject = new Application({ @@ -53,21 +47,6 @@ export class ApplicationsService extends CoreService { return this.applicationsRepository.save(application); } - async checkAdminUser(authHeader: Request): Promise { - try { - const userEntry = await this.getUserEntryFromToken(authHeader); - - // Check if the user has the ADMIN role - if (userEntry.userRole === UserRoles.ADMIN) { - return true; - } - - return false; - } catch (error) { - throw error; - } - } - async getUserEntryFromToken(authHeader: Request): Promise { try { const bearerToken = authHeader.toString(); @@ -99,16 +78,7 @@ export class ApplicationsService extends CoreService { } } - async getAllApplications( - options: QueryOptionsDto, - authorizationHeader: Request, - ): Promise { - const isAdmin = await this.checkAdminUser(authorizationHeader); - - if (!isAdmin) { - throw new Error('Access Denied. Not an ADMIN.'); - } - + async getAllApplications(options: QueryOptionsDto): Promise { const baseConditions = []; const searchableFields = ['name']; diff --git a/apps/api/src/modules/providers/providers.resolver.ts b/apps/api/src/modules/providers/providers.resolver.ts index c67306b7..38f62e58 100644 --- a/apps/api/src/modules/providers/providers.resolver.ts +++ b/apps/api/src/modules/providers/providers.resolver.ts @@ -1,4 +1,4 @@ -import { Args, Context, Mutation, Resolver, Query } from '@nestjs/graphql'; +import { Args, Mutation, Resolver, Query } from '@nestjs/graphql'; import { UseGuards } from '@nestjs/common'; import { QueryOptionsDto } from 'src/common/graphql/dtos/query-options.dto'; import { Provider } from './entities/provider.entity'; @@ -6,30 +6,28 @@ import { ProvidersService } from './providers.service'; import { CreateProviderInput } from './dto/create-provider.input'; import { ProviderResponse } from './dto/provider-response.dto'; import { GqlAuthGuard } from 'src/common/guards/api-key/gql-auth.guard'; +import { Roles } from 'src/common/decorators/roles.decorator'; +import { UserRoles } from 'src/common/constants/database'; +import { RolesGuard } from 'src/common/guards/role.guard'; @Resolver(() => Provider) -@UseGuards(GqlAuthGuard) +@Roles(UserRoles.ADMIN) +@UseGuards(GqlAuthGuard, RolesGuard) export class ProvidersResolver { constructor(private readonly providerService: ProvidersService) {} @Mutation(() => Provider, { name: 'provider' }) async createProvider( - @Context() context, @Args('createProviderInput') createProviderInput: CreateProviderInput, ): Promise { - const request: Request = context.req; - const authorizationHeader = request.headers['authorization']; - return await this.providerService.createProvider(createProviderInput, authorizationHeader); + return await this.providerService.createProvider(createProviderInput); } @Query(() => ProviderResponse, { name: 'providers' }) async findAll( - @Context() context, @Args('options', { type: () => QueryOptionsDto, nullable: true, defaultValue: {} }) options: QueryOptionsDto, ): Promise { - const request: Request = context.req; - const authorizationHeader = request.headers['authorization']; - return this.providerService.getAllProviders(options, authorizationHeader); + return this.providerService.getAllProviders(options); } } diff --git a/apps/api/src/modules/providers/providers.service.ts b/apps/api/src/modules/providers/providers.service.ts index 04491367..b72559f1 100644 --- a/apps/api/src/modules/providers/providers.service.ts +++ b/apps/api/src/modules/providers/providers.service.ts @@ -27,16 +27,7 @@ export class ProvidersService extends CoreService { return this.providerRepository.findOne({ where: { providerId, status: Status.ACTIVE } }); } - async createProvider( - providerInput: CreateProviderInput, - authorizationHeader: Request, - ): Promise { - const isAdmin = await this.applicationsService.checkAdminUser(authorizationHeader); - - if (!isAdmin) { - throw new Error('Access Denied. Not an ADMIN.'); - } - + async createProvider(providerInput: CreateProviderInput): Promise { const userExists = await this.usersService.findByUserId(providerInput.userId); const applicationExists = await this.applicationsService.findById(providerInput.applicationId); const channelExists = Object.values(ChannelType).includes(providerInput.channelType); @@ -71,16 +62,7 @@ export class ProvidersService extends CoreService { return null; } - async getAllProviders( - options: QueryOptionsDto, - authorizationHeader: Request, - ): Promise { - const isAdmin = await this.applicationsService.checkAdminUser(authorizationHeader); - - if (!isAdmin) { - throw new Error('Access Denied. Not an ADMIN.'); - } - + async getAllProviders(options: QueryOptionsDto): Promise { const baseConditions = []; const searchableFields = ['name']; From 40afcc3a4cc94e1e0f688a0c2d50ad9277e4a4a8 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 3 Oct 2024 16:42:02 +0530 Subject: [PATCH 5/7] docs: update test collection --- apps/api/OsmoX.postman_collection.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/api/OsmoX.postman_collection.json b/apps/api/OsmoX.postman_collection.json index 95313e9e..3c56e784 100644 --- a/apps/api/OsmoX.postman_collection.json +++ b/apps/api/OsmoX.postman_collection.json @@ -3276,11 +3276,11 @@ " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", "});\r", - "pm.test(\"message: Access Denied. Not an ADMIN.\", function () {\r", + "pm.test(\"message: Forbidden resource\", function () {\r", " var jsonData = pm.response.json();\r", " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", - " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Access Denied. Not an ADMIN.\");\r", + " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Forbidden resource\");\r", "});" ], "type": "text/javascript", @@ -3464,11 +3464,11 @@ " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", "});\r", - "pm.test(\"message: Access Denied. Not an ADMIN.\", function () {\r", + "pm.test(\"message: Forbidden resource\", function () {\r", " var jsonData = pm.response.json();\r", " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", - " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Access Denied. Not an ADMIN.\");\r", + " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Forbidden resource\");\r", "});" ], "type": "text/javascript", @@ -3656,11 +3656,11 @@ " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", "});\r", - "pm.test(\"message: Access Denied. Not an ADMIN.\", function () {\r", + "pm.test(\"message: Forbidden resource\", function () {\r", " var jsonData = pm.response.json();\r", " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", - " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Access Denied. Not an ADMIN.\");\r", + " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Forbidden resource\");\r", "});" ], "type": "text/javascript", @@ -3907,11 +3907,11 @@ " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", "});\r", - "pm.test(\"message: Access Denied. Not an ADMIN.\", function () {\r", + "pm.test(\"message: Forbidden resource\", function () {\r", " var jsonData = pm.response.json();\r", " pm.expect(jsonData).to.have.property(\"data\");\r", " pm.expect(jsonData).to.have.property(\"errors\").to.be.an(\"array\");\r", - " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Access Denied. Not an ADMIN.\");\r", + " pm.expect(jsonData.errors[0]).to.have.property(\"message\", \"Forbidden resource\");\r", "});" ], "type": "text/javascript", From 3d59bc48ce5682e7acbae6d8040475401b5b280b Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 3 Oct 2024 17:20:06 +0530 Subject: [PATCH 6/7] docs: update create provider collection --- apps/api/OsmoX-API.postman_collection.json | 4 ++-- apps/api/OsmoX.postman_collection.json | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/api/OsmoX-API.postman_collection.json b/apps/api/OsmoX-API.postman_collection.json index 0874ace1..70cfbabe 100644 --- a/apps/api/OsmoX-API.postman_collection.json +++ b/apps/api/OsmoX-API.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "b97d878b-aa80-4b2f-a9f0-a0640b2bc6e2", + "_postman_id": "36ff2947-7450-4393-b3cd-39e82ee073e6", "name": "OsmoX-API", "description": "List of all APIs used in OsmoX", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", @@ -883,7 +883,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 2,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"\",\n userId: 1,\n }) {\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", + "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 2,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"\",\n userId: 1,\n }) {\n providerId\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", "variables": "" } }, diff --git a/apps/api/OsmoX.postman_collection.json b/apps/api/OsmoX.postman_collection.json index 3c56e784..0d273918 100644 --- a/apps/api/OsmoX.postman_collection.json +++ b/apps/api/OsmoX.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "e9aeb9a0-761f-4b10-86a7-dd4bcaebca94", + "_postman_id": "336711a4-7ddf-4f32-b136-4aa09b0e284b", "name": "OsmoX", "description": "OsmoX API helps creating new notifications, fetching existing notifications as well as perform authorization related tasks.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", @@ -3810,7 +3810,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 2,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", + "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 2,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n providerId\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", "variables": "" } }, @@ -3873,7 +3873,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateProvider {\n provider(createProviderInput: {\n # Assuming applicationId 500 is invalid\n applicationId: 500,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", + "query": "mutation CreateProvider {\n provider(createProviderInput: {\n # Assuming applicationId 500 is invalid\n applicationId: 500,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n providerId\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", "variables": "" } }, @@ -3936,7 +3936,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 5,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", + "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 5,\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n providerId\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", "variables": "" } }, @@ -3994,7 +3994,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 5,\n unknownValue: \"Some unknown parameter\",\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", + "query": "mutation CreateProvider {\n provider(createProviderInput: {\n applicationId: 5,\n unknownValue: \"Some unknown parameter\",\n channelType: 2,\n configuration: {},\n isEnabled: 1,\n name: \"Mailgun PineStem\",\n userId: 1,\n }) {\n providerId\n applicationId\n channelType\n configuration\n isEnabled\n name\n userId\n createdOn\n updatedOn\n status\n }\n}", "variables": "" } }, From bbed12eb7057e1f4c0b105a104faf2ccd967530c Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Fri, 4 Oct 2024 11:51:38 +0530 Subject: [PATCH 7/7] feat: fetch user from context --- .../applications/applications.resolver.ts | 8 ++--- .../applications/applications.service.ts | 30 ++++--------------- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/apps/api/src/modules/applications/applications.resolver.ts b/apps/api/src/modules/applications/applications.resolver.ts index 4df24610..ab2a52af 100644 --- a/apps/api/src/modules/applications/applications.resolver.ts +++ b/apps/api/src/modules/applications/applications.resolver.ts @@ -21,12 +21,8 @@ export class ApplicationsResolver { @Context() context, @Args('createApplicationInput') createApplicationInput: CreateApplicationInput, ): Promise { - const request: Request = context.req; - const authorizationHeader = request.headers['authorization']; - return await this.applicationsService.createApplication( - createApplicationInput, - authorizationHeader, - ); + const requestUserId: number = context.req.userId; + return await this.applicationsService.createApplication(createApplicationInput, requestUserId); } @Query(() => ApplicationResponse, { name: 'applications' }) diff --git a/apps/api/src/modules/applications/applications.service.ts b/apps/api/src/modules/applications/applications.service.ts index cbffb7d8..4df6db51 100644 --- a/apps/api/src/modules/applications/applications.service.ts +++ b/apps/api/src/modules/applications/applications.service.ts @@ -8,8 +8,6 @@ import { Status } from 'src/common/constants/database'; import { ApplicationResponse } from './dto/application-response.dto'; import { QueryOptionsDto } from 'src/common/graphql/dtos/query-options.dto'; import { CoreService } from 'src/common/graphql/services/core.service'; -import { JwtService } from '@nestjs/jwt'; -import { ConfigService } from '@nestjs/config'; import { User } from '../users/entities/user.entity'; @Injectable() @@ -17,9 +15,7 @@ export class ApplicationsService extends CoreService { constructor( @InjectRepository(Application) private readonly applicationsRepository: Repository, - private readonly jwtService: JwtService, private readonly usersService: UsersService, - private configService: ConfigService, ) { super(applicationsRepository); } @@ -34,39 +30,23 @@ export class ApplicationsService extends CoreService { async createApplication( applicationInput: CreateApplicationInput, - authorizationHeader: Request, + requestUserId: number, ): Promise { - const userEntry = await this.getUserEntryFromToken(authorizationHeader); + const userEntryFromContext = await this.getUserEntryFromContext(requestUserId); const newApplicationObject = new Application({ name: applicationInput.name, - userId: userEntry.userId, + userId: userEntryFromContext.userId, }); const application = this.applicationsRepository.create(newApplicationObject); return this.applicationsRepository.save(application); } - async getUserEntryFromToken(authHeader: Request): Promise { + async getUserEntryFromContext(requestUserId: number): Promise { try { - const bearerToken = authHeader.toString(); - - if (!bearerToken || !bearerToken.startsWith('Bearer ')) { - throw new UnauthorizedException('Missing or malformed authorization header'); - } - - const token = bearerToken.slice(7).trim(); - const secret = this.configService.getOrThrow('JWT_SECRET'); - - // Decode the token to get the payload (which includes the user ID) - const decodedToken = this.jwtService.verify(token, { secret }); - - if (!decodedToken || !decodedToken.userId) { - throw new UnauthorizedException('Invalid token'); - } - // Find the related user entry using the user ID from the token - const userEntry = await this.usersService.findByUserId(decodedToken.userId); + const userEntry = await this.usersService.findByUserId(requestUserId); if (!userEntry) { throw new UnauthorizedException('User not found');