Skip to content

Commit f41357a

Browse files
committed
feat/wip
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
2 parents 29780a0 + 9c04388 commit f41357a

12 files changed

Lines changed: 2214 additions & 2662 deletions

File tree

Dockerfiles/Dockerfile.ecosystem

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Stage 1: Build the application
2-
FROM node:24-alpine3.21 AS build
2+
FROM oven/bun:1.3-alpine AS build
33
RUN apk update && apk upgrade && apk add --no-cache openssl \
44
&& rm -rf /var/cache/apk/*
55
RUN npm install -g pnpm@9.15.3 --ignore-scripts
66
WORKDIR /app
7-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
7+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml bun.lock ./
88
ENV PUPPETEER_SKIP_DOWNLOAD=true
9-
RUN pnpm i --frozen-lockfile --ignore-scripts
9+
RUN bun i --frozen-lockfile --ignore-scripts
1010
COPY . .
11-
RUN cd libs/prisma-service && npx prisma generate
12-
RUN pnpm run build ecosystem
13-
RUN pnpm prune --prod
11+
RUN cd libs/prisma-service && bunx prisma generate
12+
RUN bun --bun run build ecosystem
13+
RUN bun prune --prod
1414

1515
# Stage 2: Create the final image
16-
FROM node:24-alpine3.21
16+
FROM oven/bun:1.3-alpine
1717
RUN apk update && apk upgrade && apk add --no-cache openssl \
1818
&& rm -rf /var/cache/apk/* \
1919
&& addgroup -g 1001 -S nodejs \
@@ -23,4 +23,4 @@ COPY --from=build /app/dist/apps/ecosystem/ ./dist/apps/ecosystem/
2323
COPY --from=build /app/libs/ ./libs/
2424
COPY --from=build /app/node_modules ./node_modules
2525
USER nextjs
26-
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && cd ../.. && node dist/apps/ecosystem/main.js"]
26+
CMD ["sh", "-c", "cd libs/prisma-service && bunx prisma generate && bunx prisma migrate deploy && cd ../.. && bun --bun dist/apps/ecosystem/main.js"]
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
# Stage 1: Build the application
2-
FROM node:24-alpine3.21 AS build
2+
FROM oven/bun:1.3-alpine AS build
33
RUN apk update && apk upgrade && apk add --no-cache openssl && rm -rf /var/cache/apk/*
4-
RUN npm install -g pnpm@9.15.3 --ignore-scripts
54
WORKDIR /app
6-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
5+
COPY package.json bun.lock ./
76
ENV PUPPETEER_SKIP_DOWNLOAD=true
8-
RUN pnpm i --frozen-lockfile --ignore-scripts
7+
RUN bun install
98
COPY . .
109
RUN cd libs/prisma-service && npx prisma generate
11-
RUN pnpm run build oid4vc-issuance
12-
RUN pnpm prune --prod
10+
RUN bun --bun run build oid4vc-issuance
11+
RUN bun prune --prod
1312

1413
# Stage 2: Create the final image
15-
FROM node:24-alpine3.21
14+
FROM oven/bun:1.3-alpine
1615
RUN apk update && apk upgrade && apk add --no-cache openssl \
1716
&& rm -rf /var/cache/apk/* \
1817
&& addgroup -g 1001 -S nodejs \
@@ -22,4 +21,4 @@ COPY --from=build /app/dist/apps/oid4vc-issuance/ ./dist/apps/oid4vc-issuance/
2221
COPY --from=build /app/libs/ ./libs/
2322
COPY --from=build /app/node_modules ./node_modules
2423
USER nextjs
25-
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && cd ../.. && node dist/apps/oid4vc-issuance/main.js"]
24+
CMD ["sh", "-c", "cd libs/prisma-service && bunx prisma generate && bunx prisma migrate deploy && cd ../.. && bun --bun dist/apps/oid4vc-issuance/main.js"]
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
# Stage 1: Build the application
2-
FROM node:24-alpine3.21 AS build
2+
FROM oven/bun:1.3-alpine AS build
33
RUN apk update && apk upgrade && apk add --no-cache openssl \
44
&& rm -rf /var/cache/apk/*
5-
RUN npm install -g pnpm@9.15.3 --ignore-scripts
65
WORKDIR /app
7-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
6+
COPY package.json bun.lock ./
87
ENV PUPPETEER_SKIP_DOWNLOAD=true
9-
RUN pnpm i --frozen-lockfile --ignore-scripts
8+
RUN bun install
109
COPY . .
11-
RUN cd libs/prisma-service && npx prisma generate
12-
RUN pnpm run build oid4vc-verification
13-
RUN pnpm prune --prod
10+
RUN cd libs/prisma-service && bunx prisma generate
11+
RUN bun run build oid4vc-verification
12+
RUN bun prune --prod
1413

1514
# Stage 2: Create the final image
16-
FROM node:24-alpine3.21
15+
FROM oven/bun:1.3-alpine
1716
RUN apk update && apk upgrade && apk add --no-cache openssl \
1817
&& rm -rf /var/cache/apk/* \
1918
&& addgroup -g 1001 -S nodejs \
@@ -23,4 +22,4 @@ COPY --from=build /app/dist/apps/oid4vc-verification/ ./dist/apps/oid4vc-verific
2322
COPY --from=build /app/libs/ ./libs/
2423
COPY --from=build /app/node_modules ./node_modules
2524
USER nextjs
26-
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && cd ../.. && node dist/apps/oid4vc-verification/main.js"]
25+
CMD ["sh", "-c", "cd libs/prisma-service && bunx prisma generate && npx prisma migrate deploy && cd ../.. && bun --bun dist/apps/oid4vc-verification/main.js"]

Dockerfiles/Dockerfile.seed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:24-alpine3.21
1+
FROM node:24-alpine3.23
22
RUN apk update && apk upgrade && apk add --no-cache \
33
postgresql-client \
44
openssl \

Dockerfiles/Dockerfile.x509

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
# Stage 1: Build the application
2-
FROM node:24-alpine3.21 AS build
2+
FROM oven/bun:1.3-alpine
33
RUN apk update && apk upgrade && apk add --no-cache openssl \
44
&& rm -rf /var/cache/apk/*
5-
RUN npm install -g pnpm@9.15.3 --ignore-scripts
65
WORKDIR /app
7-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
6+
COPY package.json bun.lock ./
87
ENV PUPPETEER_SKIP_DOWNLOAD=true
9-
RUN pnpm i --frozen-lockfile --ignore-scripts
8+
RUN bun install
109
COPY . .
1110
RUN cd libs/prisma-service && npx prisma generate
12-
RUN pnpm run build x509
13-
RUN pnpm prune --prod
11+
RUN bun run build x509
12+
RUN bun prune --prod
1413

1514
# Stage 2: Create the final image
16-
FROM node:24-alpine3.21
15+
FROM oven/bun:1.3-alpine
1716
RUN apk update && apk upgrade && apk add --no-cache openssl \
1817
&& rm -rf /var/cache/apk/* \
1918
&& addgroup -g 1001 -S nodejs \
@@ -23,4 +22,4 @@ COPY --from=build /app/dist/apps/x509/ ./dist/apps/x509/
2322
COPY --from=build /app/libs/ ./libs/
2423
COPY --from=build /app/node_modules ./node_modules
2524
USER nextjs
26-
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && cd ../.. && node dist/apps/x509/main.js"]
25+
CMD ["sh", "-c", "cd libs/prisma-service && bunx prisma generate && bunx prisma migrate deploy && cd ../.. && bun --bun dist/apps/x509/main.js"]

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

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import { ResponseMessages } from '@credebl/common/response-messages';
88
import { validate as isValidUUID } from 'uuid';
99
@Injectable()
1010
export class OrgRolesGuard implements CanActivate {
11-
constructor(private reflector: Reflector) { } // eslint-disable-next-line array-callback-return
12-
11+
constructor(private reflector: Reflector) {}
1312

1413
private logger = new Logger('Org Role Guard');
1514
async canActivate(context: ExecutionContext): Promise<boolean> {
@@ -25,43 +24,48 @@ export class OrgRolesGuard implements CanActivate {
2524

2625
const req = context.switchToHttp().getRequest();
2726
const { user } = req;
28-
27+
2928
if (user?.userRole && user?.userRole.includes('holder')) {
3029
throw new ForbiddenException('This role is a holder.');
3130
}
3231

3332
req.params.orgId = req.params?.orgId ? req.params?.orgId?.trim() : '';
3433
req.query.orgId = req.query?.orgId ? req.query?.orgId?.trim() : '';
35-
req.body.orgId = req.body?.orgId ? req.body?.orgId?.trim() : '';
36-
37-
const orgId = req.params.orgId || req.query.orgId || req.body.orgId;
34+
if (req.body) {
35+
req.body.orgId = req.body.orgId?.trim() || '';
36+
}
3837

39-
if (orgId) {
38+
const orgId = req.params.orgId || req.query.orgId || req.body?.orgId;
39+
if (orgId) {
40+
if (!isValidUUID(orgId)) {
41+
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
42+
}
4043

41-
if (!isValidUUID(orgId)) {
42-
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
43-
}
44-
44+
if (user.hasOwnProperty('resource_access') && user.resource_access[orgId]) {
45+
const orgRoles: string[] = user.resource_access[orgId].roles;
46+
const roleAccess = requiredRoles.some((role) => orgRoles.includes(role));
4547

46-
if (user.hasOwnProperty('resource_access') && user.resource_access[orgId]) {
47-
const orgRoles: string[] = user.resource_access[orgId].roles;
48-
const roleAccess = requiredRoles.some((role) => orgRoles.includes(role));
49-
50-
if (!roleAccess) {
51-
throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, { cause: new Error(), description: ResponseMessages.errorMessages.forbidden });
52-
}
53-
return roleAccess;
48+
if (!roleAccess) {
49+
throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, {
50+
cause: new Error(),
51+
description: ResponseMessages.errorMessages.forbidden
52+
});
5453
}
54+
return roleAccess;
55+
}
5556

5657
const specificOrg = user.userOrgRoles.find((orgDetails) => {
5758
if (!orgDetails.orgId) {
5859
return false;
5960
}
6061
return orgDetails.orgId.toString().trim() === orgId.toString().trim();
6162
});
62-
63+
6364
if (!specificOrg) {
64-
throw new ForbiddenException(ResponseMessages.organisation.error.orgNotMatch, { cause: new Error(), description: ResponseMessages.errorMessages.forbidden });
65+
throw new ForbiddenException(ResponseMessages.organisation.error.orgNotMatch, {
66+
cause: new Error(),
67+
description: ResponseMessages.errorMessages.forbidden
68+
});
6569
}
6670

6771
user.selectedOrg = specificOrg;
@@ -71,9 +75,7 @@ export class OrgRolesGuard implements CanActivate {
7175
return orgRoleItem.orgRole.name;
7276
}
7377
});
74-
75-
} else if (requiredRolesNames.includes(OrgRoles.PLATFORM_ADMIN)) {
76-
78+
} else if (requiredRolesNames.includes(OrgRoles.PLATFORM_ADMIN)) {
7779
// eslint-disable-next-line array-callback-return
7880
const isPlatformAdmin = user.userOrgRoles.find((orgDetails) => {
7981
if (orgDetails.orgRole.name === OrgRoles.PLATFORM_ADMIN) {
@@ -86,17 +88,19 @@ export class OrgRolesGuard implements CanActivate {
8688
}
8789

8890
return false;
89-
9091
} else {
9192
throw new BadRequestException('Please provide valid orgId');
9293
}
9394

9495
// Sending user friendly message if a user attempts to access an API that is inaccessible to their role
9596
const roleAccess = requiredRoles.some((role) => user.selectedOrg?.orgRoles.includes(role));
9697
if (!roleAccess) {
97-
throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, { cause: new Error(), description: ResponseMessages.errorMessages.forbidden });
98+
throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, {
99+
cause: new Error(),
100+
description: ResponseMessages.errorMessages.forbidden
101+
});
98102
}
99103

100104
return roleAccess;
101105
}
102-
}
106+
}

apps/ecosystem/src/ecosystem.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CacheModule } from '@nestjs/cache-manager';
55
import { ClientRegistrationModule } from '@credebl/client-registration';
66
import { CommonConstants } from '@credebl/common/common.constant';
77
import { CommonModule } from '@credebl/common';
8+
import { KeycloakUrlModule } from '@credebl/keycloak-url';
89
import { ContextInterceptorModule } from '@credebl/context/contextInterceptorModule';
910
import { EcosystemController } from './ecosystem.controller';
1011
import { EcosystemRepository } from '../repositories/ecosystem.repository';
@@ -34,6 +35,7 @@ import { getNatsOptions } from '@credebl/common/nats.config';
3435
}
3536
]),
3637
CommonModule,
38+
KeycloakUrlModule,
3739
GlobalConfigModule,
3840
LoggerModule,
3941
PlatformConfig,

apps/ecosystem/src/ecosystem.service.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515

1616
import { ClientProxy, RpcException } from '@nestjs/microservices';
1717
import { ClientRegistrationService } from '@credebl/client-registration';
18+
import { CommonService } from '@credebl/common';
19+
import { KeycloakUrlService } from '@credebl/keycloak-url';
1820
import { EcosystemRepository } from 'apps/ecosystem/repositories/ecosystem.repository';
1921
import { validateNoticeUrl } from './ecosystem.helper';
2022
import { CreateEcosystemInviteTemplate } from '../templates/create-ecosystem.templates';
@@ -32,7 +34,8 @@ import {
3234
EcosystemServiceRole,
3335
Invitation,
3436
InvitationViewRole,
35-
InviteType
37+
InviteType,
38+
UnmanagedAttributePolicy
3639
} from '@credebl/enum/enum';
3740

3841
import {
@@ -65,7 +68,9 @@ export class EcosystemService {
6568
private readonly emailService: EmailService,
6669
private readonly organizationRepository: OrganizationRepository,
6770
private readonly userRepository: UserRepository,
68-
private readonly clientRegistrationService: ClientRegistrationService
71+
private readonly clientRegistrationService: ClientRegistrationService,
72+
private readonly commonService: CommonService,
73+
private readonly keycloakUrlService: KeycloakUrlService
6974
) {}
7075

7176
private readonly logger = new Logger(EcosystemService.name);
@@ -968,6 +973,10 @@ export class EcosystemService {
968973
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidEcosystemEnabledFlag);
969974
}
970975

976+
if (isEcosystemEnabled) {
977+
await this.checkUnmanagedAttributeEnabled();
978+
}
979+
971980
await this.ecosystemRepository.updateEcosystemConfig({
972981
isEcosystemEnabled,
973982
userId: platformAdminId
@@ -978,6 +987,24 @@ export class EcosystemService {
978987
};
979988
}
980989

990+
private async checkUnmanagedAttributeEnabled(): Promise<void> {
991+
const realmName = process.env.KEYCLOAK_REALM;
992+
const token = await this.clientRegistrationService.getPlatformManagementToken();
993+
994+
if (!realmName || !token) {
995+
throw new InternalServerErrorException(ResponseMessages.ecosystem.error.keycloakRealmOrTokenMissing);
996+
}
997+
998+
const userProfileUrl = await this.keycloakUrlService.GetUserProfileURL(realmName);
999+
const profileConfig = await this.commonService.httpGet(userProfileUrl, {
1000+
headers: { authorization: `Bearer ${token}` }
1001+
});
1002+
1003+
if (!profileConfig || UnmanagedAttributePolicy.ENABLED !== profileConfig.unmanagedAttributePolicy) {
1004+
throw new BadRequestException(ResponseMessages.ecosystem.error.unmanagedAttributeNotEnabled);
1005+
}
1006+
}
1007+
9811008
async getDashboardCountEcosystem(): Promise<IPlatformDashboardCount> {
9821009
return this.ecosystemRepository.getDashBoardCountPlatformAdmin();
9831010
}

libs/common/src/response-messages/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ export const ResponseMessages = {
299299
ecosystemMemberStatusFail: 'Failed to update ecosystem member status',
300300
failedEcosystemOrgUpdate: 'Failed to update ecosystem org',
301301
invitationMemberfail: 'Failed to fetch invitation members',
302-
invalidEcosystemEnabledFlag: 'Invalid ecosystem enabled flag'
302+
invalidEcosystemEnabledFlag: 'Invalid ecosystem enabled flag',
303+
keycloakRealmOrTokenMissing: 'Keycloak realm is not configured or management token could not be obtained',
304+
unmanagedAttributeNotEnabled:
305+
'Unmanaged attributes are not enabled in Keycloak. Please enable unmanaged attributes in Keycloak realm settings (Realm Settings > User Profile > Unmanaged Attributes) before enabling the ecosystem feature'
303306
}
304307
},
305308
schema: {

0 commit comments

Comments
 (0)