@platform-mesh/portal-server-lib 0.9.95 → 0.9.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/portal-options/index.d.ts +4 -0
  2. package/dist/portal-options/index.js +4 -0
  3. package/dist/portal-options/index.js.map +1 -1
  4. package/dist/portal-options/logout-callback.service.js +16 -8
  5. package/dist/portal-options/logout-callback.service.js.map +1 -1
  6. package/dist/portal-options/service-providers/content-configuration-service-providers.service.d.ts +4 -1
  7. package/dist/portal-options/service-providers/content-configuration-service-providers.service.js +14 -3
  8. package/dist/portal-options/service-providers/content-configuration-service-providers.service.js.map +1 -1
  9. package/dist/portal-options/service-providers/kubernetes-service-providers.service.d.ts +4 -2
  10. package/dist/portal-options/service-providers/kubernetes-service-providers.service.js +10 -4
  11. package/dist/portal-options/service-providers/kubernetes-service-providers.service.js.map +1 -1
  12. package/dist/portal-options/services/kcp-k8s.service.d.ts +0 -1
  13. package/dist/portal-options/services/kcp-k8s.service.js +4 -13
  14. package/dist/portal-options/services/kcp-k8s.service.js.map +1 -1
  15. package/dist/portal-options/services/permissions/adapters/authz-webhook.service.d.ts +14 -0
  16. package/dist/portal-options/services/permissions/adapters/authz-webhook.service.js +183 -0
  17. package/dist/portal-options/services/permissions/adapters/authz-webhook.service.js.map +1 -0
  18. package/dist/portal-options/services/permissions/models/permissions.model.d.ts +51 -0
  19. package/dist/portal-options/services/permissions/models/permissions.model.js +2 -0
  20. package/dist/portal-options/services/permissions/models/permissions.model.js.map +1 -0
  21. package/dist/portal-options/services/permissions/permissions-proxy.service.d.ts +9 -0
  22. package/dist/portal-options/services/permissions/permissions-proxy.service.js +48 -0
  23. package/dist/portal-options/services/permissions/permissions-proxy.service.js.map +1 -0
  24. package/dist/portal-options/services/permissions/permissions.controller.d.ts +9 -0
  25. package/dist/portal-options/services/permissions/permissions.controller.js +49 -0
  26. package/dist/portal-options/services/permissions/permissions.controller.js.map +1 -0
  27. package/dist/portal-options/utils/account-hierarchy-resolver.js +7 -3
  28. package/dist/portal-options/utils/account-hierarchy-resolver.js.map +1 -1
  29. package/dist/portal-options/utils/build-workspace-path.util.d.ts +1 -0
  30. package/dist/portal-options/utils/build-workspace-path.util.js +4 -0
  31. package/dist/portal-options/utils/build-workspace-path.util.js.map +1 -0
  32. package/dist/portal-options/utils/extract-resource-definitions.util.d.ts +7 -0
  33. package/dist/portal-options/utils/extract-resource-definitions.util.js +33 -0
  34. package/dist/portal-options/utils/extract-resource-definitions.util.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/portal-options/index.ts +4 -0
  37. package/src/portal-options/logout-callback.service.spec.ts +57 -1
  38. package/src/portal-options/logout-callback.service.ts +34 -8
  39. package/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts +8 -1
  40. package/src/portal-options/service-providers/content-configuration-service-providers.service.ts +17 -6
  41. package/src/portal-options/service-providers/kubernetes-service-providers.service.spec.ts +13 -4
  42. package/src/portal-options/service-providers/kubernetes-service-providers.service.ts +21 -8
  43. package/src/portal-options/services/kcp-k8s.service.ts +4 -15
  44. package/src/portal-options/services/permissions/adapters/authz-webhook.service.spec.ts +500 -0
  45. package/src/portal-options/services/permissions/adapters/authz-webhook.service.ts +238 -0
  46. package/src/portal-options/services/permissions/models/permissions.model.ts +56 -0
  47. package/src/portal-options/services/permissions/permissions-proxy.service.spec.ts +232 -0
  48. package/src/portal-options/services/permissions/permissions-proxy.service.ts +58 -0
  49. package/src/portal-options/services/permissions/permissions.controller.spec.ts +143 -0
  50. package/src/portal-options/services/permissions/permissions.controller.ts +31 -0
  51. package/src/portal-options/utils/account-hierarchy-resolver.ts +13 -8
  52. package/src/portal-options/utils/build-workspace-path.util.spec.ts +59 -0
  53. package/src/portal-options/utils/build-workspace-path.util.ts +21 -0
  54. package/src/portal-options/utils/extract-resource-definitions.util.spec.ts +303 -0
  55. package/src/portal-options/utils/extract-resource-definitions.util.ts +62 -0
  56. package/tsconfig.json +2 -1
