@backstage/plugin-kubernetes-backend 0.11.6-next.2 → 0.12.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.
package/dist/index.cjs.js CHANGED
@@ -6,9 +6,11 @@ var credentialProviders = require('@aws-sdk/credential-providers');
6
6
  var signatureV4 = require('@aws-sdk/signature-v4');
7
7
  var sha256Js = require('@aws-crypto/sha256-js');
8
8
  var integrationAwsNode = require('@backstage/integration-aws-node');
9
+ var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
9
10
  var identity = require('@azure/identity');
10
11
  var container = require('@google-cloud/container');
11
- var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
12
+ var clientNode = require('@kubernetes/client-node');
13
+ var fs = require('fs-extra');
12
14
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
13
15
  var express = require('express');
14
16
  var Router = require('express-promise-router');
@@ -17,11 +19,9 @@ var errors = require('@backstage/errors');
17
19
  var catalogClient = require('@backstage/catalog-client');
18
20
  var catalogModel = require('@backstage/catalog-model');
19
21
  var pluginAuthNode = require('@backstage/plugin-auth-node');
20
- var clientNode = require('@kubernetes/client-node');
21
22
  var lodash = require('lodash');
22
23
  var fetch = require('node-fetch');
23
24
  var https = require('https');
24
- var fs = require('fs-extra');
25
25
  var pluginPermissionCommon = require('@backstage/plugin-permission-common');
26
26
  var httpProxyMiddleware = require('http-proxy-middleware');
27
27
 
@@ -46,16 +46,29 @@ function _interopNamespace(e) {
46
46
  }
47
47
 
48
48
  var container__namespace = /*#__PURE__*/_interopNamespace(container);
49
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
49
50
  var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
50
51
  var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
51
52
  var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
52
53
  var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
53
54
  var https__namespace = /*#__PURE__*/_interopNamespace(https);
54
- var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
55
55
 
