@backstage/plugin-kubernetes-backend 0.21.6 → 0.22.0-next.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @backstage/plugin-kubernetes-backend
2
2
 
3
+ ## 0.22.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - c6af8ac: Added audit logging for kubernetes-backend routes. The plugin now emits auditor events for cluster list, cluster proxy, entity workload queries, custom resource queries, and the deprecated services endpoint. Administrators can filter audit logs by `eventId` values `cluster-fetch` and `resource-fetch`, and by `queryType` in event metadata.
8
+
9
+ **BREAKING**: `KubernetesProxyOptions` now requires `AuditorService`.
10
+
11
+ ## 0.21.7-next.0
12
+
13
+ ### Patch Changes
14
+
15
+ - 684c9b9: Fixed `AwsIamStrategy` to resolve account-specific AWS credentials when an assume role ARN is configured, enabling support for `webIdentityTokenFile` and `accountDefaults` in environments without default AWS credentials.
16
+ - f0834bd: Added a `kubernetes.clusterLocatorContinueOnError` configuration option. When set to `true`, a failing cluster locator no longer causes the entire cluster list request to fail — errors are logged and clusters from the remaining successful locators are still returned. The default is `false`, preserving the existing behavior.
17
+ - Updated dependencies
18
+ - @backstage/backend-plugin-api@1.10.0-next.0
19
+ - @backstage/plugin-kubernetes-node@0.4.7-next.0
20
+ - @backstage/plugin-permission-node@0.11.3-next.0
21
+ - @backstage/plugin-catalog-node@2.2.4-next.0
22
+
3
23
  ## 0.21.6
4
24
 
5
25
  ### Patch Changes
@@ -43,6 +43,10 @@
43
43
  "type"
44
44
  ]
45
45
  },