@@ -0,0 +1,48 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Injectable } from '@nestjs/common';
11
+ import { extractResourceDefinitions } from '../../utils/extract-resource-definitions.util.js';
12
+ import { AuthzWebhookService } from './adapters/authz-webhook.service.js';
13
+ let PermissionsProxyService = class PermissionsProxyService {
14
+ authzWebhook;
15
+ constructor(authzWebhook) {
16
+ this.authzWebhook = authzWebhook;
17
+ }
18
+ async resolvePermissions(token, organization, accountPath, contentConfigurations) {
19
+ const resourceDefinitions = extractResourceDefinitions(contentConfigurations);
20
+ const checks = resourceDefinitions
21
+ .map((rd) => rd.permissionsDefinition)
22
+ .filter((pd) => !!pd && pd.resourceActions.length > 0)
23
+ .map((pd) => ({
24
+ resource: pd.resource,
25
+ group: pd.group,
26
+ actions: pd.resourceActions,
27
+ }));
28
+ if (!checks.length) {
29
+ return undefined;
30
+ }
31
+ const req = {
32
+ token,
33
+ organization,
34
+ accountPath,
35
+ checks,
36
+ };
37
+ return this.authzWebhook.checkActionsForResource(req);
38
+ }
39
+ async checkResourceInstance(req) {
40
+ return this.authzWebhook.checkActionsForInstance(req);
41
+ }
42
+ };
43
+ PermissionsProxyService = __decorate([
44
+ Injectable(),
45
+ __metadata("design:paramtypes", [AuthzWebhookService])
46
+ ], PermissionsProxyService);
47
+ export { PermissionsProxyService };
48
+ //# sourceMappingURL=permissions-proxy.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions-proxy.service.js","sourceRoot":"","sources":["../../../../src/portal-options/services/permissions/permissions-proxy.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,0BAA0B,EAAE,MAAM,kDAAkD,CAAC;AAC9F,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAQnE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,YAAiC;QAAjC,iBAAY,GAAZ,YAAY,CAAqB;IAAG,CAAC;IAGlE,KAAK,CAAC,kBAAkB,CACtB,KAAa,EACb,YAAoB,EACpB,WAAmB,EACnB,qBAA6C;QAE7C,MAAM,mBAAmB,GAAG,0BAA0B,CACpD,qBAAqB,CACtB,CAAC;QAEF,MAAM,MAAM,GAAG,mBAAmB;aAC/B,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,qBAAqB,CAAC;aACrC,MAAM,CACL,CAAC,EAAE,EAA+B,EAAE,CAClC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CACxC;aACA,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACZ,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,KAAK,EAAE,EAAE,CAAC,KAAK;YACf,OAAO,EAAE,EAAE,CAAC,eAAe;SAC5B,CAAC,CAAC,CAAC;QAEN,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAyB;YAChC,KAAK;YACL,YAAY;YACZ,WAAW;YACX,MAAM;SACP,CAAC;QAEF,OAAO,IAAI,CAAC,YAAY,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;IAGD,KAAK,CAAC,qBAAqB,CACzB,GAAyB;QAEzB,OAAO,IAAI,CAAC,YAAY,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;CACF,CAAA;AA9CY,uBAAuB;IADnC,UAAU,EAAE;qCAEgC,mBAAmB;GADnD,uBAAuB,CA8CnC"}
@@ -0,0 +1,9 @@
1
+ import { HeaderParserService } from '@openmfp/portal-server-lib';
2
+ import { AuthorizationRequest, Permission } from './models/permissions.model.js';
3
+ import { PermissionsProxyService } from './permissions-proxy.service.js';
4
+ export declare class PermissionsController {
5
+ private readonly permissionsProxyService;
6
+ private readonly headerParser;
7
+ constructor(permissionsProxyService: PermissionsProxyService, headerParser: HeaderParserService);
8
+ checkResource(request: Request, body: AuthorizationRequest): Promise<Permission[]>;
9
+ }
@@ -0,0 +1,49 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Body, Controller, Post, Req, UnauthorizedException } from '@nestjs/common';
14
+ import { HeaderParserService } from '@openmfp/portal-server-lib';
15
+ import { PermissionsProxyService } from './permissions-proxy.service.js';
16
+ let PermissionsController = class PermissionsController {
17
+ permissionsProxyService;
18
+ headerParser;
19
+ constructor(permissionsProxyService, headerParser) {
20
+ this.permissionsProxyService = permissionsProxyService;
21
+ this.headerParser = headerParser;
22
+ }
23
+ async checkResource(request, body) {
24
+ const token = this.headerParser.extractBearerToken(request);
25
+ if (!token) {
26
+ throw new UnauthorizedException();
27
+ }
28
+ const result = await this.permissionsProxyService.checkResourceInstance({
29
+ ...body,
30
+ token,
31
+ });
32
+ return result ?? [];
33
+ }
34
+ };
35
+ __decorate([
36
+ Post('resource-check'),
37
+ __param(0, Req()),
38
+ __param(1, Body()),
39
+ __metadata("design:type", Function),
40
+ __metadata("design:paramtypes", [Request, Object]),
41
+ __metadata("design:returntype", Promise)
42
+ ], PermissionsController.prototype, "checkResource", null);
43
+ PermissionsController = __decorate([
44
+ Controller('rest/permissions'),
45
+ __metadata("design:paramtypes", [PermissionsProxyService,
46
+ HeaderParserService])
47
+ ], PermissionsController);
48
+ export { PermissionsController };
49
+ //# sourceMappingURL=permissions.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.controller.js","sourceRoot":"","sources":["../../../../src/portal-options/services/permissions/permissions.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAEb;IACA;IAFnB,YACmB,uBAAgD,EAChD,YAAiC;QADjC,4BAAuB,GAAvB,uBAAuB,CAAyB;QAChD,iBAAY,GAAZ,YAAY,CAAqB;IACjD,CAAC;IAGE,AAAN,KAAK,CAAC,aAAa,CACV,OAAgB,EACf,IAA0B;QAElC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAE5D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,qBAAqB,EAAE,CAAC;QACpC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,CAAC;YACtE,GAAG,IAAI;YACP,KAAK;SACN,CAAC,CAAC;QAEH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;CACF,CAAA;AAjBO;IADL,IAAI,CAAC,gBAAgB,CAAC;IAEpB,WAAA,GAAG,EAAE,CAAA;IACL,WAAA,IAAI,EAAE,CAAA;;qCADS,OAAO;;0DAexB;AAvBU,qBAAqB;IADjC,UAAU,CAAC,kBAAkB,CAAC;qCAGe,uBAAuB;QAClC,mBAAmB;GAHzC,qBAAqB,CAwBjC"}
@@ -2,7 +2,7 @@ import { replaceStringDeep } from './replace-string-deep.js';
2
2
  const ACCOUNT_ENTITY_TYPE = 'core_platform-mesh_io_account';
