Skip to content

Commit 1d4c59b

Browse files
refactor: Add validations by checking ecosystem details present in user's token. (#1578)
* refactor: add validations by checking ecosystem details present in user's token Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: removed unecessary changes Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: get verification template by ecosystemId Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: role access changes for get all ecosystems API Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: get all template by ecosystemId Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: ecosystemRolesGaurd conditions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
1 parent b3fa160 commit 1d4c59b

12 files changed

Lines changed: 185 additions & 76 deletions

File tree

apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ import { Reflector } from '@nestjs/core';
77
import { ResponseMessages } from '@credebl/common/response-messages';
88
import { validate as isValidUUID } from 'uuid';
99

10+
interface EcosystemRoles {
11+
lead?: string[];
12+
member?: string[];
13+
}
14+
15+
interface EcosystemRoleGroup {
16+
ecosystem_role?: EcosystemRoles;
17+
}
18+
19+
interface EcosystemAccess {
20+
[ecosystemId: string]: EcosystemRoleGroup;
21+
}
22+
1023
@Injectable()
1124
export class EcosystemRolesGuard implements CanActivate {
1225
constructor(private readonly reflector: Reflector) {} // eslint-disable-next-line array-callback-return
@@ -43,12 +56,34 @@ export class EcosystemRolesGuard implements CanActivate {
4356
orgId = '';
4457
}
4558

46-
const isPlatformAdmin = user.email === process.env.PLATFORM_ADMIN_EMAIL;
59+
const roles: string[] = [];
60+
61+
const ecosystemAccess: EcosystemAccess | undefined = user?.ecosystem_access;
62+
63+
const hasLead = Object.values(ecosystemAccess || {}).some(
64+
(eco: EcosystemRoleGroup) => 0 < eco?.ecosystem_role?.lead?.length
65+
);
4766

48-
if (user?.ecosystemRoles && requiredRolesNames.some((role: string) => user.ecosystemRoles.includes(role))) {
67+
const hasMember = Object.values(ecosystemAccess || {}).some(
68+
(eco: EcosystemRoleGroup) => 0 < eco?.ecosystem_role?.member?.length
69+
);
70+
71+
if (hasLead && !roles.includes(OrgRoles.ECOSYSTEM_LEAD)) {
72+
roles.push(OrgRoles.ECOSYSTEM_LEAD);
73+
}
74+
75+
if (hasMember && !roles.includes(OrgRoles.ECOSYSTEM_MEMBER)) {
76+
roles.push(OrgRoles.ECOSYSTEM_MEMBER);
77+
}
78+
79+
const ecosystemRoleAccess = requiredRolesNames.some((role) => roles.includes(role));
80+
81+
if (ecosystemRoleAccess) {
4982
return true;
5083
}
5184

85+
const isPlatformAdmin = user.email === process.env.PLATFORM_ADMIN_EMAIL;
86+
5287
if (isPlatformAdmin && requiredRolesNames.includes(OrgRoles.PLATFORM_ADMIN)) {
5388
// eslint-disable-next-line array-callback-return
5489
const isPlatformAdminFlag = user.userOrgRoles.find((orgDetails) => {
@@ -77,6 +112,7 @@ export class EcosystemRolesGuard implements CanActivate {
77112
description: ResponseMessages.errorMessages.forbidden
78113
});
79114
}
115+
80116
return roleAccess;
81117
}
82118

apps/api-gateway/src/authz/jwt-payload.interface.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
export interface ResourceAccess {
2+
roles: string[];
3+
}
4+
5+
export interface EcosystemRole {
6+
lead?: string[];
7+
member?: string[];
8+
}
9+
10+
export interface EcosystemAccess {
11+
ecosystem_role: EcosystemRole;
12+
}
13+
114
export interface JwtPayload {
215
iss: string;
316
sub: string;
@@ -10,4 +23,7 @@ export interface JwtPayload {
1023
permissions: string[];
1124
email?: string;
1225
sid: string;
26+
27+
resource_access?: Record<string, ResourceAccess>;
28+
ecosystem_access?: Record<string, EcosystemAccess>;
1329
}

apps/api-gateway/src/authz/jwt.strategy.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
/* eslint-disable camelcase */
12
import * as dotenv from 'dotenv';
23
import * as jwt from 'jsonwebtoken';
34

5+
import { CommonConstants, uuidRegex } from '@credebl/common/common.constant';
46
import { ExtractJwt, Strategy } from 'passport-jwt';
57
import { Injectable, Logger, NotFoundException, UnauthorizedException } from '@nestjs/common';
68

79
import { AuthzService } from './authz.service';
8-
import { CommonConstants, uuidRegex } from '@credebl/common/common.constant';
9-
import { EcosystemService } from '../ecosystem/ecosystem.service';
1010
import { IOrganization } from '@credebl/common/interfaces/organization.interface';
1111
import { JwtPayload } from './jwt-payload.interface';
1212
import { OrganizationService } from '../organization/organization.service';
@@ -24,8 +24,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
2424
constructor(
2525
private readonly usersService: UserService,
2626
private readonly organizationService: OrganizationService,
27-
private readonly authzService: AuthzService,
28-
private readonly ecosystemService: EcosystemService
27+
private readonly authzService: AuthzService
2928
) {
3029
super({
3130
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
@@ -74,20 +73,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
7473
if (payload?.email) {
7574
userInfo = await this.usersService.getUserByUserIdInKeycloak(payload?.email);
7675
}
77-
let ecosystemRole = null;
78-
if (userInfo?.id) {
79-
try {
80-
const user = await this.ecosystemService.getUserByKeycloakId(userInfo.id);
81-
if (user?.id) {
82-
const ecosystem = await this.ecosystemService.getEcosystemDetailsByUserId(user.id);
83-
if (ecosystem?.id) {
84-
ecosystemRole = await this.ecosystemService.getEcosystemOrgDetailsByUserId(user.id, ecosystem.id);
85-
}
86-
}
87-
} catch (error) {
88-
this.logger.warn('Failed to fetch ecosystem roles', JSON.stringify(error));
89-
}
90-
}
9176

9277
if (payload.hasOwnProperty('client_id') && uuidRegex.test(payload['client_id'])) {
9378
const orgDetails: IOrganization = await this.organizationService.findOrganizationOwner(payload['client_id']);
@@ -122,11 +107,8 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
122107
userDetails['userRole'] = userInfo?.['attributes']?.userRole;
123108
}
124109

125-
if (Array.isArray(ecosystemRole) && 0 < ecosystemRole.length) {
126-
const ecosystemRoleList = [
127-
...new Set(ecosystemRole.map((record: { ecosystemRole: { name: string } }) => record.ecosystemRole.name))
128-
];
129-
userDetails.ecosystemRoles = ecosystemRoleList;
110+
if (userDetails && payload?.ecosystem_access) {
111+
userDetails.ecosystem_access = payload.ecosystem_access;
130112
}
131113

132114
return {

apps/api-gateway/src/ecosystem/ecosystem.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class EcosystemController {
9595
return res.status(HttpStatus.CREATED).json(finalResponse);
9696
}
9797

98-
@Post('/invitation/status')
98+
@Put('/invitation/status')
9999
@ApiOperation({
100100
summary: 'Update invitation status',
101101
description: 'Updates the status of an existing ecosystem invitation (accept or reject).'
@@ -210,7 +210,7 @@ export class EcosystemController {
210210
required: true,
211211
type: String
212212
})
213-
@Roles(OrgRoles.PLATFORM_ADMIN, OrgRoles.ECOSYSTEM_LEAD)
213+
@Roles(OrgRoles.PLATFORM_ADMIN, OrgRoles.ECOSYSTEM_LEAD, OrgRoles.ECOSYSTEM_MEMBER)
214214
async getEcosystems(
215215
@User() reqUser: user,
216216
@Res() res: Response,

apps/api-gateway/src/ecosystem/ecosystem.service.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,15 @@ export class EcosystemService {
186186
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-intents', { ecosystemId, intentId, pageDetail });
187187
}
188188

189-
async getVerificationTemplates(orgId: string, pageDetail: IPaginationSortingDto): Promise<PaginatedResponse<object>> {
189+
async getVerificationTemplates(
190+
ecosystemId: string,
191+
pageDetail: IPaginationSortingDto,
192+
orgId?: string
193+
): Promise<PaginatedResponse<object>> {
190194
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-verification-templates-by-org-id', {
191-
orgId,
192-
pageDetail
195+
ecosystemId,
196+
pageDetail,
197+
orgId
193198
});
194199
}
195200

apps/api-gateway/src/ecosystem/intent/intent.controller.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export class IntentController {
8585
@Body() createIntentDto: CreateIntentDto,
8686
@Param(
8787
'ecosystemId',
88+
TrimStringParamPipe,
8889
new ParseUUIDPipe({
8990
exceptionFactory: (): Error => {
9091
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId);
@@ -274,38 +275,48 @@ export class IntentController {
274275
/**
275276
* Get template details by org ID
276277
*/
277-
@Get('/org/:orgId/verification-templates')
278+
@Get('/ecosystem/:ecosystemId/verification-templates')
278279
@Roles(OrgRoles.ECOSYSTEM_LEAD, OrgRoles.OWNER)
279280
@UseGuards(AuthGuard('jwt'), EcosystemRolesGuard)
280281
@ApiBearerAuth()
281282
@ApiOperation({
282-
summary: 'Get template details by orgId',
283-
description: 'Retrieve verification template details by orgId'
283+
summary: 'Get verification templates by ecosystemId',
284+
description: 'Retrieve verification templates of all member orgs in ecosystem'
284285
})
285-
@ApiParam({
286+
@ApiQuery({
286287
name: 'orgId',
287-
required: true,
288+
required: false,
288289
description: 'Organization ID'
289290
})
290291
@ApiResponse({
291292
status: HttpStatus.OK,
292293
description: 'Template details fetched successfully'
293294
})
294-
async getTemplateByIntentId(
295+
async getTemplatesByEcosystemId(
295296
@Param(
296-
'orgId',
297+
'ecosystemId',
297298
TrimStringParamPipe,
298299
new ParseUUIDPipe({
299300
exceptionFactory: (): Error => {
300-
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId);
301+
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidEcosystemId);
301302
}
302303
})
303304
)
304-
orgId: string,
305+
ecosystemId: string,
306+
@Query() pageDto: PaginationDto,
305307
@Res() res: Response,
306-
@Query() pageDto: PaginationDto
308+
@Query(
309+
'orgId',
310+
new ParseUUIDPipe({
311+
optional: true,
312+
exceptionFactory: (): Error => {
313+
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId);
314+
}
315+
})
316+
)
317+
orgId?: string
307318
): Promise<Response> {
308-
const templates = await this.ecosystemService.getVerificationTemplates(orgId, pageDto);
319+
const templates = await this.ecosystemService.getVerificationTemplates(ecosystemId, pageDto, orgId);
309320

310321
return res.status(HttpStatus.OK).json({
311322
statusCode: HttpStatus.OK,

apps/api-gateway/src/platform/platform.controller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export class PlatformController {
269269
description: 'Invitations fetched successfully'
270270
})
271271
@Roles(OrgRoles.PLATFORM_ADMIN)
272-
@UseGuards(AuthGuard('jwt'), OrgRolesGuard, EcosystemFeatureGuard)
272+
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
273273
@ApiBearerAuth()
274274
async getInvitations(
275275
@User() reqUser: user,
@@ -324,7 +324,6 @@ export class PlatformController {
324324
status: HttpStatus.OK,
325325
description: 'Ecosystem status fetched successfully'
326326
})
327-
@Roles(OrgRoles.PLATFORM_ADMIN)
328327
@UseGuards(AuthGuard('jwt'), EcosystemRolesGuard)
329328
@ApiBearerAuth()
330329
async getEcosystemEnableStatus(@Res() res: Response): Promise<Response> {

apps/ecosystem/interfaces/ecosystem.interfaces.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { EcosystemOrgStatus, InvitationViewRole } from '@credebl/enum/enum';
2-
import { Prisma, PrismaClient } from '@prisma/client';
2+
import { Prisma, PrismaClient, SignerOption } from '@prisma/client';
33

44
import { CommonTableColumns } from '@credebl/common/interfaces/interface';
55
import { OrgRoles } from 'libs/org-roles/enums';
@@ -244,3 +244,18 @@ export interface IGetEcosystemOrgsResponse {
244244
name: string | null;
245245
} | null;
246246
}
247+
248+
export interface IVerificationTemplateList {
249+
id: string;
250+
name: string;
251+
orgId: string;
252+
createDateTime: Date;
253+
createdBy: string;
254+
lastChangedDateTime: Date;
255+
lastChangedBy: string;
256+
signerOption: SignerOption;
257+
organisation: {
258+
id: string;
259+
name: string;
260+
};
261+
}

0 commit comments

Comments
 (0)