@platform-mesh/portal-server-lib 0.9.96 → 0.10.0

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 (52) 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/service-providers/content-configuration-service-providers.service.d.ts +4 -1
  5. package/dist/portal-options/service-providers/content-configuration-service-providers.service.js +14 -3
  6. package/dist/portal-options/service-providers/content-configuration-service-providers.service.js.map +1 -1
  7. package/dist/portal-options/service-providers/kubernetes-service-providers.service.d.ts +4 -2
  8. package/dist/portal-options/service-providers/kubernetes-service-providers.service.js +10 -4
  9. package/dist/portal-options/service-providers/kubernetes-service-providers.service.js.map +1 -1
  10. package/dist/portal-options/services/kcp-k8s.service.d.ts +0 -1
  11. package/dist/portal-options/services/kcp-k8s.service.js +4 -13
  12. package/dist/portal-options/services/kcp-k8s.service.js.map +1 -1
  13. package/dist/portal-options/services/permissions/adapters/authz-webhook.service.d.ts +14 -0
  14. package/dist/portal-options/services/permissions/adapters/authz-webhook.service.js +183 -0
  15. package/dist/portal-options/services/permissions/adapters/authz-webhook.service.js.map +1 -0
  16. package/dist/portal-options/services/permissions/models/permissions.model.d.ts +51 -0
  17. package/dist/portal-options/services/permissions/models/permissions.model.js +2 -0
  18. package/dist/portal-options/services/permissions/models/permissions.model.js.map +1 -0
  19. package/dist/portal-options/services/permissions/permissions-proxy.service.d.ts +9 -0
  20. package/dist/portal-options/services/permissions/permissions-proxy.service.js +48 -0
  21. package/dist/portal-options/services/permissions/permissions-proxy.service.js.map +1 -0
  22. package/dist/portal-options/services/permissions/permissions.controller.d.ts +9 -0
  23. package/dist/portal-options/services/permissions/permissions.controller.js +49 -0
  24. package/dist/portal-options/services/permissions/permissions.controller.js.map +1 -0
  25. package/dist/portal-options/utils/account-hierarchy-resolver.js +7 -3
  26. package/dist/portal-options/utils/account-hierarchy-resolver.js.map +1 -1
  27. package/dist/portal-options/utils/build-workspace-path.util.d.ts +1 -0
  28. package/dist/portal-options/utils/build-workspace-path.util.js +4 -0
  29. package/dist/portal-options/utils/build-workspace-path.util.js.map +1 -0
  30. package/dist/portal-options/utils/extract-resource-definitions.util.d.ts +7 -0
  31. package/dist/portal-options/utils/extract-resource-definitions.util.js +33 -0
  32. package/dist/portal-options/utils/extract-resource-definitions.util.js.map +1 -0
  33. package/package.json +4 -4
  34. package/src/portal-options/index.ts +4 -0
  35. package/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts +8 -1
  36. package/src/portal-options/service-providers/content-configuration-service-providers.service.ts +17 -6
  37. package/src/portal-options/service-providers/kubernetes-service-providers.service.spec.ts +13 -4
  38. package/src/portal-options/service-providers/kubernetes-service-providers.service.ts +21 -8
  39. package/src/portal-options/services/kcp-k8s.service.ts +4 -15
  40. package/src/portal-options/services/permissions/adapters/authz-webhook.service.spec.ts +500 -0
  41. package/src/portal-options/services/permissions/adapters/authz-webhook.service.ts +238 -0
  42. package/src/portal-options/services/permissions/models/permissions.model.ts +56 -0
  43. package/src/portal-options/services/permissions/permissions-proxy.service.spec.ts +232 -0
  44. package/src/portal-options/services/permissions/permissions-proxy.service.ts +58 -0
  45. package/src/portal-options/services/permissions/permissions.controller.spec.ts +143 -0
  46. package/src/portal-options/services/permissions/permissions.controller.ts +31 -0
  47. package/src/portal-options/utils/account-hierarchy-resolver.ts +13 -8
  48. package/src/portal-options/utils/build-workspace-path.util.spec.ts +59 -0
  49. package/src/portal-options/utils/build-workspace-path.util.ts +21 -0
  50. package/src/portal-options/utils/extract-resource-definitions.util.spec.ts +303 -0
  51. package/src/portal-options/utils/extract-resource-definitions.util.ts +62 -0
  52. package/tsconfig.json +2 -1
@@ -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.96",
3
+ "version": "0.10.0",
4
4
  "author": "Platform Mesh",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -37,6 +37,7 @@
37
37
  "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"
38
38
  },
39
39
  "peerDependencies": {
40
+ "@kubernetes/client-node": "^2.0.0",
40
41
  "@nestjs/axios": "^4.0.1",
41
42
  "@nestjs/common": "^11.1.17",
42
43
  "@nestjs/core": "^11.1.9",
@@ -46,13 +47,12 @@
46
47
  "axios": "^1.15.0",
47
48
  "cookie-parser": "^1.4.7",
48
49
  "express": "^5.2.1",
49
- "rxjs": "^7.8.2",
50
50
  "graphql-request": "^7.4.0",
51
- "@kubernetes/client-node": "^1.4.0"
51
+ "rxjs": "^7.8.2"
52
52
  },
53
- "dependencies": {},
54
53
  "devDependencies": {
55
54
  "@eslint/js": "10.0.1",
55
+ "@kubernetes/client-node": "^2.0.0",
56
56
  "@nestjs/cli": "11.0.19",
57
57
  "@nestjs/testing": "11.1.18",
58
58
  "@openmfp/config-prettier": "0.9.2",
@@ -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';
@@ -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,
@@ -1,4 +1,5 @@
1
1
  import { K8sRequestContext, K8sResourceDescriptor } from '../models/k8s.js';
2
+ import { buildWorkspacePath } from '../utils/build-workspace-path.util.js';
2
3
  import { getOrganization } from '../utils/domain.js';
3
4
  import {
4
5
  CoreV1Api,
@@ -65,20 +66,8 @@ export class KcpKubernetesService {
65
66
  return this.k8sCoreV1Api;
66
67
  }
67
68
 
68
- private buildWorkspacePath(organization?: string, account?: string) {
69
- let path = `root:orgs`;
70
- if (organization) {
71
- path += `:${organization}`;
72
- }
73
- if (account) {
74
- path += `:${account}`; // FIXME: how are nested accounts and paths handled in the portal?
75
- }
76
-
77
- return path;
78
- }
79
-
80
69
  getKcpVirtualWorkspaceUrl(organization: string, account: string) {
81
- const path = this.buildWorkspacePath(organization, account);
70
+ const path = buildWorkspacePath([organization, account]);
82
71
  return new URL(
83
72
  `${this.baseUrl.origin}/services/contentconfigurations/clusters/${path}`,
84
73
  );
@@ -90,14 +79,14 @@ export class KcpKubernetesService {
90
79
  workspacePath?: string,
91
80
  ) {
92
81
  const path =
93
- workspacePath || this.buildWorkspacePath(organization, account);
82
+ workspacePath || buildWorkspacePath([organization, account]);
94
83
  return new URL(`${this.baseUrl.origin}/clusters/${path}`);
95
84
  }
96
85
 
97
86
  getKcpWorkspacePublicUrl(request: Request) {
98
87
  const organization = getOrganization(request);
99
88
  const account = request.query?.['core_platform-mesh_io_account'];
100
- const path = this.buildWorkspacePath(organization, account);
89
+ const path = buildWorkspacePath([organization, account]);
101
90
 
102
91
  const kcpUrl = process.env.KCP_URL || '';
103
92
  if (kcpUrl) {