3
3
  export const updateEntityTypeFromAccountPath = (contentConfiguration, accountPath) => {
4
4
  contentConfiguration.luigiConfigFragment.data.nodes.forEach((node) => {
5
- if (!node.entityType.includes(ACCOUNT_ENTITY_TYPE)) {
5
+ if (!node.entityType?.includes(ACCOUNT_ENTITY_TYPE)) {
6
6
  return;
7
7
  }
8
8
  const accountPathParts = accountPath
@@ -15,12 +15,15 @@ export const updateEntityTypeFromAccountPath = (contentConfiguration, accountPat
15
15
  return contentConfiguration;
16
16
  };
17
17
  export const updateAccountNodeChildren = (contentConfiguration, accountPath) => {
18
- const accountChildrenNode = contentConfiguration.luigiConfigFragment.data.nodes[0]?.children?.[0];
18
+ const children = contentConfiguration.luigiConfigFragment.data.nodes[0]?.children;
19
+ const accountChildrenNode = Array.isArray(children) ? children[0] : undefined;
19
20
  if (accountChildrenNode) {
20
21
  const nextHierarchyLevel = accountPath.split(':').filter(Boolean).length + 1;
21
22
  const previousPathSegment = accountChildrenNode.pathSegment;
22
23
  const nextPathSegment = `:${nextHierarchyLevel}_${ACCOUNT_ENTITY_TYPE}Id`;
23
- replaceStringDeep(accountChildrenNode, previousPathSegment, nextPathSegment);
24
+ if (previousPathSegment) {
25
+ replaceStringDeep(accountChildrenNode, previousPathSegment, nextPathSegment);
26
+ }
24
27
  }
25
28
  return contentConfiguration;
26
29
  };
@@ -32,5 +35,6 @@ export const processContentConfigurationForAccountHierarchy = (contentConfigurat
32
35
  }
33
36
  return updateEntityTypeFromAccountPath(contentConfiguration, accountPath);
34
37
  }
38
+ return contentConfiguration;
35
39
  };
36
40
  //# sourceMappingURL=account-hierarchy-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"account-hierarchy-resolver.js","sourceRoot":"","sources":["../../../src/portal-options/utils/account-hierarchy-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAE5D,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,oBAA0C,EAC1C,WAAmB,EACG,EAAE;IACxB,oBAAoB,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACnE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QAED,MAAM,gBAAgB,GAAG,WAAW;aACjC,KAAK,CAAC,GAAG,CAAC;aACV,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC;aAC9B,IAAI,CAAC,GAAG,CAAC,CAAC;QAEb,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CACvC,mBAAmB,EACnB,gBAAgB,CACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,oBAA0C,EAC1C,WAAmB,EACG,EAAE;IACxB,MAAM,mBAAmB,GACvB,oBAAoB,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IAExE,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,kBAAkB,GACtB,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QACpD,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,WAAW,CAAC;QAC5D,MAAM,eAAe,GAAG,IAAI,kBAAkB,IAAI,mBAAmB,IAAI,CAAC;QAE1E,iBAAiB,CACf,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,8CAA8C,GAAG,CAC5D,oBAA0C,EAC1C,OAA4B,EACN,EAAE;IACxB,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACxE,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,oBAAoB,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7C,yBAAyB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;QAED,OAAO,+BAA+B,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC,CAAC"}
1
+ {"version":3,"file":"account-hierarchy-resolver.js","sourceRoot":"","sources":["../../../src/portal-options/utils/account-hierarchy-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAE5D,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,oBAA0C,EAC1C,WAAmB,EACG,EAAE;IACxB,oBAAoB,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACnE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACpD,OAAO;QACT,CAAC;QAED,MAAM,gBAAgB,GAAG,WAAW;aACjC,KAAK,CAAC,GAAG,CAAC;aACV,MAAM,CAAC,OAAO,CAAC;aACf,GAAG,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC;aAC9B,IAAI,CAAC,GAAG,CAAC,CAAC;QAEb,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CACvC,mBAAmB,EACnB,gBAAgB,CACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,oBAA0C,EAC1C,WAAmB,EACG,EAAE;IACxB,MAAM,QAAQ,GACZ,oBAAoB,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;IACnE,MAAM,mBAAmB,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE9E,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,kBAAkB,GACtB,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QACpD,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,WAAW,CAAC;QAC5D,MAAM,eAAe,GAAG,IAAI,kBAAkB,IAAI,mBAAmB,IAAI,CAAC;QAE1E,IAAI,mBAAmB,EAAE,CAAC;YACxB,iBAAiB,CACf,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,CAChB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,8CAA8C,GAAG,CAC5D,oBAA0C,EAC1C,OAA4B,EACN,EAAE;IACxB,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACxE,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,oBAAoB,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7C,yBAAyB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;QAED,OAAO,+BAA+B,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function buildWorkspacePath(segments?: (string | undefined | null)[]): string;
@@ -0,0 +1,4 @@
1
+ export function buildWorkspacePath(segments = []) {
2
+ return segments.reduce((path, segment) => (segment ? `${path}:${segment}` : path), 'root:orgs');
3
+ }
4
+ //# sourceMappingURL=build-workspace-path.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-workspace-path.util.js","sourceRoot":"","sources":["../../../src/portal-options/utils/build-workspace-path.util.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,kBAAkB,CAChC,WAA0C,EAAE;IAE5C,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAC1D,WAAW,CACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { ContentConfiguration } from '@openmfp/portal-server-lib';
2
+ import { PermissionsDefinition } from '../services/permissions/models/permissions.model.js';
3
+ export interface ResourceDefinitionLocal {
4
+ entity: string;
5
+ permissionsDefinition?: PermissionsDefinition;
6
+ }
7
+ export declare function extractResourceDefinitions(contentConfigurations: ContentConfiguration[]): ResourceDefinitionLocal[];
@@ -0,0 +1,33 @@
1
+ function mergeResourceActions(existing, incoming) {
2
+ return [...new Set([...(existing ?? []), ...(incoming ?? [])])];
3
+ }
4
+ function collectFromNodes(nodes, acc) {
5
+ for (const node of nodes) {
6
+ const rd = node.context?.['resourceDefinition'];
7
+ if (rd?.entity !== undefined && rd.permissionsDefinition !== undefined) {
8
+ const existing = acc.get(rd.entity);
9
+ acc.set(rd.entity, {
10
+ ...rd,
11
+ permissionsDefinition: {
12
+ ...rd.permissionsDefinition,
13
+ resourceActions: mergeResourceActions(existing?.permissionsDefinition?.resourceActions, rd.permissionsDefinition.resourceActions),
14
+ },
15
+ });
16
+ }
17
+ const children = node.children;
18
+ if (Array.isArray(children) && children.length > 0) {
19
+ collectFromNodes(children, acc);
20
+ }
21
+ }
22
+ }
23
+ export function extractResourceDefinitions(contentConfigurations) {
24
+ const acc = new Map();
25
+ for (const cc of contentConfigurations) {
26
+ const nodes = cc.luigiConfigFragment?.data?.nodes;
27
+ if (Array.isArray(nodes)) {
28
+ collectFromNodes(nodes, acc);
29
+ }
30
+ }
31
+ return Array.from(acc.values());
32
+ }
33
+ //# sourceMappingURL=extract-resource-definitions.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-resource-definitions.util.js","sourceRoot":"","sources":["../../../src/portal-options/utils/extract-resource-definitions.util.ts"],"names":[],"mappings":"AAQA,SAAS,oBAAoB,CAC3B,QAA8B,EAC9B,QAA8B;IAE9B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAmE,EACnE,GAAyC;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,oBAAoB,CAEjC,CAAC;QAEd,IAAI,EAAE,EAAE,MAAM,KAAK,SAAS,IAAI,EAAE,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;YACvE,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAIpC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE;gBACjB,GAAG,EAAE;gBACL,qBAAqB,EAAE;oBACrB,GAAG,EAAE,CAAC,qBAAqB;oBAC3B,eAAe,EAAE,oBAAoB,CACnC,QAAQ,EAAE,qBAAqB,EAAE,eAAe,EAChD,EAAE,CAAC,qBAAqB,CAAC,eAAe,CACzC;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnD,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,qBAA6C;IAE7C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAmC,CAAC;IAEvD,KAAK,MAAM,EAAE,IAAI,qBAAqB,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,EAAE,CAAC,mBAAmB,EAAE,IAAI,EAAE,KAAK,CAAC;QAClD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,gBAAgB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;AAClC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-mesh/portal-server-lib",
3
- "version": "0.9.95",
3
+ "version": "0.9.97",
4
4
  "author": "Platform Mesh",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -7,3 +7,7 @@ export * from './service-providers/content-configuration-service-providers.servi
7
7
  export * from './service-providers/kubernetes-service-providers.service.js';
8
8
 
9
9
  export * from './services/kcp-k8s.service.js';
10
+ export * from './services/permissions/adapters/authz-webhook.service.js';
11
+ export * from './services/permissions/models/permissions.model.js';
12
+ export * from './services/permissions/permissions-proxy.service.js';
13
+ export * from './services/permissions/permissions.controller.js';
@@ -63,10 +63,36 @@ describe('PMLogoutService', () => {
63
63
  expect(httpService.post).toHaveBeenCalledWith(
64
64
  'https://keycloak/logout',
65
65
  expect.any(URLSearchParams),
66
- { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } },
66
+ {
67
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
68
+ auth: { username: 'client-id', password: 'secret' },
69
+ },
67
70
  );
68
71
  });
69
72
 
73
+ it('should not send the client secret as a form parameter', async () => {
74
+ const mockRequest = {} as Request;
75
+ const mockResponse = {} as Response;
76
+
77
+ (authConfigService.getAuthConfig as jest.Mock).mockResolvedValue({
78
+ clientId: 'client-id',
79
+ clientSecret: 'secret',
80
+ endSessionUrl: 'https://keycloak/logout',
81
+ });
82
+ (cookiesService.getAuthCookie as jest.Mock).mockReturnValue(
83
+ 'refresh-token',
84
+ );
85
+ (httpService.post as jest.Mock).mockReturnValue(of({ data: {} }));
86
+
87
+ await service.handleLogout(mockRequest, mockResponse);
88
+
89
+ const body = (httpService.post as jest.Mock).mock
90
+ .calls[0][1] as URLSearchParams;
91
+ expect(body.get('refresh_token')).toBe('refresh-token');
92
+ expect(body.get('client_secret')).toBeNull();
93
+ expect(body.get('client_id')).toBeNull();
94
+ });
95
+
70
96
  it('should return logoutWithIdToken URL when logout fails', async () => {
71
97
  const mockRequest = {
72
98
  query: {
@@ -105,9 +131,39 @@ describe('PMLogoutService', () => {
105
131
  const result = (service as any).logoutWithIdToken(
106
132
  request,
107
133
  'https://kc/logout',
134
+ 'client-id',
108
135
  );
109
136
  expect(result).toBe(
110
137
  'https://kc/logout?id_token_hint=token123&post_logout_redirect_uri=https%3A%2F%2Fexample.com',
111
138
  );
112
139
  });
140
+
141
+ it('logoutWithIdToken should fall back to client_id when no id token is present', () => {
142
+ const request = {
143
+ query: { post_logout_redirect_uri: 'https://example.com' },
144
+ } as unknown as Request;
145
+
146
+ const result = (service as any).logoutWithIdToken(
147
+ request,
148
+ 'https://kc/logout',
149
+ 'client-id',
150
+ );
151
+
152
+ expect(result).toBe(
153
+ 'https://kc/logout?client_id=client-id&post_logout_redirect_uri=https%3A%2F%2Fexample.com',
154
+ );
155
+ expect(result).not.toContain('id_token_hint');
156
+ });
157
+
158
+ it('logoutWithIdToken should omit an absent post logout redirect uri', () => {
159
+ const request = { query: {} } as unknown as Request;
160
+
161
+ const result = (service as any).logoutWithIdToken(
162
+ request,
163
+ 'https://kc/logout',
164
+ 'client-id',
165
+ );
166
+
167
+ expect(result).toBe('https://kc/logout?client_id=client-id');
168
+ });
113
169
  });
@@ -29,14 +29,20 @@ export class PMLogoutService implements LogoutCallback {
29
29
  const refreshToken = this.cookiesService.getAuthCookie(request);
30
30
 
31
31
  const body = new URLSearchParams({
32
- client_id: authConfig.clientId,
33
- client_secret: authConfig.clientSecret,
34
32
  refresh_token: refreshToken,
35
33
  });
36
34
 
35
+ // Client credentials go in the Authorization header, not the form body:
36
+ // the client is registered with token_endpoint_auth_method
37
+ // client_secret_basic, and an IdP that enforces it rejects credentials
38
+ // sent as form parameters with unauthorized_client.
37
39
  await firstValueFrom(
38
40
  this.httpService.post(authConfig.endSessionUrl, body, {
39
41
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
42
+ auth: {
43
+ username: authConfig.clientId,
44
+ password: authConfig.clientSecret,
45
+ },
40
46
  }),
41
47
  );
42
48
  } catch (error: any) {
@@ -45,16 +51,36 @@ export class PMLogoutService implements LogoutCallback {
45
51
  error?.response?.data || error.message,
46
52
  );
47
53
  this.logger.warn('Trying to log out with the id token');
48
- return this.logoutWithIdToken(request, authConfig.endSessionUrl);
54
+ return this.logoutWithIdToken(
55
+ request,
56
+ authConfig.endSessionUrl,
57
+ authConfig.clientId,
58
+ );
49
59
  }
50
60
  }
51
61
 
52
- private logoutWithIdToken(request: Request, endSessionUrl: string) {
62
+ private logoutWithIdToken(
63
+ request: Request,
64
+ endSessionUrl: string,
65
+ clientId: string,
66
+ ) {
53
67
  const { id_token_hint, post_logout_redirect_uri } = request.query;
54
- const params = new URLSearchParams({
55
- id_token_hint: String(id_token_hint || ''),
56
- post_logout_redirect_uri: String(post_logout_redirect_uri || ''),
57
- });
68
+ const params = new URLSearchParams();
69
+
70
+ // RP-initiated logout honours post_logout_redirect_uri only together with
71
+ // id_token_hint or client_id. An empty id_token_hint is not a no-op: the
72
+ // IdP rejects the request ("Missing parameters: id_token_hint") and the
73
+ // user is stranded on an error page instead of being signed out.
74
+ if (id_token_hint) {
75
+ params.set('id_token_hint', String(id_token_hint));
76
+ } else {
77
+ params.set('client_id', clientId);
78
+ }
79
+
80
+ if (post_logout_redirect_uri) {
81
+ params.set('post_logout_redirect_uri', String(post_logout_redirect_uri));
82
+ }
83
+
58
84
  return `${endSessionUrl}?${params}`;
59
85
  }
60
86
  }
@@ -1,4 +1,5 @@
1
1
  import { RequestContext } from '../pm-request-context-provider.js';
2
+ import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
2
3
  import { ContentConfigurationServiceProvidersService } from './content-configuration-service-providers.service.js';
3
4
  import { welcomeNodeConfig } from './models/welcome-node-config.js';
4
5
  import { GraphQLClient } from 'graphql-request';
@@ -18,9 +19,15 @@ describe('ContentConfigurationServiceProvidersService', () => {
18
19
  let service: ContentConfigurationServiceProvidersService;
19
20
  let mockClient: jest.Mocked<GraphQLClient>;
20
21
  let context: RequestContext;
22
+ let permissionsProxyService: jest.Mocked<PermissionsProxyService>;
21
23
 
22
24
  beforeEach(() => {
23
- service = new ContentConfigurationServiceProvidersService();
25
+ permissionsProxyService = {
26
+ resolvePermissions: jest.fn().mockResolvedValue([]),
27
+ } as unknown as jest.Mocked<PermissionsProxyService>;
28
+ service = new ContentConfigurationServiceProvidersService(
29
+ permissionsProxyService,
30
+ );
24
31
  mockClient = new GraphQLClient('') as any;
25
32
  (GraphQLClient as jest.Mock).mockReturnValue(mockClient);
26
33
  context = {
@@ -1,8 +1,3 @@
1
- import { RequestContext } from '../pm-request-context-provider.js';
2
- import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
3
- import { contentConfigurationsQuery } from './contentconfigurations-query.js';
4
- import { ContentConfigurationQueryResponse } from './models/contentconfigurations.js';
5
- import { welcomeNodeConfig } from './models/welcome-node-config.js';
6
1
  import { Injectable } from '@nestjs/common';
7
2
  import {
8
3
  ContentConfiguration,
@@ -10,9 +5,17 @@ import {
10
5
  ServiceProviderService,
11
6
  } from '@openmfp/portal-server-lib';
12
7
  import { GraphQLClient } from 'graphql-request';
8
+ import { RequestContext } from '../pm-request-context-provider.js';
9
+ import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
10
+ import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
11
+ import { contentConfigurationsQuery } from './contentconfigurations-query.js';
12
+ import { ContentConfigurationQueryResponse } from './models/contentconfigurations.js';
13
+ import { welcomeNodeConfig } from './models/welcome-node-config.js';
13
14
 
14
15
  @Injectable()
15
16
  export class ContentConfigurationServiceProvidersService implements ServiceProviderService {
17
+ constructor(private permissionsProxyService: PermissionsProxyService) {}
18
+
16
19
  async getServiceProviders(
17
20
  token: string,
18
21
  entities: string[],
@@ -31,7 +34,7 @@ export class ContentConfigurationServiceProvidersService implements ServiceProvi
31
34
  throw new Error('Context with organization is required');
32
35
  }
33
36
 
34
- let url = context.crdGatewayApiUrl.replace(
37
+ let url = context.crdGatewayApiUrl!.replace(
35
38
  'kubernetes-graphql-gateway/root',
36
39
  'kubernetes-graphql-gateway/virtual-workspace/contentconfigurations/root',
37
40
  );
@@ -112,6 +115,13 @@ export class ContentConfigurationServiceProvidersService implements ServiceProvi
112
115
  }
113
116
  });
114
117
 
118
+ const nodesPermissions = await this.permissionsProxyService.resolvePermissions(
119
+ token,
120
+ context.organization,
121
+ accountPath ?? '',
122
+ contentConfigurations,
123
+ );
124
+
115
125
  return {
116
126
  rawServiceProviders: [
117
127
  {
@@ -119,6 +129,7 @@ export class ContentConfigurationServiceProvidersService implements ServiceProvi
119
129
  displayName: '',
120
130
  creationTimestamp: '',
121
131
  contentConfiguration: contentConfigurations,
132
+ nodeContext: { nodesPermissions },
122
133
  },
123
134
  ],
124
135
  };
@@ -1,5 +1,6 @@
1
1
  import { K8sRequestContext, K8sResourceDescriptor } from '../models/k8s.js';
2
2
  import { KcpKubernetesService } from '../services/kcp-k8s.service.js';
3
+ import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
3
4
  import { KubernetesServiceProvidersService } from './kubernetes-service-providers.service.js';
4
5
  import { welcomeNodeConfig } from './models/welcome-node-config.js';
5
6
  import { Test, TestingModule } from '@nestjs/testing';
@@ -34,6 +35,7 @@ jest.mock('@kubernetes/client-node/dist/gen/middleware.js', () => ({
34
35
  describe('KubernetesServiceProvidersService', () => {
35
36
  let service: KubernetesServiceProvidersService;
36
37
  let kcpKubernetesServiceMock: jest.Mocked<KcpKubernetesService>;
38
+ let permissionsProxyServiceMock: jest.Mocked<PermissionsProxyService>;
37
39
 
38
40
  const mockToken = 'test-token-123';
39
41
  const mockEntities = ['test-entity'];
@@ -44,6 +46,9 @@ describe('KubernetesServiceProvidersService', () => {
44
46
 
45
47
  beforeEach(async () => {
46
48
  kcpKubernetesServiceMock = mock<KcpKubernetesService>();
49
+ permissionsProxyServiceMock = {
50
+ resolvePermissions: jest.fn().mockResolvedValue([]),
51
+ } as unknown as jest.Mocked<PermissionsProxyService>;
47
52
 
48
53
  const module: TestingModule = await Test.createTestingModule({
49
54
  providers: [
@@ -52,6 +57,10 @@ describe('KubernetesServiceProvidersService', () => {
52
57
  provide: KcpKubernetesService,
53
58
  useValue: kcpKubernetesServiceMock,
54
59
  },
60
+ {
61
+ provide: PermissionsProxyService,
62
+ useValue: permissionsProxyServiceMock,
63
+ },
55
64
  ],
56
65
  }).compile();
57
66
 
@@ -443,7 +452,7 @@ describe('KubernetesServiceProvidersService', () => {
443
452
  },
444
453
  );
445
454
 
446
- const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
455
+ const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock, permissionsProxyServiceMock);
447
456
  const res = await svc.getServiceProviders('token', ['main'], {
448
457
  organization: 'org',
449
458
  isSubDomain: true,
@@ -477,7 +486,7 @@ describe('KubernetesServiceProvidersService', () => {
477
486
  },
478
487
  );
479
488
 
480
- const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
489
+ const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock, permissionsProxyServiceMock);
481
490
  const res = await svc.getServiceProviders('token', ['main'], {
482
491
  organization: 'org',
483
492
  isSubDomain: true,
@@ -524,7 +533,7 @@ describe('KubernetesServiceProvidersService', () => {
524
533
  },
525
534
  );
526
535
 
527
- const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
536
+ const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock, permissionsProxyServiceMock);
528
537
  const res = await svc.getServiceProviders('token', ['main'], {
529
538
  organization: 'org',
530
539
  isSubDomain: true,
@@ -560,7 +569,7 @@ describe('KubernetesServiceProvidersService', () => {
560
569
  },
561
570
  );
562
571
 
563
- const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock);
572
+ const svc = new KubernetesServiceProvidersService(kcpKubernetesServiceMock, permissionsProxyServiceMock);
564
573
  const res = await svc.getServiceProviders('token', ['main'], {
565
574
  organization: 'org',
566
575
  isSubDomain: true,
@@ -1,17 +1,21 @@
1
- import { K8sRequestContext, K8sResourceDescriptor } from '../models/k8s.js';
2
- import { KcpKubernetesService } from '../services/kcp-k8s.service.js';
3
- import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
4
- import { welcomeNodeConfig } from './models/welcome-node-config.js';
5
1
  import { Injectable } from '@nestjs/common';
6
2
  import {
7
3
  ContentConfiguration,
8
4
  ServiceProviderResponse,
9
5
  ServiceProviderService,
10
6
  } from '@openmfp/portal-server-lib';
7
+ import { K8sRequestContext, K8sResourceDescriptor } from '../models/k8s.js';
8
+ import { KcpKubernetesService } from '../services/kcp-k8s.service.js';
9
+ import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
10
+ import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
11
+ import { welcomeNodeConfig } from './models/welcome-node-config.js';
11
12
 
12
13
  @Injectable()
13
14
  export class KubernetesServiceProvidersService implements ServiceProviderService {
14
- constructor(private kcpKubernetesService: KcpKubernetesService) {}
15
+ constructor(
16
+ private kcpKubernetesService: KcpKubernetesService,
17
+ private permissionsProxyService: PermissionsProxyService,
18
+ ) {}
15
19
 
16
20
  async getServiceProviders(
17
21
  token: string,
@@ -30,7 +34,7 @@ export class KubernetesServiceProvidersService implements ServiceProviderService
30
34
  if (!context?.organization) {
31
35
  throw new Error('Context with organization is required');
32
36
  }
33
-
37
+
34
38
  const response = await this.listContentConfigurationsForEntity(
35
39
  token,
36
40
  context as K8sRequestContext,
@@ -62,6 +66,15 @@ export class KubernetesServiceProvidersService implements ServiceProviderService
62
66
  return contentConfiguration;
63
67
  });
64
68
 
69
+ const accountPath =
70
+ context?.accountPath ?? context?.['core_platform-mesh_io_account'] ?? '';
71
+ const nodesPermissions = await this.permissionsProxyService.resolvePermissions(
72
+ token,
73
+ context.organization,
74
+ accountPath,
75
+ contentConfigurations,
76
+ );
77
+
65
78
  return {
66
79
  rawServiceProviders: [
67
80
  {
@@ -69,6 +82,7 @@ export class KubernetesServiceProvidersService implements ServiceProviderService
69
82
  displayName: '',
70
83
  creationTimestamp: '',
71
84
  contentConfiguration: contentConfigurations,
85
+ nodeContext: { nodesPermissions },
72
86
  },
73
87
  ],
74
88
  };
@@ -90,12 +104,11 @@ export class KubernetesServiceProvidersService implements ServiceProviderService
90
104
  context,
91
105
  token,
92
106
  );
93
- } catch (error) {
107
+ } catch (error: any) {
94
108
  console.error(error);
95
109
 
96
110
  if (error.code == 429 || error.statusCode == 429) {
97
111
  await new Promise((resolve) => setTimeout(resolve, 1000));
98
- console.log('Retry after 1 second reading kubernetes resources.');
99
112
  return await this.kcpKubernetesService.listClusterCustomObjectInKcpVirtualWorkspace(
100
113
  gvr,
101
114
  context,