56
- class AksKubernetesAuthTranslator {
57
- async decorateClusterDetailsWithAuth(clusterDetails, auth) {
58
- return { ...clusterDetails, serviceAccountToken: auth.aks };
56
+ class AksStrategy {
57
+ async getCredential(_, requestAuth) {
58
+ const token = requestAuth.aks;
59
+ return token ? { type: "bearer token", token } : { type: "anonymous" };
60
+ }
61
+ validateCluster() {
62
+ return [];
63
+ }
64
+ }
65
+
66
+ class AnonymousStrategy {
67
+ async getCredential() {
68
+ return { type: "anonymous" };
69
+ }
70
+ validateCluster() {
71
+ return [];
59
72
  }
60
73
  }
61
74
 
@@ -66,11 +79,24 @@ var __publicField$a = (obj, key, value) => {
66
79
  return value;
67
80
  };
68
81
  const defaultRegion = "us-east-1";
69
- class AwsIamKubernetesAuthTranslator {
82
+ class AwsIamStrategy {
70
83
  constructor(opts) {
71
84
  __publicField$a(this, "credsManager");
72
85
  this.credsManager = integrationAwsNode.DefaultAwsCredentialsManager.fromConfig(opts.config);
73
86
  }
87
+ async getCredential(clusterDetails) {
88
+ return {
89
+ type: "bearer token",
90
+ token: await this.getBearerToken(
91
+ clusterDetails.name,
92
+ clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE],
93
+ clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID]
94
+ )
95
+ };
96
+ }
97
+ validateCluster() {
98
+ return [];
99
+ }
74
100
  async getBearerToken(clusterName, assumeRole, externalId) {
75
101
  var _a, _b;
76
102
  const region = (_a = process.env.AWS_REGION) != null ? _a : defaultRegion;
@@ -121,18 +147,6 @@ class AwsIamKubernetesAuthTranslator {
121
147
  const url = `https://${request.hostname}${request.path}?${query}`;
122
148
  return `k8s-aws-v1.${Buffer.from(url).toString("base64url")}`;
123
149
  }
124
- async decorateClusterDetailsWithAuth(clusterDetails) {
125
- const clusterDetailsWithAuthToken = Object.assign(
126
- {},
127
- clusterDetails
128
- );
129
- clusterDetailsWithAuthToken.serviceAccountToken = await this.getBearerToken(
130
- clusterDetails.name,
131
- clusterDetails.assumeRole,
132
- clusterDetails.externalId
133
- );
134
- return clusterDetailsWithAuthToken;
135
- }
136
150
  }
137
151
 
138
152
  var __defProp$9 = Object.defineProperty;
@@ -142,29 +156,24 @@ var __publicField$9 = (obj, key, value) => {
142
156
  return value;
143
157
  };
144
158
  const aksScope = "6dae42f8-4368-4678-94ff-3960e28e3630/.default";
145
- class AzureIdentityKubernetesAuthTranslator {
159
+ class AzureIdentityStrategy {
146
160
  constructor(logger, tokenCredential = new identity.DefaultAzureCredential()) {
147
161
  this.logger = logger;
148
162
  this.tokenCredential = tokenCredential;
149
163
  __publicField$9(this, "accessToken", { token: "", expiresOnTimestamp: 0 });
150
164
  __publicField$9(this, "newTokenPromise");
151
165
  }
152
- async decorateClusterDetailsWithAuth(clusterDetails) {
153
- const clusterDetailsWithAuthToken = Object.assign(
154
- {},
155
- clusterDetails
156
- );
157
- clusterDetailsWithAuthToken.serviceAccountToken = await this.getToken();
158
- return clusterDetailsWithAuthToken;
159
- }
160
- async getToken() {
166
+ async getCredential() {
161
167
  if (!this.tokenRequiresRefresh()) {
162
- return this.accessToken.token;
168
+ return { type: "bearer token", token: this.accessToken.token };
163
169
  }
164
170
  if (!this.newTokenPromise) {
165
171
  this.newTokenPromise = this.fetchNewToken();
166
172
  }
167
- return this.newTokenPromise;
173
+ return this.newTokenPromise ? { type: "bearer token", token: await this.newTokenPromise } : { type: "anonymous" };
174
+ }
175
+ validateCluster() {
176
+ return [];
168
177
  }
169
178
  async fetchNewToken() {
170
179
  try {
@@ -195,40 +204,34 @@ class AzureIdentityKubernetesAuthTranslator {
195
204
  }
196
205
  }
197
206
 
198
- class GoogleKubernetesAuthTranslator {
199
- async decorateClusterDetailsWithAuth(clusterDetails, authConfig) {
200
- const clusterDetailsWithAuthToken = Object.assign(
201
- {},
202
- clusterDetails
203
- );
204
- const authToken = authConfig.google;
205
- if (authToken) {
206
- clusterDetailsWithAuthToken.serviceAccountToken = authToken;
207
- } else {
207
+ class GoogleStrategy {
208
+ async getCredential(_, requestAuth) {
209
+ const token = requestAuth.google;
210
+ if (!token) {
208
211
  throw new Error(
209
212
  "Google token not found under auth.google in request body"
210
213
  );
211
214
  }
212
- return clusterDetailsWithAuthToken;
215
+ return { type: "bearer token", token };
216
+ }
217
+ validateCluster() {
218
+ return [];
213
219
  }
214
220
  }
215
221
 
216
- class GoogleServiceAccountAuthTranslator {
217
- async decorateClusterDetailsWithAuth(clusterDetails) {
218
- const clusterDetailsWithAuthToken = Object.assign(
219
- {},
220
- clusterDetails
221
- );
222
+ class GoogleServiceAccountStrategy {
223
+ async getCredential() {
222
224
  const client = new container__namespace.v1.ClusterManagerClient();
223
- const accessToken = await client.auth.getAccessToken();
224
- if (accessToken) {
225
- clusterDetailsWithAuthToken.serviceAccountToken = accessToken;
226
- } else {
225
+ const token = await client.auth.getAccessToken();
226
+ if (!token) {
227
227
  throw new Error(
228
228
  "Unable to obtain access token for the current Google Application Default Credentials"
229
229
  );
230
230
  }
231
- return clusterDetailsWithAuthToken;
231
+ return { type: "bearer token", token };
232
+ }
233
+ validateCluster() {
234
+ return [];
232
235
  }
233
236
  }
234
237
 
@@ -238,49 +241,76 @@ var __publicField$8 = (obj, key, value) => {
238
241
  __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
239
242
  return value;
240
243
  };
241
- class DispatchingKubernetesAuthTranslator {
244
+ class DispatchStrategy {
242
245
  constructor(options) {
243
- __publicField$8(this, "translatorMap");
244
- this.translatorMap = options.authTranslatorMap;
246
+ __publicField$8(this, "strategyMap");
247
+ this.strategyMap = options.authStrategyMap;
245
248
  }
246
- decorateClusterDetailsWithAuth(clusterDetails, auth) {
247
- if (this.translatorMap[clusterDetails.authProvider]) {
248
- return this.translatorMap[clusterDetails.authProvider].decorateClusterDetailsWithAuth(clusterDetails, auth);
249
+ getCredential(clusterDetails, auth) {
250
+ const authProvider = clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
251
+ if (this.strategyMap[authProvider]) {
252
+ return this.strategyMap[authProvider].getCredential(clusterDetails, auth);
249
253
  }
250
254
  throw new Error(
251
- `authProvider "${clusterDetails.authProvider}" has no KubernetesAuthTranslator associated with it`
255
+ `authProvider "${authProvider}" has no AuthenticationStrategy associated with it`
252
256
  );
253
257
  }
258
+ validateCluster(authMetadata) {
259
+ const authProvider = authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
260
+ const strategy = this.strategyMap[authProvider];
261
+ if (!strategy) {
262
+ return [
263
+ new Error(
264
+ `authProvider "${authProvider}" has no config associated with it`
265
+ )
266
+ ];
267
+ }
268
+ return strategy.validateCluster(authMetadata);
269
+ }
254
270
  }
255
271
 
256
- class NoopKubernetesAuthTranslator {
257
- async decorateClusterDetailsWithAuth(clusterDetails) {
258
- return clusterDetails;
272
+ class ServiceAccountStrategy {
273
+ async getCredential(clusterDetails) {
274
+ const token = clusterDetails.authMetadata.serviceAccountToken;
275
+ if (token) {
276
+ return { type: "bearer token", token };
277
+ }
278
+ const kc = new clientNode.KubeConfig();
279
+ kc.loadFromCluster();
280
+ const user = kc.getCurrentUser();
281
+ return {
282
+ type: "bearer token",
283
+ token: fs__default["default"].readFileSync(user.authProvider.config.tokenFile).toString()
284
+ };
285
+ }
286
+ validateCluster() {
287
+ return [];
259
288
  }
260
289
  }
261
290
 
262
- class OidcKubernetesAuthTranslator {
263
- async decorateClusterDetailsWithAuth(clusterDetails, authConfig) {
291
+ class OidcStrategy {
292
+ async getCredential(clusterDetails, authConfig) {
264
293
  var _a;
265
- const clusterDetailsWithAuthToken = Object.assign(
266
- {},
267
- clusterDetails
268
- );
269
- const { oidcTokenProvider } = clusterDetails;
294
+ const oidcTokenProvider = clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];
270
295
  if (!oidcTokenProvider || oidcTokenProvider === "") {
271
296
  throw new Error(
272
297
  `oidc authProvider requires a configured oidcTokenProvider`
273
298
  );
274
299
  }
275
- const authToken = (_a = authConfig.oidc) == null ? void 0 : _a[oidcTokenProvider];
276
- if (authToken) {
277
- clusterDetailsWithAuthToken.serviceAccountToken = authToken;
278
- } else {
300
+ const token = (_a = authConfig.oidc) == null ? void 0 : _a[oidcTokenProvider];
301
+ if (!token) {
279
302
  throw new Error(
280
303
  `Auth token not found under oidc.${oidcTokenProvider} in request body`
281
304
  );
282
305
  }
283
- return clusterDetailsWithAuthToken;
306
+ return { type: "bearer token", token };
307
+ }
308
+ validateCluster(authMetadata) {
309
+ const oidcTokenProvider = authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];
310
+ if (!oidcTokenProvider || oidcTokenProvider === "") {
311
+ return [new Error(`Must specify a token provider for 'oidc' strategy`)];
312
+ }
313
+ return [];
284
314
  }
285
315
  }
286
316
 
@@ -295,7 +325,7 @@ class ConfigClusterLocator {
295
325
  __publicField$7(this, "clusterDetails");
296
326
  this.clusterDetails = clusterDetails;
297
327
  }
298
- static fromConfig(config) {
328
+ static fromConfig(config, authStrategy) {
299
329
  return new ConfigClusterLocator(
300
330
  config.getConfigArray("clusters").map((c) => {
301
331
  var _a, _b;
@@ -303,12 +333,14 @@ class ConfigClusterLocator {
303
333
  const clusterDetails = {
304
334
  name: c.getString("name"),
305
335
  url: c.getString("url"),
306
- serviceAccountToken: c.getOptionalString("serviceAccountToken"),
307
336
  skipTLSVerify: (_a = c.getOptionalBoolean("skipTLSVerify")) != null ? _a : false,
308
337
  skipMetricsLookup: (_b = c.getOptionalBoolean("skipMetricsLookup")) != null ? _b : false,
309
338
  caData: c.getOptionalString("caData"),
310
339
  caFile: c.getOptionalString("caFile"),
311
- authProvider
340
+ authMetadata: {
341
+ [pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER]: authProvider,
342
+ ...ConfigClusterLocator.parseAuthMetadata(c)
343
+ }
312
344
  };
313
345
  const customResources = c.getOptionalConfigArray("customResources");
314
346
  if (customResources) {
@@ -331,40 +363,38 @@ class ConfigClusterLocator {
331
363
  if (c.has("dashboardParameters")) {
332
364
  clusterDetails.dashboardParameters = c.get("dashboardParameters");
333
365
  }
334
- switch (authProvider) {
335
- case "google": {
336
- return clusterDetails;
337
- }
338
- case "aws": {
339
- const assumeRole = c.getOptionalString("assumeRole");
340
- const externalId = c.getOptionalString("externalId");
341
- return { assumeRole, externalId, ...clusterDetails };
342
- }
343
- case "azure": {
344
- return clusterDetails;
345
- }
346
- case "oidc": {
347
- const oidcTokenProvider = c.getString("oidcTokenProvider");
348
- return { oidcTokenProvider, ...clusterDetails };
349
- }
350
- case "serviceAccount": {
351
- return clusterDetails;
352
- }
353
- case "googleServiceAccount": {
354
- return clusterDetails;
355
- }
356
- case "aks": {
357
- return clusterDetails;
358
- }
359
- default: {
360
- throw new Error(
361
- `authProvider "${authProvider}" has no config associated with it`
362
- );
363
- }
366
+ const validationErrors = authStrategy.validateCluster(
367
+ clusterDetails.authMetadata
368
+ );
369
+ if (validationErrors.length !== 0) {
370
+ throw new Error(
371
+ `Invalid cluster '${clusterDetails.name}': ${validationErrors.map((e) => e.message).join(", ")}`
372
+ );
364
373
  }
374
+ return clusterDetails;
365
375
  })
366
376
  );
367
377
  }
378
+ static parseAuthMetadata(clusterConfig) {
379
+ const serviceAccountToken = clusterConfig.getOptionalString(
380
+ "serviceAccountToken"
381
+ );
382
+ const assumeRole = clusterConfig.getOptionalString("assumeRole");
383
+ const externalId = clusterConfig.getOptionalString("externalId");
384
+ const oidcTokenProvider = clusterConfig.getOptionalString("oidcTokenProvider");
385
+ return serviceAccountToken || assumeRole || externalId || oidcTokenProvider ? {
386
+ ...serviceAccountToken && { serviceAccountToken },
387
+ ...assumeRole && {
388
+ [pluginKubernetesCommon.ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE]: assumeRole
389
+ },
390
+ ...externalId && {
391
+ [pluginKubernetesCommon.ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID]: externalId
392
+ },
393
+ ...oidcTokenProvider && {
394
+ [pluginKubernetesCommon.ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER]: oidcTokenProvider
395
+ }
396
+ } : void 0;
397
+ }
368
398
  async getClusters() {
369
399
  return this.clusterDetails;
370
400
  }
@@ -467,7 +497,7 @@ class GkeClusterLocator {
467
497
  // TODO filter out clusters which don't have name or endpoint
468
498
  name: (_a2 = r.name) != null ? _a2 : "unknown",
469
499
  url: `https://${(_b = r.endpoint) != null ? _b : ""}`,
470
- authProvider: "google",
500
+ authMetadata: { [pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER]: "google" },
471
501
  skipTLSVerify,
472
502
  skipMetricsLookup,
473
503
  ...exposeDashboard ? {
@@ -522,21 +552,13 @@ class CatalogClusterLocator {
522
552
  const clusterDetails = {
523
553
  name: entity.metadata.name,
524
554
  url: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_API_SERVER],
555
+ authMetadata: entity.metadata.annotations,
525
556
  caData: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_API_SERVER_CA],
526
- authProvider: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER],
527
- oidcTokenProvider: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER],
528
557
  skipMetricsLookup: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_SKIP_METRICS_LOOKUP] === "true",
529
558
  skipTLSVerify: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_SKIP_TLS_VERIFY] === "true",
530
559
  dashboardUrl: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_DASHBOARD_URL],
531
560
  dashboardApp: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_DASHBOARD_APP]
532
561
  };
533
- if (clusterDetails.authProvider === "aws") {
534
- return {
535
- ...clusterDetails,
536
- assumeRole: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AWS_ASSUME_ROLE],
537
- externalId: entity.metadata.annotations[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AWS_EXTERNAL_ID]
538
- };
539
- }
540
562
  return clusterDetails;
541
563
  });
542
564
  }
@@ -555,7 +577,9 @@ class LocalKubectlProxyClusterLocator {
555
577
  {
556
578
  name: "local",
557
579
  url: "http:/localhost:8001",
558
- authProvider: "localKubectlProxy",
580
+ authMetadata: {
581
+ [pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER]: "localKubectlProxy"
582
+ },
559
583
  skipMetricsLookup: true
560
584
  }
561
585
  ];
@@ -579,7 +603,7 @@ class CombinedClustersSupplier {
579
603
  });
580
604
  }
581
605
  }
582
- const getCombinedClusterSupplier = (rootConfig, catalogClient, refreshInterval = void 0) => {
606
+ const getCombinedClusterSupplier = (rootConfig, catalogClient, authStrategy, refreshInterval = void 0) => {
583
607
  const clusterSuppliers = rootConfig.getConfigArray("kubernetes.clusterLocatorMethods").map((clusterLocatorMethod) => {
584
608
  const type = clusterLocatorMethod.getString("type");
585
609
  switch (type) {
@@ -588,7 +612,10 @@ const getCombinedClusterSupplier = (rootConfig, catalogClient, refreshInterval =
588
612
  case "localKubectlProxy":
589
613
  return new LocalKubectlProxyClusterLocator();
590
614
  case "config":
591
- return ConfigClusterLocator.fromConfig(clusterLocatorMethod);
615
+ return ConfigClusterLocator.fromConfig(
616
+ clusterLocatorMethod,
617
+ authStrategy
618
+ );
592
619
  case "gke":
593
620
  return GkeClusterLocator.fromConfig(
594
621
  clusterLocatorMethod,
@@ -682,8 +709,6 @@ var __publicField$3 = (obj, key, value) => {
682
709
  __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
683
710
  return value;
684
711
  };
685
- const isRejected = (input) => input.status === "rejected";
686
- const isFulfilled = (input) => input.status === "fulfilled";
687
712
  const DEFAULT_OBJECTS = [
688
713
  {
689
714
  group: "",
@@ -794,20 +819,20 @@ class KubernetesFanOutHandler {
794
819
  serviceLocator,
795
820
  customResources,
796
821
  objectTypesToFetch = DEFAULT_OBJECTS,
797
- authTranslator
822
+ authStrategy
798
823
  }) {
799
824
  __publicField$3(this, "logger");
800
825
  __publicField$3(this, "fetcher");
801
826
  __publicField$3(this, "serviceLocator");
802
827
  __publicField$3(this, "customResources");
803
828
  __publicField$3(this, "objectTypesToFetch");
804
- __publicField$3(this, "authTranslator");
829
+ __publicField$3(this, "authStrategy");
805
830
  this.logger = logger;
806
831
  this.fetcher = fetcher;
807
832
  this.serviceLocator = serviceLocator;
808
833
  this.customResources = customResources;
809
834
  this.objectTypesToFetch = new Set(objectTypesToFetch);
810
- this.authTranslator = authTranslator;
835
+ this.authStrategy = authStrategy;
811
836
  }
812
837
  async getCustomResourcesByEntity({
813
838
  entity,
@@ -830,29 +855,39 @@ class KubernetesFanOutHandler {
830
855
  async fanOutRequests(entity, auth, objectTypesToFetch, customResources) {
831
856
  var _a, _b, _c, _d, _e, _f, _g;
832
857
  const entityName = ((_b = (_a = entity.metadata) == null ? void 0 : _a.annotations) == null ? void 0 : _b["backstage.io/kubernetes-id"]) || ((_c = entity.metadata) == null ? void 0 : _c.name);
833
- const clusterDetailsDecoratedForAuth = await this.decorateClusterDetailsWithAuth(entity, auth, {
858
+ const { clusters } = await this.serviceLocator.getClustersByEntity(entity, {
834
859
  objectTypesToFetch,
835
860
  customResources: customResources != null ? customResources : []
836
861
  });
837
862
  this.logger.info(
838
- `entity.metadata.name=${entityName} clusterDetails=[${clusterDetailsDecoratedForAuth.map((c) => c.name).join(", ")}]`
863
+ `entity.metadata.name=${entityName} clusterDetails=[${clusters.map((c) => c.name).join(", ")}]`
839
864
  );
840
865
  const labelSelector = ((_e = (_d = entity.metadata) == null ? void 0 : _d.annotations) == null ? void 0 : _e["backstage.io/kubernetes-label-selector"]) || `backstage.io/kubernetes-id=${entityName}`;
841
866
  const namespace = (_g = (_f = entity.metadata) == null ? void 0 : _f.annotations) == null ? void 0 : _g["backstage.io/kubernetes-namespace"];
842
867
  return Promise.all(
843
- clusterDetailsDecoratedForAuth.map((clusterDetailsItem) => {
868
+ clusters.map(async (clusterDetails) => {
869
+ const credential = await this.authStrategy.getCredential(
870
+ clusterDetails,
871
+ auth
872
+ );
844
873
  return this.fetcher.fetchObjectsForService({
845
874
  serviceId: entityName,
846
- clusterDetails: clusterDetailsItem,
875
+ clusterDetails,
876
+ credential,
847
877
  objectTypesToFetch,
848
878
  labelSelector,
849
- customResources: (customResources || clusterDetailsItem.customResources || this.customResources).map((c) => ({
879
+ customResources: (customResources || clusterDetails.customResources || this.customResources).map((c) => ({
850
880
  ...c,
851
881
  objectType: "customresources"
852
882
  })),
853
883
  namespace
854
884
  }).then(
855
- (result) => this.getMetricsForPods(clusterDetailsItem, labelSelector, result)
885
+ (result) => this.getMetricsForPods(
886
+ clusterDetails,
887
+ credential,
888
+ labelSelector,
889
+ result
890
+ )
856
891
  ).catch(
857
892
  (e) => e.name === "FetchError" ? Promise.resolve([
858
893
  {
@@ -863,22 +898,10 @@ class KubernetesFanOutHandler {
863
898
  },
864
899
  []
865
900
  ]) : Promise.reject(e)
866
- ).then((r) => this.toClusterObjects(clusterDetailsItem, r));
901
+ ).then((r) => this.toClusterObjects(clusterDetails, r));
867
902
  })
868
903
  ).then(this.toObjectsByEntityResponse);
869
904
  }
870
- async decorateClusterDetailsWithAuth(entity, auth, requestContext) {
871
- const clusterDetails = (await this.serviceLocator.getClustersByEntity(entity, requestContext)).clusters;
872
- const promiseResults = await Promise.allSettled(
873
- clusterDetails.map((cd) => {
874
- return this.authTranslator.decorateClusterDetailsWithAuth(cd, auth);
875
- })
876
- );
877
- promiseResults.filter(isRejected).map((item) => {
878
- this.logger.info(`Failed to decorate cluster details: ${item.reason}`);
879
- });
880
- return promiseResults.filter(isFulfilled).map((item) => item.value);
881
- }
882
905
  toObjectsByEntityResponse(clusterObjects) {
883
906
  return {
884
907
  items: clusterObjects.filter(
@@ -909,7 +932,7 @@ class KubernetesFanOutHandler {
909
932
  }
910
933
  return objects;
911
934
  }
912
- async getMetricsForPods(clusterDetails, labelSelector, result) {
935
+ async getMetricsForPods(clusterDetails, credential, labelSelector, result) {
913
936
  if (clusterDetails.skipMetricsLookup) {
914
937
  return [result, []];
915
938
  }
@@ -924,6 +947,7 @@ class KubernetesFanOutHandler {
924
947
  }
925
948
  const podMetrics = await this.fetcher.fetchPodMetricsByNamespaces(
926
949
  clusterDetails,
950
+ credential,
927
951
  namespaces,
928
952
  labelSelector
929
953
  );
@@ -972,6 +996,7 @@ class KubernetesClientBasedFetcher {
972
996
  const fetchResults = Array.from(params.objectTypesToFetch).concat(params.customResources).map(
973
997
  ({ objectType, group, apiVersion, plural }) => this.fetchResource(
974
998
  params.clusterDetails,
999
+ params.credential,
975
1000
  group,
976
1001
  apiVersion,
977
1002
  plural,
@@ -991,18 +1016,27 @@ class KubernetesClientBasedFetcher {
991
1016
  );
992
1017
  return Promise.all(fetchResults).then(fetchResultsToResponseWrapper);
993
1018
  }
994
- fetchPodMetricsByNamespaces(clusterDetails, namespaces, labelSelector) {
1019
+ fetchPodMetricsByNamespaces(clusterDetails, credential, namespaces, labelSelector) {
995
1020
  const fetchResults = Array.from(namespaces).map(async (ns) => {
996
1021
  const [podMetrics, podList] = await Promise.all([
997
1022
  this.fetchResource(
998
1023
  clusterDetails,
1024
+ credential,
999
1025
  "metrics.k8s.io",
1000
1026
  "v1beta1",
1001
1027
  "pods",
1002
1028
  ns,
1003
1029
  labelSelector
1004
1030
  ),
1005
- this.fetchResource(clusterDetails, "", "v1", "pods", ns, labelSelector)
1031
+ this.fetchResource(
1032
+ clusterDetails,
1033
+ credential,
1034
+ "",
1035
+ "v1",
1036
+ "pods",
1037
+ ns,
1038
+ labelSelector
1039
+ )
1006
1040
  ]);
1007
1041
  if (podMetrics.ok && podList.ok) {
1008
1042
  return clientNode.topPods(
@@ -1036,7 +1070,7 @@ class KubernetesClientBasedFetcher {
1036
1070
  resourcePath
1037
1071
  };
1038
1072
  }
1039
- fetchResource(clusterDetails, group, apiVersion, plural, namespace, labelSelector) {
1073
+ fetchResource(clusterDetails, credential, group, apiVersion, plural, namespace, labelSelector) {
1040
1074
  const encode = (s) => encodeURIComponent(s);
1041
1075
  let resourcePath = group ? `/apis/${encode(group)}/${encode(apiVersion)}` : `/api/${encode(apiVersion)}`;
1042
1076
  if (namespace) {
@@ -1045,10 +1079,11 @@ class KubernetesClientBasedFetcher {
1045
1079
  resourcePath += `/${encode(plural)}`;
1046
1080
  let url;
1047
1081
  let requestInit;
1048
- if (clusterDetails.serviceAccountToken || clusterDetails.authProvider === "localKubectlProxy") {
1049
- [url, requestInit] = this.fetchArgsFromClusterDetails(clusterDetails);
1050
- } else if (fs__default["default"].pathExistsSync(clientNode.Config.SERVICEACCOUNT_TOKEN_PATH)) {
1051
- [url, requestInit] = this.fetchArgsInCluster();
1082
+ const authProvider = clusterDetails.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER];
1083
+ if (authProvider === "serviceAccount" && !clusterDetails.authMetadata.serviceAccountToken && fs__default["default"].pathExistsSync(clientNode.Config.SERVICEACCOUNT_CA_PATH)) {
1084
+ [url, requestInit] = this.fetchArgsInCluster(credential);
1085
+ } else if (credential.type === "bearer token" || authProvider === "localKubectlProxy") {
1086
+ [url, requestInit] = this.fetchArgs(clusterDetails, credential);
1052
1087
  } else {
1053
1088
  return Promise.reject(
1054
1089
  new Error(
@@ -1066,14 +1101,16 @@ class KubernetesClientBasedFetcher {
1066
1101
  }
1067
1102
  return fetch__default["default"](url, requestInit);
1068
1103
  }
1069
- fetchArgsFromClusterDetails(clusterDetails) {
1104
+ fetchArgs(clusterDetails, credential) {
1070
1105
  var _a;
1071
1106
  const requestInit = {
1072
1107
  method: "GET",
1073
1108
  headers: {
1074
1109
  Accept: "application/json",
1075
1110
  "Content-Type": "application/json",
1076
- Authorization: `Bearer ${clusterDetails.serviceAccountToken}`
1111
+ ...credential.type === "bearer token" && {
1112
+ Authorization: `Bearer ${credential.token}`
1113
+ }
1077
1114
  }
1078
1115
  };
1079
1116
  const url = new URL(clusterDetails.url);
@@ -1088,20 +1125,20 @@ class KubernetesClientBasedFetcher {
1088
1125
  }
1089
1126
  return [url, requestInit];
1090
1127
  }
1091
- fetchArgsInCluster() {
1092
- const kc = new clientNode.KubeConfig();
1093
- kc.loadFromCluster();
1094
- const cluster = kc.getCurrentCluster();
1095
- const user = kc.getCurrentUser();
1096
- const token = fs__default["default"].readFileSync(user.authProvider.config.tokenFile);
1128
+ fetchArgsInCluster(credential) {
1097
1129
  const requestInit = {
1098
1130
  method: "GET",
1099
1131
  headers: {
1100
1132
  Accept: "application/json",
1101
1133
  "Content-Type": "application/json",
1102
- Authorization: `Bearer ${token}`
1134
+ ...credential.type === "bearer token" && {
1135
+ Authorization: `Bearer ${credential.token}`
1136
+ }
1103
1137
  }
1104
1138
  };
1139
+ const kc = new clientNode.KubeConfig();
1140
+ kc.loadFromCluster();
1141
+ const cluster = kc.getCurrentCluster();
1105
1142
  const url = new URL(cluster.server);
1106
1143
  if (url.protocol === "https:") {
1107
1144
  requestInit.agent = new https__namespace.Agent({
@@ -1125,14 +1162,15 @@ class KubernetesProxy {
1125
1162
  __publicField$1(this, "middlewareForClusterName", /* @__PURE__ */ new Map());
1126
1163
  __publicField$1(this, "logger");
1127
1164
  __publicField$1(this, "clusterSupplier");
1128
- __publicField$1(this, "authTranslator");
1165
+ __publicField$1(this, "authStrategy");
1129
1166
  this.logger = options.logger;
1130
1167
  this.clusterSupplier = options.clusterSupplier;
1131
- this.authTranslator = options.authTranslator;
1168
+ this.authStrategy = options.authStrategy;
1132
1169
  }
1133
1170
  createRequestHandler(options) {
1134
1171
  const { permissionApi } = options;
1135
1172
  return async (req, res, next) => {
1173
+ var _a, _b;
1136
1174
  const authorizeResponse = await permissionApi.authorize(
1137
1175
  [{ permission: pluginKubernetesCommon.kubernetesProxyPermission }],
1138
1176
  {
@@ -1150,17 +1188,19 @@ class KubernetesProxy {
1150
1188
  if (authHeader) {
1151
1189
  req.headers.authorization = authHeader;
1152
1190
  } else {
1153
- const { serviceAccountToken } = await this.getClusterForRequest(
1154
- req
1155
- ).then(
1156
- (cd) => this.authTranslator.decorateClusterDetailsWithAuth(cd, {})
1157
- );
1158
- if (serviceAccountToken) {
1159
- req.headers.authorization = `Bearer ${serviceAccountToken}`;
1191
+ const credential = await this.getClusterForRequest(req).then((cd) => {
1192
+ return this.authStrategy.getCredential(cd, {});
1193
+ });
1194
+ if (credential.type === "bearer token") {
1195
+ req.headers.authorization = `Bearer ${credential.token}`;
1160
1196
  }
1161
1197
  }
1162
1198
  const middleware = await this.getMiddleware(req);
1163
- middleware(req, res, next);
1199
+ if (((_a = req.header("connection")) == null ? void 0 : _a.toLowerCase()) === "upgrade" && ((_b = req.header("upgrade")) == null ? void 0 : _b.toLowerCase()) === "websocket") {
1200
+ middleware.upgrade(req, req.socket, void 0);
1201
+ } else {
1202
+ middleware(req, res, next);
1203
+ }
1164
1204
  };
1165
1205
  }
1166
1206
  // We create one middleware per remote cluster and hold on to them, because
@@ -1192,7 +1232,10 @@ class KubernetesProxy {
1192
1232
  protocol: url.protocol,
1193
1233
  host: url.hostname,
1194
1234
  port: url.port,
1195
- ca: (_a = clientNode.bufferFromFileOrString("", cluster.caData)) == null ? void 0 : _a.toString()
1235
+ ca: (_a = clientNode.bufferFromFileOrString(
1236
+ cluster.caFile,
1237
+ cluster.caData
1238
+ )) == null ? void 0 : _a.toString()
1196
1239
  };
1197
1240
  },
1198
1241
  onError: (error, req, res) => {
@@ -1252,7 +1295,7 @@ class KubernetesBuilder {
1252
1295
  __publicField(this, "fetcher");
1253
1296
  __publicField(this, "serviceLocator");
1254
1297
  __publicField(this, "proxy");
1255
- __publicField(this, "authTranslatorMap");
1298
+ __publicField(this, "authStrategyMap");
1256
1299
  }
1257
1300
  static createBuilder(env) {
1258
1301
  return new KubernetesBuilder(env);
@@ -1276,7 +1319,7 @@ class KubernetesBuilder {
1276
1319
  const customResources = this.buildCustomResources();
1277
1320
  const fetcher = this.getFetcher();
1278
1321
  const clusterSupplier = this.getClusterSupplier();
1279
- const authTranslatorMap = this.getAuthTranslatorMap();
1322
+ const authStrategyMap = this.getAuthStrategyMap();
1280
1323
  const proxy = this.getProxy(logger, clusterSupplier);
1281
1324
  const serviceLocator = this.getServiceLocator();
1282
1325
  const objectsProvider = this.getObjectsProvider({
@@ -1302,7 +1345,7 @@ class KubernetesBuilder {
1302
1345
  objectsProvider,
1303
1346
  router,
1304
1347
  serviceLocator,
1305
- authTranslatorMap
1348
+ authStrategyMap
1306
1349
  };
1307
1350
  }
1308
1351
  setClusterSupplier(clusterSupplier) {
@@ -1329,8 +1372,12 @@ class KubernetesBuilder {
1329
1372
  this.proxy = proxy;
1330
1373
  return this;
1331
1374
  }
1332
- setAuthTranslatorMap(authTranslatorMap) {
1333
- this.authTranslatorMap = authTranslatorMap;
1375
+ setAuthStrategyMap(authStrategyMap) {
1376
+ this.authStrategyMap = authStrategyMap;
1377
+ }
1378
+ addAuthStrategy(key, strategy) {
1379
+ this.getAuthStrategyMap()[key] = strategy;
1380
+ return this;
1334
1381
  }
1335
1382
  buildCustomResources() {
1336
1383
  var _a;
@@ -1352,16 +1399,17 @@ class KubernetesBuilder {
1352
1399
  this.clusterSupplier = getCombinedClusterSupplier(
1353
1400
  config,
1354
1401
  this.env.catalogApi,
1402
+ new DispatchStrategy({ authStrategyMap: this.getAuthStrategyMap() }),
1355
1403
  refreshInterval
1356
1404
  );
1357
1405
  return this.clusterSupplier;
1358
1406
  }
1359
1407
  buildObjectsProvider(options) {
1360
- const authTranslatorMap = this.getAuthTranslatorMap();
1408
+ const authStrategyMap = this.getAuthStrategyMap();
1361
1409
  this.objectsProvider = new KubernetesFanOutHandler({
1362
1410
  ...options,
1363
- authTranslator: new DispatchingKubernetesAuthTranslator({
1364
- authTranslatorMap
1411
+ authStrategy: new DispatchStrategy({
1412
+ authStrategyMap
1365
1413
  })
1366
1414
  });
1367
1415
  return this.objectsProvider;
@@ -1394,14 +1442,14 @@ class KubernetesBuilder {
1394
1442
  throw new Error("not implemented");
1395
1443
  }
1396
1444
  buildProxy(logger, clusterSupplier) {
1397
- const authTranslatorMap = this.getAuthTranslatorMap();
1398
- const authTranslator = new DispatchingKubernetesAuthTranslator({
1399
- authTranslatorMap
1445
+ const authStrategyMap = this.getAuthStrategyMap();
1446
+ const authStrategy = new DispatchStrategy({
1447
+ authStrategyMap
1400
1448
  });
1401
1449
  this.proxy = new KubernetesProxy({
1402
1450
  logger,
1403
1451
  clusterSupplier,
1404
- authTranslator
1452
+ authStrategy
1405
1453
  });
1406
1454
  return this.proxy;
1407
1455
  }
@@ -1434,29 +1482,32 @@ class KubernetesBuilder {
1434
1482
  router.get("/clusters", async (_, res) => {
1435
1483
  const clusterDetails = await this.fetchClusterDetails(clusterSupplier);
1436
1484
  res.json({
1437
- items: clusterDetails.map((cd) => ({
1438
- name: cd.name,
1439
- dashboardUrl: cd.dashboardUrl,
1440
- authProvider: cd.authProvider,
1441
- oidcTokenProvider: cd.oidcTokenProvider
1442
- }))
1485
+ items: clusterDetails.map((cd) => {
1486
+ const oidcTokenProvider = cd.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];
1487
+ return {
1488
+ name: cd.name,
1489
+ dashboardUrl: cd.dashboardUrl,
1490
+ authProvider: cd.authMetadata[pluginKubernetesCommon.ANNOTATION_KUBERNETES_AUTH_PROVIDER],
1491
+ ...oidcTokenProvider && { oidcTokenProvider }
1492
+ };
1493
+ })
1443
1494
  });
1444
1495
  });
1445
1496
  addResourceRoutesToRouter(router, catalogApi, objectsProvider);
1446
1497
  return router;
1447
1498
  }
1448
- buildAuthTranslatorMap() {
1449
- this.authTranslatorMap = {
1450
- google: new GoogleKubernetesAuthTranslator(),
1451
- aks: new AksKubernetesAuthTranslator(),
1452
- aws: new AwsIamKubernetesAuthTranslator({ config: this.env.config }),
1453
- azure: new AzureIdentityKubernetesAuthTranslator(this.env.logger),
1454
- serviceAccount: new NoopKubernetesAuthTranslator(),
1455
- googleServiceAccount: new GoogleServiceAccountAuthTranslator(),
1456
- oidc: new OidcKubernetesAuthTranslator(),
1457
- localKubectlProxy: new NoopKubernetesAuthTranslator()
1499
+ buildAuthStrategyMap() {
1500
+ this.authStrategyMap = {
1501
+ aks: new AksStrategy(),
1502
+ aws: new AwsIamStrategy({ config: this.env.config }),
1503
+ azure: new AzureIdentityStrategy(this.env.logger),
1504
+ google: new GoogleStrategy(),
1505
+ googleServiceAccount: new GoogleServiceAccountStrategy(),
1506
+ localKubectlProxy: new AnonymousStrategy(),
1507
+ oidc: new OidcStrategy(),
1508
+ serviceAccount: new ServiceAccountStrategy()
1458
1509
  };
1459
- return this.authTranslatorMap;
1510
+ return this.authStrategyMap;
1460
1511
  }
1461
1512
  async fetchClusterDetails(clusterSupplier) {
1462
1513
  const clusterDetails = await clusterSupplier.getClusters();
@@ -1516,9 +1567,9 @@ class KubernetesBuilder {
1516
1567
  var _a;
1517
1568
  return (_a = this.proxy) != null ? _a : this.buildProxy(logger, clusterSupplier);
1518
1569
  }
1519
- getAuthTranslatorMap() {
1570
+ getAuthStrategyMap() {
1520
1571
  var _a;
1521
- return (_a = this.authTranslatorMap) != null ? _a : this.buildAuthTranslatorMap();
1572
+ return (_a = this.authStrategyMap) != null ? _a : this.buildAuthStrategyMap();
1522
1573
  }
1523
1574
  }
1524
1575
 
@@ -1527,18 +1578,19 @@ async function createRouter(options) {
1527
1578
  return router;
1528
1579
  }
1529
1580
 
1530
- exports.AksKubernetesAuthTranslator = AksKubernetesAuthTranslator;
1531
- exports.AwsIamKubernetesAuthTranslator = AwsIamKubernetesAuthTranslator;
1532
- exports.AzureIdentityKubernetesAuthTranslator = AzureIdentityKubernetesAuthTranslator;
1581
+ exports.AksStrategy = AksStrategy;
1582
+ exports.AnonymousStrategy = AnonymousStrategy;
1583
+ exports.AwsIamStrategy = AwsIamStrategy;
1584
+ exports.AzureIdentityStrategy = AzureIdentityStrategy;
1533
1585
  exports.DEFAULT_OBJECTS = DEFAULT_OBJECTS;
1534
- exports.DispatchingKubernetesAuthTranslator = DispatchingKubernetesAuthTranslator;
1535
- exports.GoogleKubernetesAuthTranslator = GoogleKubernetesAuthTranslator;
1536
- exports.GoogleServiceAccountAuthTranslator = GoogleServiceAccountAuthTranslator;
1586
+ exports.DispatchStrategy = DispatchStrategy;
1587
+ exports.GoogleServiceAccountStrategy = GoogleServiceAccountStrategy;
1588
+ exports.GoogleStrategy = GoogleStrategy;
1537
1589
  exports.HEADER_KUBERNETES_AUTH = HEADER_KUBERNETES_AUTH;
1538
1590
  exports.HEADER_KUBERNETES_CLUSTER = HEADER_KUBERNETES_CLUSTER;
1539
1591
  exports.KubernetesBuilder = KubernetesBuilder;
1540
1592
  exports.KubernetesProxy = KubernetesProxy;
1541
- exports.NoopKubernetesAuthTranslator = NoopKubernetesAuthTranslator;
1542
- exports.OidcKubernetesAuthTranslator = OidcKubernetesAuthTranslator;
1593
+ exports.OidcStrategy = OidcStrategy;
1594
+ exports.ServiceAccountStrategy = ServiceAccountStrategy;
1543
1595
  exports.createRouter = createRouter;
1544
1596
  //# sourceMappingURL=index.cjs.js.map