46
+ "clusterLocatorContinueOnError": {
47
+ "type": "boolean",
48
+ "description": "Whether to continue returning clusters from successful locators when one or more locators fail. When set to `true`, errors are logged and only the clusters from successful locators are returned. Defaults to `false`, which preserves the existing behaviour of failing the entire request when any locator errors."
49
+ },
46
50
  "clusterLocatorMethods": {
47
51
  "type": "array",
48
52
  "items": {
@@ -27,19 +27,29 @@ class AwsIamStrategy {
27
27
  }
28
28
  async getBearerToken(clusterId, assumeRole, externalId) {
29
29
  const region = process.env.AWS_REGION ?? defaultRegion;
30
- let credentials = (await this.credsManager.getCredentialProvider()).sdkCredentialProvider;
30
+ let masterCredentials;
31
31
  if (assumeRole) {
32
- credentials = credentialProviders.fromTemporaryCredentials({
33
- masterCredentials: credentials,
34
- clientConfig: {
35
- region
36
- },
37
- params: {
38
- RoleArn: assumeRole,
39
- ExternalId: externalId
32
+ try {
33
+ masterCredentials = (await this.credsManager.getCredentialProvider({ arn: assumeRole })).sdkCredentialProvider;
34
+ } catch (error) {
35
+ if (!(error instanceof Error) || !error.message.includes("There is no AWS integration that matches")) {
36
+ throw error;
40
37
  }
41
- });
38
+ masterCredentials = (await this.credsManager.getCredentialProvider()).sdkCredentialProvider;
39
+ }
40
+ } else {
41
+ masterCredentials = (await this.credsManager.getCredentialProvider()).sdkCredentialProvider;
42
42
  }
43
+ const credentials = assumeRole ? credentialProviders.fromTemporaryCredentials({
44
+ masterCredentials,
45
+ clientConfig: {
46
+ region
47
+ },
48
+ params: {
49
+ RoleArn: assumeRole,
50
+ ExternalId: externalId
51
+ }
52
+ }) : masterCredentials;
43
53
  const signer = new signatureV4.SignatureV4({
44
54
  credentials,
45
55
  region,
@@ -1 +1 @@
1
- {"version":3,"file":"AwsIamStrategy.cjs.js","sources":["../../src/auth/AwsIamStrategy.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { fromTemporaryCredentials } from '@aws-sdk/credential-providers';\nimport { SignatureV4 } from '@smithy/signature-v4';\nimport { Sha256 } from '@aws-crypto/sha256-js';\nimport {\n AwsCredentialsManager,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport { Config } from '@backstage/config';\nimport {\n ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE,\n ANNOTATION_KUBERNETES_AWS_CLUSTER_ID,\n ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID,\n} from '@backstage/plugin-kubernetes-common';\nimport {\n AuthMetadata,\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\n\n/**\n *\n * @public\n */\nexport type SigningCreds = {\n accessKeyId: string | undefined;\n secretAccessKey: string | undefined;\n sessionToken: string | undefined;\n};\n\nconst defaultRegion = 'us-east-1';\n\n/**\n *\n * @public\n */\nexport class AwsIamStrategy implements AuthenticationStrategy {\n private readonly credsManager: AwsCredentialsManager;\n\n constructor(opts: { config: Config }) {\n this.credsManager = DefaultAwsCredentialsManager.fromConfig(opts.config);\n }\n\n public async getCredential(\n clusterDetails: ClusterDetails,\n ): Promise<KubernetesCredential> {\n return {\n type: 'bearer token',\n token: await this.getBearerToken(\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AWS_CLUSTER_ID] ??\n clusterDetails.name,\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE],\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID],\n ),\n };\n }\n\n public validateCluster(): Error[] {\n return [];\n }\n\n private async getBearerToken(\n clusterId: string,\n assumeRole?: string,\n externalId?: string,\n ): Promise<string> {\n const region = process.env.AWS_REGION ?? defaultRegion;\n\n let credentials = (await this.credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n if (assumeRole) {\n credentials = fromTemporaryCredentials({\n masterCredentials: credentials,\n clientConfig: {\n region,\n },\n params: {\n RoleArn: assumeRole,\n ExternalId: externalId,\n },\n });\n }\n\n const signer = new SignatureV4({\n credentials,\n region,\n service: 'sts',\n sha256: Sha256,\n });\n\n const request = await signer.presign(\n {\n headers: {\n host: `sts.${region}.amazonaws.com`,\n 'x-k8s-aws-id': clusterId,\n },\n hostname: `sts.${region}.amazonaws.com`,\n method: 'GET',\n path: '/',\n protocol: 'https:',\n query: {\n Action: 'GetCallerIdentity',\n Version: '2011-06-15',\n },\n },\n { expiresIn: 0 },\n );\n\n const query = Object.keys(request?.query ?? {})\n .map(\n q =>\n `${encodeURIComponent(q)}=${encodeURIComponent(\n request.query?.[q] as string,\n )}`,\n )\n .join('&');\n\n const url = `https://${request.hostname}${request.path}?${query}`;\n\n return `k8s-aws-v1.${Buffer.from(url).toString('base64url')}`;\n }\n\n public presentAuthMetadata(_authMetadata: AuthMetadata): AuthMetadata {\n return {};\n }\n}\n"],"names":["DefaultAwsCredentialsManager","ANNOTATION_KUBERNETES_AWS_CLUSTER_ID","ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE","ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID","fromTemporaryCredentials","SignatureV4","Sha256"],"mappings":";;;;;;;;AA6CA,MAAM,aAAA,GAAgB,WAAA;AAMf,MAAM,cAAA,CAAiD;AAAA,EAC3C,YAAA;AAAA,EAEjB,YAAY,IAAA,EAA0B;AACpC,IAAA,IAAA,CAAK,YAAA,GAAeA,+CAAA,CAA6B,UAAA,CAAW,IAAA,CAAK,MAAM,CAAA;AAAA,EACzE;AAAA,EAEA,MAAa,cACX,cAAA,EAC+B;AAC/B,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,cAAA;AAAA,MACN,KAAA,EAAO,MAAM,IAAA,CAAK,cAAA;AAAA,QAChB,cAAA,CAAe,YAAA,CAAaC,2DAAoC,CAAA,IAC9D,cAAA,CAAe,IAAA;AAAA,QACjB,cAAA,CAAe,aAAaC,4DAAqC,CAAA;AAAA,QACjE,cAAA,CAAe,aAAaC,4DAAqC;AAAA;AACnE,KACF;AAAA,EACF;AAAA,EAEO,eAAA,GAA2B;AAChC,IAAA,OAAO,EAAC;AAAA,EACV;AAAA,EAEA,MAAc,cAAA,CACZ,SAAA,EACA,UAAA,EACA,UAAA,EACiB;AACjB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,UAAA,IAAc,aAAA;AAEzC,IAAA,IAAI,WAAA,GAAA,CAAe,MAAM,IAAA,CAAK,YAAA,CAAa,uBAAsB,EAC9D,qBAAA;AACH,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,WAAA,GAAcC,4CAAA,CAAyB;AAAA,QACrC,iBAAA,EAAmB,WAAA;AAAA,QACnB,YAAA,EAAc;AAAA,UACZ;AAAA,SACF;AAAA,QACA,MAAA,EAAQ;AAAA,UACN,OAAA,EAAS,UAAA;AAAA,UACT,UAAA,EAAY;AAAA;AACd,OACD,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,MAAA,GAAS,IAAIC,uBAAA,CAAY;AAAA,MAC7B,WAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA,EAAS,KAAA;AAAA,MACT,MAAA,EAAQC;AAAA,KACT,CAAA;AAED,IAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,OAAA;AAAA,MAC3B;AAAA,QACE,OAAA,EAAS;AAAA,UACP,IAAA,EAAM,OAAO,MAAM,CAAA,cAAA,CAAA;AAAA,UACnB,cAAA,EAAgB;AAAA,SAClB;AAAA,QACA,QAAA,EAAU,OAAO,MAAM,CAAA,cAAA,CAAA;AAAA,QACvB,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,GAAA;AAAA,QACN,QAAA,EAAU,QAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,MAAA,EAAQ,mBAAA;AAAA,UACR,OAAA,EAAS;AAAA;AACX,OACF;AAAA,MACA,EAAE,WAAW,CAAA;AAAE,KACjB;AAEA,IAAA,MAAM,QAAQ,MAAA,CAAO,IAAA,CAAK,SAAS,KAAA,IAAS,EAAE,CAAA,CAC3C,GAAA;AAAA,MACC,CAAA,CAAA,KACE,CAAA,EAAG,kBAAA,CAAmB,CAAC,CAAC,CAAA,CAAA,EAAI,kBAAA;AAAA,QAC1B,OAAA,CAAQ,QAAQ,CAAC;AAAA,OAClB,CAAA;AAAA,KACL,CACC,KAAK,GAAG,CAAA;AAEX,IAAA,MAAM,GAAA,GAAM,WAAW,OAAA,CAAQ,QAAQ,GAAG,OAAA,CAAQ,IAAI,IAAI,KAAK,CAAA,CAAA;AAE/D,IAAA,OAAO,cAAc,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA,CAAE,QAAA,CAAS,WAAW,CAAC,CAAA,CAAA;AAAA,EAC7D;AAAA,EAEO,oBAAoB,aAAA,EAA2C;AACpE,IAAA,OAAO,EAAC;AAAA,EACV;AACF;;;;"}
1
+ {"version":3,"file":"AwsIamStrategy.cjs.js","sources":["../../src/auth/AwsIamStrategy.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { fromTemporaryCredentials } from '@aws-sdk/credential-providers';\nimport { SignatureV4 } from '@smithy/signature-v4';\nimport { Sha256 } from '@aws-crypto/sha256-js';\nimport {\n AwsCredentialsManager,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport { Config } from '@backstage/config';\nimport {\n ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE,\n ANNOTATION_KUBERNETES_AWS_CLUSTER_ID,\n ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID,\n} from '@backstage/plugin-kubernetes-common';\nimport {\n AuthMetadata,\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\n\n/**\n *\n * @public\n */\nexport type SigningCreds = {\n accessKeyId: string | undefined;\n secretAccessKey: string | undefined;\n sessionToken: string | undefined;\n};\n\nconst defaultRegion = 'us-east-1';\n\n/**\n *\n * @public\n */\nexport class AwsIamStrategy implements AuthenticationStrategy {\n private readonly credsManager: AwsCredentialsManager;\n\n constructor(opts: { config: Config }) {\n this.credsManager = DefaultAwsCredentialsManager.fromConfig(opts.config);\n }\n\n public async getCredential(\n clusterDetails: ClusterDetails,\n ): Promise<KubernetesCredential> {\n return {\n type: 'bearer token',\n token: await this.getBearerToken(\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AWS_CLUSTER_ID] ??\n clusterDetails.name,\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE],\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID],\n ),\n };\n }\n\n public validateCluster(): Error[] {\n return [];\n }\n\n private async getBearerToken(\n clusterId: string,\n assumeRole?: string,\n externalId?: string,\n ): Promise<string> {\n const region = process.env.AWS_REGION ?? defaultRegion;\n\n let masterCredentials;\n if (assumeRole) {\n try {\n masterCredentials = (\n await this.credsManager.getCredentialProvider({ arn: assumeRole })\n ).sdkCredentialProvider;\n } catch (error) {\n if (\n !(error instanceof Error) ||\n !error.message.includes('There is no AWS integration that matches')\n ) {\n throw error;\n }\n masterCredentials = (await this.credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n }\n } else {\n masterCredentials = (await this.credsManager.getCredentialProvider())\n .sdkCredentialProvider;\n }\n\n const credentials = assumeRole\n ? fromTemporaryCredentials({\n masterCredentials,\n clientConfig: {\n region,\n },\n params: {\n RoleArn: assumeRole,\n ExternalId: externalId,\n },\n })\n : masterCredentials;\n\n const signer = new SignatureV4({\n credentials,\n region,\n service: 'sts',\n sha256: Sha256,\n });\n\n const request = await signer.presign(\n {\n headers: {\n host: `sts.${region}.amazonaws.com`,\n 'x-k8s-aws-id': clusterId,\n },\n hostname: `sts.${region}.amazonaws.com`,\n method: 'GET',\n path: '/',\n protocol: 'https:',\n query: {\n Action: 'GetCallerIdentity',\n Version: '2011-06-15',\n },\n },\n { expiresIn: 0 },\n );\n\n const query = Object.keys(request?.query ?? {})\n .map(\n q =>\n `${encodeURIComponent(q)}=${encodeURIComponent(\n request.query?.[q] as string,\n )}`,\n )\n .join('&');\n\n const url = `https://${request.hostname}${request.path}?${query}`;\n\n return `k8s-aws-v1.${Buffer.from(url).toString('base64url')}`;\n }\n\n public presentAuthMetadata(_authMetadata: AuthMetadata): AuthMetadata {\n return {};\n }\n}\n"],"names":["DefaultAwsCredentialsManager","ANNOTATION_KUBERNETES_AWS_CLUSTER_ID","ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE","ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID","fromTemporaryCredentials","SignatureV4","Sha256"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var errors = require('@backstage/errors');
3
4
  var ConfigClusterLocator = require('./ConfigClusterLocator.cjs.js');
4
5
  var GkeClusterLocator = require('./GkeClusterLocator.cjs.js');
5
6
  var CatalogClusterLocator = require('./CatalogClusterLocator.cjs.js');
@@ -8,18 +9,36 @@ var LocalKubectlProxyLocator = require('./LocalKubectlProxyLocator.cjs.js');
8
9
  class CombinedClustersSupplier {
9
10
  clusterSuppliers;
10
11
  logger;
11
- constructor(clusterSuppliers, logger) {
12
+ continueOnError;
13
+ constructor(clusterSuppliers, logger, continueOnError = false) {
12
14
  this.clusterSuppliers = clusterSuppliers;
13
15
  this.logger = logger;
16
+ this.continueOnError = continueOnError;
14
17
  }
15
18
  async getClusters(options) {
16
- const clusters = await Promise.all(
19
+ const clusters = this.continueOnError ? await this.getClustersSettled(options) : await Promise.all(
17
20
  this.clusterSuppliers.map((supplier) => supplier.getClusters(options))
18
- ).then((res) => {
19
- return res.flat();
20
- });
21
+ ).then((res) => res.flat());
21
22
  return this.warnDuplicates(clusters);
22
23
  }
24
+ async getClustersSettled(options) {
25
+ const results = await Promise.allSettled(
26
+ this.clusterSuppliers.map((supplier) => supplier.getClusters(options))
27
+ );
28
+ const clusters = [];
29
+ for (let i = 0; i < results.length; i++) {
30
+ const result = results[i];
31
+ if (result.status === "fulfilled") {
32
+ clusters.push(...result.value);
33
+ } else {
34
+ this.logger.error(
35
+ `Failed to retrieve clusters from cluster locator method #${i + 1}`,
36
+ errors.toError(result.reason)
37
+ );
38
+ }
39
+ }
40
+ return clusters;
41
+ }
23
42
  warnDuplicates(clusters) {
24
43
  const clusterNames = /* @__PURE__ */ new Set();
25
44
  const duplicatedNames = /* @__PURE__ */ new Set();
@@ -61,7 +80,12 @@ const getCombinedClusterSupplier = (rootConfig, catalogService, authStrategy, lo
61
80
  );
62
81
  }
63
82
  });
64
- return new CombinedClustersSupplier(clusterSuppliers, logger);
83
+ const continueOnError = rootConfig.getOptionalBoolean("kubernetes.clusterLocatorContinueOnError") ?? false;
84
+ return new CombinedClustersSupplier(
85
+ clusterSuppliers,
86
+ logger,
87
+ continueOnError
88
+ );
65
89
  };
66
90
 
67
91
  exports.getCombinedClusterSupplier = getCombinedClusterSupplier;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../src/cluster-locator/index.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { Duration } from 'luxon';\nimport { ConfigClusterLocator } from './ConfigClusterLocator';\nimport { GkeClusterLocator } from './GkeClusterLocator';\nimport { CatalogClusterLocator } from './CatalogClusterLocator';\nimport { LocalKubectlProxyClusterLocator } from './LocalKubectlProxyLocator';\nimport {\n AuthService,\n BackstageCredentials,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nclass CombinedClustersSupplier implements KubernetesClustersSupplier {\n readonly clusterSuppliers: KubernetesClustersSupplier[];\n readonly logger: LoggerService;\n\n constructor(\n clusterSuppliers: KubernetesClustersSupplier[],\n logger: LoggerService,\n ) {\n this.clusterSuppliers = clusterSuppliers;\n this.logger = logger;\n }\n\n async getClusters(options: {\n credentials: BackstageCredentials;\n }): Promise<ClusterDetails[]> {\n const clusters = await Promise.all(\n this.clusterSuppliers.map(supplier => supplier.getClusters(options)),\n ).then(res => {\n return res.flat();\n });\n return this.warnDuplicates(clusters);\n }\n\n private warnDuplicates(clusters: ClusterDetails[]): ClusterDetails[] {\n const clusterNames = new Set<string>();\n const duplicatedNames = new Set<string>();\n for (const clusterName of clusters.map(c => c.name)) {\n if (clusterNames.has(clusterName)) {\n duplicatedNames.add(clusterName);\n } else {\n clusterNames.add(clusterName);\n }\n }\n for (const clusterName of duplicatedNames) {\n this.logger.warn(`Duplicate cluster name '${clusterName}'`);\n }\n return clusters;\n }\n}\n\nexport const getCombinedClusterSupplier = (\n rootConfig: Config,\n catalogService: CatalogService,\n authStrategy: AuthenticationStrategy,\n logger: LoggerService,\n refreshInterval: Duration | undefined = undefined,\n auth: AuthService,\n): KubernetesClustersSupplier => {\n const clusterSuppliers = rootConfig\n .getConfigArray('kubernetes.clusterLocatorMethods')\n .map(clusterLocatorMethod => {\n const type = clusterLocatorMethod.getString('type');\n switch (type) {\n case 'catalog':\n return CatalogClusterLocator.fromConfig(catalogService, auth);\n case 'localKubectlProxy':\n return new LocalKubectlProxyClusterLocator();\n case 'config':\n return ConfigClusterLocator.fromConfig(\n clusterLocatorMethod,\n authStrategy,\n );\n case 'gke':\n return GkeClusterLocator.fromConfig(\n clusterLocatorMethod,\n logger,\n refreshInterval,\n );\n default:\n throw new Error(\n `Unsupported kubernetes.clusterLocatorMethods: \"${type}\"`,\n );\n }\n });\n\n return new CombinedClustersSupplier(clusterSuppliers, logger);\n};\n"],"names":["CatalogClusterLocator","LocalKubectlProxyClusterLocator","ConfigClusterLocator","GkeClusterLocator"],"mappings":";;;;;;;AAkCA,MAAM,wBAAA,CAA+D;AAAA,EAC1D,gBAAA;AAAA,EACA,MAAA;AAAA,EAET,WAAA,CACE,kBACA,MAAA,EACA;AACA,IAAA,IAAA,CAAK,gBAAA,GAAmB,gBAAA;AACxB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,MAAM,YAAY,OAAA,EAEY;AAC5B,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC7B,KAAK,gBAAA,CAAiB,GAAA,CAAI,cAAY,QAAA,CAAS,WAAA,CAAY,OAAO,CAAC;AAAA,KACrE,CAAE,KAAK,CAAA,GAAA,KAAO;AACZ,MAAA,OAAO,IAAI,IAAA,EAAK;AAAA,IAClB,CAAC,CAAA;AACD,IAAA,OAAO,IAAA,CAAK,eAAe,QAAQ,CAAA;AAAA,EACrC;AAAA,EAEQ,eAAe,QAAA,EAA8C;AACnE,IAAA,MAAM,YAAA,uBAAmB,GAAA,EAAY;AACrC,IAAA,MAAM,eAAA,uBAAsB,GAAA,EAAY;AACxC,IAAA,KAAA,MAAW,eAAe,QAAA,CAAS,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,IAAI,CAAA,EAAG;AACnD,MAAA,IAAI,YAAA,CAAa,GAAA,CAAI,WAAW,CAAA,EAAG;AACjC,QAAA,eAAA,CAAgB,IAAI,WAAW,CAAA;AAAA,MACjC,CAAA,MAAO;AACL,QAAA,YAAA,CAAa,IAAI,WAAW,CAAA;AAAA,MAC9B;AAAA,IACF;AACA,IAAA,KAAA,MAAW,eAAe,eAAA,EAAiB;AACzC,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,wBAAA,EAA2B,WAAW,CAAA,CAAA,CAAG,CAAA;AAAA,IAC5D;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AACF;AAEO,MAAM,0BAAA,GAA6B,CACxC,UAAA,EACA,cAAA,EACA,cACA,MAAA,EACA,eAAA,GAAwC,QACxC,IAAA,KAC+B;AAC/B,EAAA,MAAM,mBAAmB,UAAA,CACtB,cAAA,CAAe,kCAAkC,CAAA,CACjD,IAAI,CAAA,oBAAA,KAAwB;AAC3B,IAAA,MAAM,IAAA,GAAO,oBAAA,CAAqB,SAAA,CAAU,MAAM,CAAA;AAClD,IAAA,QAAQ,IAAA;AAAM,MACZ,KAAK,SAAA;AACH,QAAA,OAAOA,2CAAA,CAAsB,UAAA,CAAW,cAAA,EAAgB,IAAI,CAAA;AAAA,MAC9D,KAAK,mBAAA;AACH,QAAA,OAAO,IAAIC,wDAAA,EAAgC;AAAA,MAC7C,KAAK,QAAA;AACH,QAAA,OAAOC,yCAAA,CAAqB,UAAA;AAAA,UAC1B,oBAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF,KAAK,KAAA;AACH,QAAA,OAAOC,mCAAA,CAAkB,UAAA;AAAA,UACvB,oBAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AACE,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,kDAAkD,IAAI,CAAA,CAAA;AAAA,SACxD;AAAA;AACJ,EACF,CAAC,CAAA;AAEH,EAAA,OAAO,IAAI,wBAAA,CAAyB,gBAAA,EAAkB,MAAM,CAAA;AAC9D;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../src/cluster-locator/index.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { toError } from '@backstage/errors';\nimport { Duration } from 'luxon';\nimport { ConfigClusterLocator } from './ConfigClusterLocator';\nimport { GkeClusterLocator } from './GkeClusterLocator';\nimport { CatalogClusterLocator } from './CatalogClusterLocator';\nimport { LocalKubectlProxyClusterLocator } from './LocalKubectlProxyLocator';\nimport {\n AuthService,\n BackstageCredentials,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nclass CombinedClustersSupplier implements KubernetesClustersSupplier {\n readonly clusterSuppliers: KubernetesClustersSupplier[];\n readonly logger: LoggerService;\n readonly continueOnError: boolean;\n\n constructor(\n clusterSuppliers: KubernetesClustersSupplier[],\n logger: LoggerService,\n continueOnError: boolean = false,\n ) {\n this.clusterSuppliers = clusterSuppliers;\n this.logger = logger;\n this.continueOnError = continueOnError;\n }\n\n async getClusters(options: {\n credentials: BackstageCredentials;\n }): Promise<ClusterDetails[]> {\n const clusters = this.continueOnError\n ? await this.getClustersSettled(options)\n : await Promise.all(\n this.clusterSuppliers.map(supplier => supplier.getClusters(options)),\n ).then(res => res.flat());\n return this.warnDuplicates(clusters);\n }\n\n private async getClustersSettled(options: {\n credentials: BackstageCredentials;\n }): Promise<ClusterDetails[]> {\n const results = await Promise.allSettled(\n this.clusterSuppliers.map(supplier => supplier.getClusters(options)),\n );\n const clusters: ClusterDetails[] = [];\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result.status === 'fulfilled') {\n clusters.push(...result.value);\n } else {\n this.logger.error(\n `Failed to retrieve clusters from cluster locator method #${i + 1}`,\n toError(result.reason),\n );\n }\n }\n return clusters;\n }\n\n private warnDuplicates(clusters: ClusterDetails[]): ClusterDetails[] {\n const clusterNames = new Set<string>();\n const duplicatedNames = new Set<string>();\n for (const clusterName of clusters.map(c => c.name)) {\n if (clusterNames.has(clusterName)) {\n duplicatedNames.add(clusterName);\n } else {\n clusterNames.add(clusterName);\n }\n }\n for (const clusterName of duplicatedNames) {\n this.logger.warn(`Duplicate cluster name '${clusterName}'`);\n }\n return clusters;\n }\n}\n\nexport const getCombinedClusterSupplier = (\n rootConfig: Config,\n catalogService: CatalogService,\n authStrategy: AuthenticationStrategy,\n logger: LoggerService,\n refreshInterval: Duration | undefined = undefined,\n auth: AuthService,\n): KubernetesClustersSupplier => {\n const clusterSuppliers = rootConfig\n .getConfigArray('kubernetes.clusterLocatorMethods')\n .map(clusterLocatorMethod => {\n const type = clusterLocatorMethod.getString('type');\n switch (type) {\n case 'catalog':\n return CatalogClusterLocator.fromConfig(catalogService, auth);\n case 'localKubectlProxy':\n return new LocalKubectlProxyClusterLocator();\n case 'config':\n return ConfigClusterLocator.fromConfig(\n clusterLocatorMethod,\n authStrategy,\n );\n case 'gke':\n return GkeClusterLocator.fromConfig(\n clusterLocatorMethod,\n logger,\n refreshInterval,\n );\n default:\n throw new Error(\n `Unsupported kubernetes.clusterLocatorMethods: \"${type}\"`,\n );\n }\n });\n\n const continueOnError =\n rootConfig.getOptionalBoolean('kubernetes.clusterLocatorContinueOnError') ??\n false;\n\n return new CombinedClustersSupplier(\n clusterSuppliers,\n logger,\n continueOnError,\n );\n};\n"],"names":["toError","CatalogClusterLocator","LocalKubectlProxyClusterLocator","ConfigClusterLocator","GkeClusterLocator"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
- import { LoggerService, DiscoveryService, HttpAuthService, PermissionsService } from '@backstage/backend-plugin-api';
2
+ import { LoggerService, DiscoveryService, HttpAuthService, AuditorService, PermissionsService } from '@backstage/backend-plugin-api';
3
3
  import * as k8sTypes from '@backstage/plugin-kubernetes-node';
4
4
  import { AuthenticationStrategy, ClusterDetails, KubernetesCredential, AuthMetadata, ObjectToFetch, KubernetesClustersSupplier } from '@backstage/plugin-kubernetes-node';
5
5
  import { KubernetesRequestAuth, KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
@@ -222,6 +222,7 @@ type KubernetesProxyOptions = {
222
222
  authStrategy: AuthenticationStrategy;
223
223
  discovery: DiscoveryService;
224
224
  httpAuth: HttpAuthService;
225
+ auditor: AuditorService;
225
226
  };
226
227
  /**
227
228
  * A proxy that routes requests to the Kubernetes API.
@@ -234,9 +235,53 @@ declare class KubernetesProxy {
234
235
  private readonly clusterSupplier;
235
236
  private readonly authStrategy;
236
237
  private readonly httpAuth;
238
+ private readonly auditor;
237
239
  constructor(options: KubernetesProxyOptions);
238
240
  createRequestHandler(options: KubernetesProxyCreateRequestHandlerOptions): RequestHandler;
239
- private getMiddleware;
241
+ /**
242
+ * Authorizes the request, resolves the target cluster, and dispatches
243
+ * to either the HTTP or WebSocket proxy path. Returns `true` when
244
+ * HTTP finish listeners have been attached.
245
+ */
246
+ private authorizeAndDispatch;
247
+ /**
248
+ * Resolves the cluster, credentials, and target URL for a proxy request
249
+ * before dispatching to the middleware. This ensures all async preparation
250
+ * completes under the caller's try/catch, avoiding unhandled rejections
251
+ * from http-proxy-middleware's fire-and-forget upgrade() path.
252
+ */
253
+ private prepareProxyTarget;
254
+ private getOrCreateMiddleware;
255
+ /**
256
+ * Returns a copy of the request with query parameters stripped from both
257
+ * `originalUrl` and `url` so that sensitive values (e.g. exec command
258
+ * arguments) are not written to central audit logs.
259
+ */
260
+ private static createSanitizedRequest;
261
+ /**
262
+ * Builds a once-only audit finalizer that resolves the auditor event as
263
+ * either success or failure. Returns a setter for the resolved cluster
264
+ * name so downstream code can enrich the terminal metadata.
265
+ */
266
+ private static createAuditFinalizer;
267
+ /**
268
+ * Branches to WebSocket upgrade or HTTP middleware dispatch and wires
269
+ * up the appropriate audit listeners. Returns `true` when HTTP finish
270
+ * listeners have been attached (used by the caller's catch block to
271
+ * avoid double-finalizing).
272
+ */
273
+ private static dispatchToProxy;
274
+ /**
275
+ * Lifecycle handler for WebSocket proxy requests. Reads the audit
276
+ * finalizer and the early socket-close listener from the request,
277
+ * then wires once-only listeners on `upgrade` (success), `response`
278
+ * (upstream rejection), `error`, `socket error`, and `socket close`
279
+ * to resolve the audit event exactly once. The first terminal
280
+ * callback removes all observers to prevent leaks and duplicate
281
+ * finalization.
282
+ */
283
+ private static onProxyReqWs;
284
+ private static attachHttpAuditListeners;
240
285
  private getClusterForRequest;
241
286
  private static authHeadersToKubernetesRequestAuth;
242
287
  private static headerToDictionary;
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version = "0.21.6";
5
+ var version = "0.22.0-next.1";
6
6
  var packageinfo = {
7
7
  version: version};
8
8
 
@@ -146,7 +146,8 @@ const kubernetesPlugin = backendPluginApi.createBackendPlugin({
146
146
  catalog: pluginCatalogNode.catalogServiceRef,
147
147
  permissions: backendPluginApi.coreServices.permissions,
148
148
  auth: backendPluginApi.coreServices.auth,
149
- httpAuth: backendPluginApi.coreServices.httpAuth
149
+ httpAuth: backendPluginApi.coreServices.httpAuth,
150
+ auditor: backendPluginApi.coreServices.auditor
150
151
  },
151
152
  async init({
152
153
  http,
@@ -156,7 +157,8 @@ const kubernetesPlugin = backendPluginApi.createBackendPlugin({
156
157
  catalog,
157
158
  permissions,
158
159
  auth,
159
- httpAuth
160
+ httpAuth,
161
+ auditor
160
162
  }) {
161
163
  if (config.has("kubernetes")) {
162
164
  const initializer = KubernetesInitializer.KubernetesInitializer.create({
@@ -185,6 +187,7 @@ const kubernetesPlugin = backendPluginApi.createBackendPlugin({
185
187
  discovery,
186
188
  auth,
187
189
  httpAuth,
190
+ auditor,
188
191
  authStrategyMap: Object.fromEntries(authStrategyMap.entries()),
189
192
  fetcher,
190
193
  clusterSupplier,
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\n\nimport {\n type AuthenticationStrategy,\n kubernetesAuthStrategyExtensionPoint,\n type KubernetesAuthStrategyExtensionPoint,\n type KubernetesClustersSupplier,\n kubernetesClusterSupplierExtensionPoint,\n type KubernetesClusterSupplierExtensionPoint,\n KubernetesClusterSupplierFactory,\n type KubernetesFetcher,\n kubernetesFetcherExtensionPoint,\n type KubernetesFetcherExtensionPoint,\n KubernetesFetcherFactory,\n type KubernetesObjectsProvider,\n kubernetesObjectsProviderExtensionPoint,\n type KubernetesObjectsProviderExtensionPoint,\n KubernetesObjectsProviderFactory,\n KubernetesRouterExtensionPoint,\n kubernetesRouterExtensionPoint,\n KubernetesRouterFactory,\n type KubernetesServiceLocator,\n kubernetesServiceLocatorExtensionPoint,\n type KubernetesServiceLocatorExtensionPoint,\n KubernetesServiceLocatorFactory,\n} from '@backstage/plugin-kubernetes-node';\nimport { KubernetesRouter } from './service/KubernetesRouter';\nimport { KubernetesInitializer } from './service/KubernetesInitializer';\n\nclass ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {\n private objectsProvider: KubernetesObjectsProviderFactory | undefined;\n\n getObjectsProvider() {\n return this.objectsProvider;\n }\n\n addObjectsProvider(\n provider: KubernetesObjectsProvider | KubernetesObjectsProviderFactory,\n ) {\n if (this.objectsProvider) {\n throw new Error(\n 'Multiple Kubernetes objects provider is not supported at this time',\n );\n }\n if (typeof provider !== 'function') {\n this.objectsProvider = async () => provider;\n } else {\n this.objectsProvider = provider;\n }\n }\n}\n\nclass ClusterSuplier implements KubernetesClusterSupplierExtensionPoint {\n private clusterSupplier: KubernetesClusterSupplierFactory | undefined;\n\n getClusterSupplier() {\n return this.clusterSupplier;\n }\n\n addClusterSupplier(\n clusterSupplier:\n | KubernetesClustersSupplier\n | KubernetesClusterSupplierFactory,\n ) {\n if (this.clusterSupplier) {\n throw new Error(\n 'Multiple Kubernetes Cluster Suppliers is not supported at this time',\n );\n }\n if (typeof clusterSupplier !== 'function') {\n this.clusterSupplier = async () => clusterSupplier;\n } else {\n this.clusterSupplier = clusterSupplier;\n }\n }\n}\n\nclass Fetcher implements KubernetesFetcherExtensionPoint {\n private fetcher: KubernetesFetcherFactory | undefined;\n\n getFetcher() {\n return this.fetcher;\n }\n\n addFetcher(fetcher: KubernetesFetcher | KubernetesFetcherFactory) {\n if (this.fetcher) {\n throw new Error(\n 'Multiple Kubernetes Fetchers is not supported at this time',\n );\n }\n if (typeof fetcher !== 'function') {\n this.fetcher = async () => fetcher;\n } else {\n this.fetcher = fetcher;\n }\n }\n}\n\nclass ServiceLocator implements KubernetesServiceLocatorExtensionPoint {\n private serviceLocator: KubernetesServiceLocatorFactory | undefined;\n\n getServiceLocator() {\n return this.serviceLocator;\n }\n\n addServiceLocator(\n serviceLocator: KubernetesServiceLocator | KubernetesServiceLocatorFactory,\n ) {\n if (this.serviceLocator) {\n throw new Error(\n 'Multiple Kubernetes Service Locators is not supported at this time',\n );\n }\n\n if (typeof serviceLocator !== 'function') {\n this.serviceLocator = async () => serviceLocator;\n } else {\n this.serviceLocator = serviceLocator;\n }\n }\n}\n\nclass AuthStrategy implements KubernetesAuthStrategyExtensionPoint {\n private authStrategies: Map<string, AuthenticationStrategy> | undefined;\n\n getAuthenticationStrategies() {\n return this.authStrategies;\n }\n\n addAuthStrategy(key: string, authStrategy: AuthenticationStrategy) {\n if (!this.authStrategies) {\n this.authStrategies = new Map<string, AuthenticationStrategy>();\n }\n\n if (key.includes('-')) {\n throw new Error('Strategy name can not include dashes');\n }\n\n this.authStrategies.set(key, authStrategy);\n }\n}\n\nclass CustomRouter implements KubernetesRouterExtensionPoint {\n private router: KubernetesRouterFactory | undefined;\n\n getRouter() {\n return this.router;\n }\n\n addRouter(router: KubernetesRouterFactory) {\n if (this.router) {\n throw new Error(\n 'Multiple Kubernetes routers is not supported at this time',\n );\n }\n\n this.router = router;\n }\n}\n\n/**\n * This is the backend plugin that provides the Kubernetes integration.\n * @public\n */\nexport const kubernetesPlugin = createBackendPlugin({\n pluginId: 'kubernetes',\n register(env) {\n const extPointObjectsProvider = new ObjectsProvider();\n const extPointClusterSuplier = new ClusterSuplier();\n const extPointAuthStrategy = new AuthStrategy();\n const extPointFetcher = new Fetcher();\n const extPointServiceLocator = new ServiceLocator();\n const extPointRouter = new CustomRouter();\n\n env.registerExtensionPoint(\n kubernetesObjectsProviderExtensionPoint,\n extPointObjectsProvider,\n );\n env.registerExtensionPoint(\n kubernetesClusterSupplierExtensionPoint,\n extPointClusterSuplier,\n );\n env.registerExtensionPoint(\n kubernetesAuthStrategyExtensionPoint,\n extPointAuthStrategy,\n );\n env.registerExtensionPoint(\n kubernetesFetcherExtensionPoint,\n extPointFetcher,\n );\n env.registerExtensionPoint(\n kubernetesServiceLocatorExtensionPoint,\n extPointServiceLocator,\n );\n env.registerExtensionPoint(kubernetesRouterExtensionPoint, extPointRouter);\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n discovery: coreServices.discovery,\n catalog: catalogServiceRef,\n permissions: coreServices.permissions,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n },\n async init({\n http,\n logger,\n config,\n discovery,\n catalog,\n permissions,\n auth,\n httpAuth,\n }) {\n // TODO: this could do with a cleanup and push some of this initialization somewhere else\n if (config.has('kubernetes')) {\n const initializer = KubernetesInitializer.create({\n logger,\n config,\n catalog,\n auth,\n fetcher: extPointFetcher.getFetcher(),\n clusterSupplier: extPointClusterSuplier.getClusterSupplier(),\n serviceLocator: extPointServiceLocator.getServiceLocator(),\n objectsProvider: extPointObjectsProvider.getObjectsProvider(),\n authStrategyMap: extPointAuthStrategy.getAuthenticationStrategies(),\n });\n\n const {\n fetcher,\n authStrategyMap,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n } = await initializer.init();\n\n const router = KubernetesRouter.create({\n logger,\n config,\n catalog,\n permissions,\n discovery,\n auth,\n httpAuth,\n authStrategyMap: Object.fromEntries(authStrategyMap.entries()),\n fetcher,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n customRouter: extPointRouter.getRouter(),\n });\n\n http.use(await router.getRouter());\n } else {\n logger.warn(\n 'Failed to initialize kubernetes backend: valid kubernetes config is missing',\n );\n }\n },\n });\n },\n});\n"],"names":["createBackendPlugin","kubernetesObjectsProviderExtensionPoint","kubernetesClusterSupplierExtensionPoint","kubernetesAuthStrategyExtensionPoint","kubernetesFetcherExtensionPoint","kubernetesServiceLocatorExtensionPoint","kubernetesRouterExtensionPoint","coreServices","catalogServiceRef","KubernetesInitializer","KubernetesRouter"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\n\nimport {\n type AuthenticationStrategy,\n kubernetesAuthStrategyExtensionPoint,\n type KubernetesAuthStrategyExtensionPoint,\n type KubernetesClustersSupplier,\n kubernetesClusterSupplierExtensionPoint,\n type KubernetesClusterSupplierExtensionPoint,\n KubernetesClusterSupplierFactory,\n type KubernetesFetcher,\n kubernetesFetcherExtensionPoint,\n type KubernetesFetcherExtensionPoint,\n KubernetesFetcherFactory,\n type KubernetesObjectsProvider,\n kubernetesObjectsProviderExtensionPoint,\n type KubernetesObjectsProviderExtensionPoint,\n KubernetesObjectsProviderFactory,\n KubernetesRouterExtensionPoint,\n kubernetesRouterExtensionPoint,\n KubernetesRouterFactory,\n type KubernetesServiceLocator,\n kubernetesServiceLocatorExtensionPoint,\n type KubernetesServiceLocatorExtensionPoint,\n KubernetesServiceLocatorFactory,\n} from '@backstage/plugin-kubernetes-node';\nimport { KubernetesRouter } from './service/KubernetesRouter';\nimport { KubernetesInitializer } from './service/KubernetesInitializer';\n\nclass ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {\n private objectsProvider: KubernetesObjectsProviderFactory | undefined;\n\n getObjectsProvider() {\n return this.objectsProvider;\n }\n\n addObjectsProvider(\n provider: KubernetesObjectsProvider | KubernetesObjectsProviderFactory,\n ) {\n if (this.objectsProvider) {\n throw new Error(\n 'Multiple Kubernetes objects provider is not supported at this time',\n );\n }\n if (typeof provider !== 'function') {\n this.objectsProvider = async () => provider;\n } else {\n this.objectsProvider = provider;\n }\n }\n}\n\nclass ClusterSuplier implements KubernetesClusterSupplierExtensionPoint {\n private clusterSupplier: KubernetesClusterSupplierFactory | undefined;\n\n getClusterSupplier() {\n return this.clusterSupplier;\n }\n\n addClusterSupplier(\n clusterSupplier:\n | KubernetesClustersSupplier\n | KubernetesClusterSupplierFactory,\n ) {\n if (this.clusterSupplier) {\n throw new Error(\n 'Multiple Kubernetes Cluster Suppliers is not supported at this time',\n );\n }\n if (typeof clusterSupplier !== 'function') {\n this.clusterSupplier = async () => clusterSupplier;\n } else {\n this.clusterSupplier = clusterSupplier;\n }\n }\n}\n\nclass Fetcher implements KubernetesFetcherExtensionPoint {\n private fetcher: KubernetesFetcherFactory | undefined;\n\n getFetcher() {\n return this.fetcher;\n }\n\n addFetcher(fetcher: KubernetesFetcher | KubernetesFetcherFactory) {\n if (this.fetcher) {\n throw new Error(\n 'Multiple Kubernetes Fetchers is not supported at this time',\n );\n }\n if (typeof fetcher !== 'function') {\n this.fetcher = async () => fetcher;\n } else {\n this.fetcher = fetcher;\n }\n }\n}\n\nclass ServiceLocator implements KubernetesServiceLocatorExtensionPoint {\n private serviceLocator: KubernetesServiceLocatorFactory | undefined;\n\n getServiceLocator() {\n return this.serviceLocator;\n }\n\n addServiceLocator(\n serviceLocator: KubernetesServiceLocator | KubernetesServiceLocatorFactory,\n ) {\n if (this.serviceLocator) {\n throw new Error(\n 'Multiple Kubernetes Service Locators is not supported at this time',\n );\n }\n\n if (typeof serviceLocator !== 'function') {\n this.serviceLocator = async () => serviceLocator;\n } else {\n this.serviceLocator = serviceLocator;\n }\n }\n}\n\nclass AuthStrategy implements KubernetesAuthStrategyExtensionPoint {\n private authStrategies: Map<string, AuthenticationStrategy> | undefined;\n\n getAuthenticationStrategies() {\n return this.authStrategies;\n }\n\n addAuthStrategy(key: string, authStrategy: AuthenticationStrategy) {\n if (!this.authStrategies) {\n this.authStrategies = new Map<string, AuthenticationStrategy>();\n }\n\n if (key.includes('-')) {\n throw new Error('Strategy name can not include dashes');\n }\n\n this.authStrategies.set(key, authStrategy);\n }\n}\n\nclass CustomRouter implements KubernetesRouterExtensionPoint {\n private router: KubernetesRouterFactory | undefined;\n\n getRouter() {\n return this.router;\n }\n\n addRouter(router: KubernetesRouterFactory) {\n if (this.router) {\n throw new Error(\n 'Multiple Kubernetes routers is not supported at this time',\n );\n }\n\n this.router = router;\n }\n}\n\n/**\n * This is the backend plugin that provides the Kubernetes integration.\n * @public\n */\nexport const kubernetesPlugin = createBackendPlugin({\n pluginId: 'kubernetes',\n register(env) {\n const extPointObjectsProvider = new ObjectsProvider();\n const extPointClusterSuplier = new ClusterSuplier();\n const extPointAuthStrategy = new AuthStrategy();\n const extPointFetcher = new Fetcher();\n const extPointServiceLocator = new ServiceLocator();\n const extPointRouter = new CustomRouter();\n\n env.registerExtensionPoint(\n kubernetesObjectsProviderExtensionPoint,\n extPointObjectsProvider,\n );\n env.registerExtensionPoint(\n kubernetesClusterSupplierExtensionPoint,\n extPointClusterSuplier,\n );\n env.registerExtensionPoint(\n kubernetesAuthStrategyExtensionPoint,\n extPointAuthStrategy,\n );\n env.registerExtensionPoint(\n kubernetesFetcherExtensionPoint,\n extPointFetcher,\n );\n env.registerExtensionPoint(\n kubernetesServiceLocatorExtensionPoint,\n extPointServiceLocator,\n );\n env.registerExtensionPoint(kubernetesRouterExtensionPoint, extPointRouter);\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n discovery: coreServices.discovery,\n catalog: catalogServiceRef,\n permissions: coreServices.permissions,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n auditor: coreServices.auditor,\n },\n async init({\n http,\n logger,\n config,\n discovery,\n catalog,\n permissions,\n auth,\n httpAuth,\n auditor,\n }) {\n // TODO: this could do with a cleanup and push some of this initialization somewhere else\n if (config.has('kubernetes')) {\n const initializer = KubernetesInitializer.create({\n logger,\n config,\n catalog,\n auth,\n fetcher: extPointFetcher.getFetcher(),\n clusterSupplier: extPointClusterSuplier.getClusterSupplier(),\n serviceLocator: extPointServiceLocator.getServiceLocator(),\n objectsProvider: extPointObjectsProvider.getObjectsProvider(),\n authStrategyMap: extPointAuthStrategy.getAuthenticationStrategies(),\n });\n\n const {\n fetcher,\n authStrategyMap,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n } = await initializer.init();\n\n const router = KubernetesRouter.create({\n logger,\n config,\n catalog,\n permissions,\n discovery,\n auth,\n httpAuth,\n auditor,\n authStrategyMap: Object.fromEntries(authStrategyMap.entries()),\n fetcher,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n customRouter: extPointRouter.getRouter(),\n });\n\n http.use(await router.getRouter());\n } else {\n logger.warn(\n 'Failed to initialize kubernetes backend: valid kubernetes config is missing',\n );\n }\n },\n });\n },\n});\n"],"names":["createBackendPlugin","kubernetesObjectsProviderExtensionPoint","kubernetesClusterSupplierExtensionPoint","kubernetesAuthStrategyExtensionPoint","kubernetesFetcherExtensionPoint","kubernetesServiceLocatorExtensionPoint","kubernetesRouterExtensionPoint","coreServices","catalogServiceRef","KubernetesInitializer","KubernetesRouter"],"mappings":"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"}
@@ -5,9 +5,9 @@ var errors = require('@backstage/errors');
5
5
  var requirePermission = require('../auth/requirePermission.cjs.js');
6
6
  var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
7
7
 
8
- const addResourceRoutesToRouter = (router, catalog, objectsProvider, httpAuth, permissionApi) => {
8
+ const addResourceRoutesToRouter = (router, catalog, objectsProvider, httpAuth, permissionApi, auditor, logger) => {
9
9
  const getEntityByReq = async (req) => {
10
- const rawEntityRef = req.body.entityRef;
10
+ const rawEntityRef = req.body?.entityRef;
11
11
  if (rawEntityRef && typeof rawEntityRef !== "string") {
12
12
  throw new errors.InputError(`entity query must be a string`);
13
13
  } else if (!rawEntityRef) {
@@ -30,46 +30,82 @@ const addResourceRoutesToRouter = (router, catalog, objectsProvider, httpAuth, p
30
30
  return entity;
31
31
  };
32
32
  router.post("/resources/workloads/query", async (req, res) => {
33
- await requirePermission.requirePermission(
34
- permissionApi,
35
- pluginKubernetesCommon.kubernetesResourcesReadPermission,
36
- httpAuth,
37
- req
38
- );
39
- const entity = await getEntityByReq(req);
40
- const response = await objectsProvider.getKubernetesObjectsByEntity(
41
- {
42
- entity,
43
- auth: req.body.auth
44
- },
45
- { credentials: await httpAuth.credentials(req) }
46
- );
47
- res.json(response);
33
+ const entityRef = typeof req.body?.entityRef === "string" ? req.body.entityRef : void 0;
34
+ const auditorEvent = await auditor.createEvent({
35
+ eventId: "resource-fetch",
36
+ request: req,
37
+ meta: { queryType: "workloads", entityRef }
38
+ });
39
+ try {
40
+ await requirePermission.requirePermission(
41
+ permissionApi,
42
+ pluginKubernetesCommon.kubernetesResourcesReadPermission,
43
+ httpAuth,
44
+ req
45
+ );
46
+ const entity = await getEntityByReq(req);
47
+ const response = await objectsProvider.getKubernetesObjectsByEntity(
48
+ {
49
+ entity,
50
+ auth: req.body.auth
51
+ },
52
+ { credentials: await httpAuth.credentials(req) }
53
+ );
54
+ res.json(response);
55
+ auditorEvent.success().catch(
56
+ (error) => logger.error(
57
+ "Failed to emit audit event resource-fetch (workloads)",
58
+ error
59
+ )
60
+ );
61
+ } catch (error) {
62
+ const err = error instanceof Error ? error : new Error(String(error));
63
+ await auditorEvent.fail({ error: err });
64
+ throw error;
65
+ }
48
66
  });
49
67
  router.post("/resources/custom/query", async (req, res) => {
50
- await requirePermission.requirePermission(
51
- permissionApi,
52
- pluginKubernetesCommon.kubernetesResourcesReadPermission,
53
- httpAuth,
54
- req
55
- );
56
- const entity = await getEntityByReq(req);
57
- if (!req.body.customResources) {
58
- throw new errors.InputError("customResources is a required field");
59
- } else if (!Array.isArray(req.body.customResources)) {
60
- throw new errors.InputError("customResources must be an array");
61
- } else if (req.body.customResources.length === 0) {
62
- throw new errors.InputError("at least 1 customResource is required");
68
+ const entityRef = typeof req.body?.entityRef === "string" ? req.body.entityRef : void 0;
69
+ const auditorEvent = await auditor.createEvent({
70
+ eventId: "resource-fetch",
71
+ request: req,
72
+ meta: { queryType: "custom", entityRef }
73
+ });
74
+ try {
75
+ await requirePermission.requirePermission(
76
+ permissionApi,
77
+ pluginKubernetesCommon.kubernetesResourcesReadPermission,
78
+ httpAuth,
79
+ req
80
+ );
81
+ const entity = await getEntityByReq(req);
82
+ if (!req.body.customResources) {
83
+ throw new errors.InputError("customResources is a required field");
84
+ } else if (!Array.isArray(req.body.customResources)) {
85
+ throw new errors.InputError("customResources must be an array");
86
+ } else if (req.body.customResources.length === 0) {
87
+ throw new errors.InputError("at least 1 customResource is required");
88
+ }
89
+ const response = await objectsProvider.getCustomResourcesByEntity(
90
+ {
91
+ entity,
92
+ customResources: req.body.customResources,
93
+ auth: req.body.auth
94
+ },
95
+ { credentials: await httpAuth.credentials(req) }
96
+ );
97
+ res.json(response);
98
+ auditorEvent.success().catch(
99
+ (error) => logger.error(
100
+ "Failed to emit audit event resource-fetch (custom)",
101
+ error
102
+ )
103
+ );
104
+ } catch (error) {
105
+ const err = error instanceof Error ? error : new Error(String(error));
106
+ await auditorEvent.fail({ error: err });
107
+ throw error;
63
108
  }
64
- const response = await objectsProvider.getCustomResourcesByEntity(
65
- {
66
- entity,
67
- customResources: req.body.customResources,
68
- auth: req.body.auth
69
- },
70
- { credentials: await httpAuth.credentials(req) }
71
- );
72
- res.json(response);
73
109
  });
74
110
  };
75
111
 
@@ -1 +1 @@
1
- {"version":3,"file":"resourcesRoutes.cjs.js","sources":["../../src/routes/resourcesRoutes.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n CompoundEntityRef,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { InputError } from '@backstage/errors';\nimport express, { Request } from 'express';\nimport { KubernetesObjectsProvider } from '@backstage/plugin-kubernetes-node';\nimport { HttpAuthService } from '@backstage/backend-plugin-api';\nimport { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport { requirePermission } from '../auth/requirePermission';\nimport { kubernetesResourcesReadPermission } from '@backstage/plugin-kubernetes-common';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const addResourceRoutesToRouter = (\n router: express.Router,\n catalog: CatalogService,\n objectsProvider: KubernetesObjectsProvider,\n httpAuth: HttpAuthService,\n permissionApi: PermissionEvaluator,\n) => {\n const getEntityByReq = async (req: Request<any>) => {\n const rawEntityRef = req.body.entityRef;\n if (rawEntityRef && typeof rawEntityRef !== 'string') {\n throw new InputError(`entity query must be a string`);\n } else if (!rawEntityRef) {\n throw new InputError('entity is a required field');\n }\n let entityRef: CompoundEntityRef | undefined = undefined;\n\n try {\n entityRef = parseEntityRef(rawEntityRef);\n } catch (error) {\n throw new InputError(`Invalid entity ref, ${error}`);\n }\n\n const entity = await catalog.getEntityByRef(entityRef, {\n credentials: await httpAuth.credentials(req),\n });\n if (!entity) {\n throw new InputError(\n `Entity ref missing, ${stringifyEntityRef(entityRef)}`,\n );\n }\n\n return entity;\n };\n\n router.post('/resources/workloads/query', async (req, res) => {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const entity = await getEntityByReq(req);\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity,\n auth: req.body.auth,\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n });\n\n router.post('/resources/custom/query', async (req, res) => {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const entity = await getEntityByReq(req);\n\n if (!req.body.customResources) {\n throw new InputError('customResources is a required field');\n } else if (!Array.isArray(req.body.customResources)) {\n throw new InputError('customResources must be an array');\n } else if (req.body.customResources.length === 0) {\n throw new InputError('at least 1 customResource is required');\n }\n\n const response = await objectsProvider.getCustomResourcesByEntity(\n {\n entity,\n customResources: req.body.customResources,\n auth: req.body.auth,\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n });\n};\n"],"names":["InputError","parseEntityRef","stringifyEntityRef","requirePermission","kubernetesResourcesReadPermission"],"mappings":"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"}
1
+ {"version":3,"file":"resourcesRoutes.cjs.js","sources":["../../src/routes/resourcesRoutes.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n CompoundEntityRef,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { InputError } from '@backstage/errors';\nimport express, { Request } from 'express';\nimport { KubernetesObjectsProvider } from '@backstage/plugin-kubernetes-node';\nimport {\n AuditorService,\n HttpAuthService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport { requirePermission } from '../auth/requirePermission';\nimport { kubernetesResourcesReadPermission } from '@backstage/plugin-kubernetes-common';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const addResourceRoutesToRouter = (\n router: express.Router,\n catalog: CatalogService,\n objectsProvider: KubernetesObjectsProvider,\n httpAuth: HttpAuthService,\n permissionApi: PermissionEvaluator,\n auditor: AuditorService,\n logger: LoggerService,\n) => {\n const getEntityByReq = async (req: Request<any>) => {\n const rawEntityRef = req.body?.entityRef;\n if (rawEntityRef && typeof rawEntityRef !== 'string') {\n throw new InputError(`entity query must be a string`);\n } else if (!rawEntityRef) {\n throw new InputError('entity is a required field');\n }\n let entityRef: CompoundEntityRef | undefined = undefined;\n\n try {\n entityRef = parseEntityRef(rawEntityRef);\n } catch (error) {\n throw new InputError(`Invalid entity ref, ${error}`);\n }\n\n const entity = await catalog.getEntityByRef(entityRef, {\n credentials: await httpAuth.credentials(req),\n });\n if (!entity) {\n throw new InputError(\n `Entity ref missing, ${stringifyEntityRef(entityRef)}`,\n );\n }\n\n return entity;\n };\n\n router.post('/resources/workloads/query', async (req, res) => {\n const entityRef =\n typeof req.body?.entityRef === 'string' ? req.body.entityRef : undefined;\n\n const auditorEvent = await auditor.createEvent({\n eventId: 'resource-fetch',\n request: req,\n meta: { queryType: 'workloads', entityRef },\n });\n\n try {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const entity = await getEntityByReq(req);\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity,\n auth: req.body.auth,\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n auditorEvent\n .success()\n .catch(error =>\n logger.error(\n 'Failed to emit audit event resource-fetch (workloads)',\n error,\n ),\n );\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n await auditorEvent.fail({ error: err });\n throw error;\n }\n });\n\n router.post('/resources/custom/query', async (req, res) => {\n const entityRef =\n typeof req.body?.entityRef === 'string' ? req.body.entityRef : undefined;\n\n const auditorEvent = await auditor.createEvent({\n eventId: 'resource-fetch',\n request: req,\n meta: { queryType: 'custom', entityRef },\n });\n\n try {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const entity = await getEntityByReq(req);\n\n if (!req.body.customResources) {\n throw new InputError('customResources is a required field');\n } else if (!Array.isArray(req.body.customResources)) {\n throw new InputError('customResources must be an array');\n } else if (req.body.customResources.length === 0) {\n throw new InputError('at least 1 customResource is required');\n }\n\n const response = await objectsProvider.getCustomResourcesByEntity(\n {\n entity,\n customResources: req.body.customResources,\n auth: req.body.auth,\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n auditorEvent\n .success()\n .catch(error =>\n logger.error(\n 'Failed to emit audit event resource-fetch (custom)',\n error,\n ),\n );\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n await auditorEvent.fail({ error: err });\n throw error;\n }\n });\n};\n"],"names":["InputError","parseEntityRef","stringifyEntityRef","requirePermission","kubernetesResourcesReadPermission"],"mappings":";;;;;;;AAiCO,MAAM,yBAAA,GAA4B,CACvC,MAAA,EACA,OAAA,EACA,iBACA,QAAA,EACA,aAAA,EACA,SACA,MAAA,KACG;AACH,EAAA,MAAM,cAAA,GAAiB,OAAO,GAAA,KAAsB;AAClD,IAAA,MAAM,YAAA,GAAe,IAAI,IAAA,EAAM,SAAA;AAC/B,IAAA,IAAI,YAAA,IAAgB,OAAO,YAAA,KAAiB,QAAA,EAAU;AACpD,MAAA,MAAM,IAAIA,kBAAW,CAAA,6BAAA,CAA+B,CAAA;AAAA,IACtD,CAAA,MAAA,IAAW,CAAC,YAAA,EAAc;AACxB,MAAA,MAAM,IAAIA,kBAAW,4BAA4B,CAAA;AAAA,IACnD;AACA,IAAA,IAAI,SAAA,GAA2C,MAAA;AAE/C,IAAA,IAAI;AACF,MAAA,SAAA,GAAYC,4BAAe,YAAY,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAID,iBAAA,CAAW,CAAA,oBAAA,EAAuB,KAAK,CAAA,CAAE,CAAA;AAAA,IACrD;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,cAAA,CAAe,SAAA,EAAW;AAAA,MACrD,WAAA,EAAa,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG;AAAA,KAC5C,CAAA;AACD,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAIA,iBAAA;AAAA,QACR,CAAA,oBAAA,EAAuBE,+BAAA,CAAmB,SAAS,CAAC,CAAA;AAAA,OACtD;AAAA,IACF;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAA,CAAO,IAAA,CAAK,4BAAA,EAA8B,OAAO,GAAA,EAAK,GAAA,KAAQ;AAC5D,IAAA,MAAM,SAAA,GACJ,OAAO,GAAA,CAAI,IAAA,EAAM,cAAc,QAAA,GAAW,GAAA,CAAI,KAAK,SAAA,GAAY,MAAA;AAEjE,IAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,WAAA,CAAY;AAAA,MAC7C,OAAA,EAAS,gBAAA;AAAA,MACT,OAAA,EAAS,GAAA;AAAA,MACT,IAAA,EAAM,EAAE,SAAA,EAAW,WAAA,EAAa,SAAA;AAAU,KAC3C,CAAA;AAED,IAAA,IAAI;AACF,MAAA,MAAMC,mCAAA;AAAA,QACJ,aAAA;AAAA,QACAC,wDAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,GAAG,CAAA;AACvC,MAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,4BAAA;AAAA,QACrC;AAAA,UACE,MAAA;AAAA,UACA,IAAA,EAAM,IAAI,IAAA,CAAK;AAAA,SACjB;AAAA,QACA,EAAE,WAAA,EAAa,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAAE,OACjD;AACA,MAAA,GAAA,CAAI,KAAK,QAAQ,CAAA;AACjB,MAAA,YAAA,CACG,SAAQ,CACR,KAAA;AAAA,QAAM,WACL,MAAA,CAAO,KAAA;AAAA,UACL,uDAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,IACJ,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,MAAA,MAAM,YAAA,CAAa,IAAA,CAAK,EAAE,KAAA,EAAO,KAAK,CAAA;AACtC,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,IAAA,CAAK,yBAAA,EAA2B,OAAO,GAAA,EAAK,GAAA,KAAQ;AACzD,IAAA,MAAM,SAAA,GACJ,OAAO,GAAA,CAAI,IAAA,EAAM,cAAc,QAAA,GAAW,GAAA,CAAI,KAAK,SAAA,GAAY,MAAA;AAEjE,IAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,WAAA,CAAY;AAAA,MAC7C,OAAA,EAAS,gBAAA;AAAA,MACT,OAAA,EAAS,GAAA;AAAA,MACT,IAAA,EAAM,EAAE,SAAA,EAAW,QAAA,EAAU,SAAA;AAAU,KACxC,CAAA;AAED,IAAA,IAAI;AACF,MAAA,MAAMD,mCAAA;AAAA,QACJ,aAAA;AAAA,QACAC,wDAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,GAAG,CAAA;AAEvC,MAAA,IAAI,CAAC,GAAA,CAAI,IAAA,CAAK,eAAA,EAAiB;AAC7B,QAAA,MAAM,IAAIJ,kBAAW,qCAAqC,CAAA;AAAA,MAC5D,WAAW,CAAC,KAAA,CAAM,QAAQ,GAAA,CAAI,IAAA,CAAK,eAAe,CAAA,EAAG;AACnD,QAAA,MAAM,IAAIA,kBAAW,kCAAkC,CAAA;AAAA,MACzD,CAAA,MAAA,IAAW,GAAA,CAAI,IAAA,CAAK,eAAA,CAAgB,WAAW,CAAA,EAAG;AAChD,QAAA,MAAM,IAAIA,kBAAW,uCAAuC,CAAA;AAAA,MAC9D;AAEA,MAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,0BAAA;AAAA,QACrC;AAAA,UACE,MAAA;AAAA,UACA,eAAA,EAAiB,IAAI,IAAA,CAAK,eAAA;AAAA,UAC1B,IAAA,EAAM,IAAI,IAAA,CAAK;AAAA,SACjB;AAAA,QACA,EAAE,WAAA,EAAa,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAAE,OACjD;AACA,MAAA,GAAA,CAAI,KAAK,QAAQ,CAAA;AACjB,MAAA,YAAA,CACG,SAAQ,CACR,KAAA;AAAA,QAAM,WACL,MAAA,CAAO,KAAA;AAAA,UACL,oDAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,IACJ,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,MAAA,MAAM,YAAA,CAAa,IAAA,CAAK,EAAE,KAAA,EAAO,KAAK,CAAA;AACtC,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF,CAAC,CAAA;AACH;;;;"}