@jfvilas/plugin-kwirth-backend 0.12.8 → 0.13.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/README.md CHANGED
@@ -11,13 +11,16 @@ This [Backstage]((https://backstage.io)) backend plugin is primarily responsible
11
11
  - Performing login processes to remote Kwirth instances, and thus obtaining valid API keys for users to stream kubernetes data.
12
12
  - Receiving and answering API calls from configured frontend Kwirth plugins on your Backstage instance.
13
13
 
14
+
14
15
  ## Version compatibility
15
16
  Following table shows version compatibility between Kwirth Backstage plugin and Kwirth Core server.
16
17
 
17
18
  | Plugin Kwirth version | Kwirth version |
18
19
  |-|-|
19
- |0.12.6|0.4.20|
20
+ |0.13.0|0.4.131|
20
21
  |0.12.8|0.4.45|
22
+ |0.12.6|0.4.20|
23
+
21
24
 
22
25
  ## Install plugin
23
26
  Here we show how to get this backend plugin up and running quickly. First we need to add the `@jfvilas/plugin-kwirth-backend` package to your Backstage project:
@@ -42,6 +45,7 @@ Next, you need to modify your backend index file for starting Kwirth backend plu
42
45
  backend.start();
43
46
  ```
44
47
 
48
+
45
49
  ## Configure
46
50
  To have your Kwirth backend plugins ready for work you must perform some previous additional tasks, like deploying Kwirth, creating API Keys, defining clusters, etc... In this section we cover all these needs in a structured way.
47
51
 
package/dist/index.cjs.js CHANGED
@@ -14,20 +14,20 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
14
14
  var express__default = /*#__PURE__*/_interopDefaultCompat(express);
15
15
  var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
16
16
 
17
- const VERSION = "0.0.1";
18
- const MIN_KWIRTH_VERSION = "0.4.11";
17
+ const MIN_KWIRTH_VERSION = "0.4.63";
19
18
  class KwirthStaticData {
20
19
  static clusterKwirthData = /* @__PURE__ */ new Map();
20
+ static latestVersions;
21
21
  }
22
22
 
23
23
  const loadNamespacePermissions = (channelConfig, logger) => {
24
- var namespacePermissions = [];
24
+ let namespacePermissions = [];
25
25
  if (channelConfig.has("namespacePermissions")) {
26
26
  logger.info(` Namespace permisson evaluation will be performed.`);
27
- var permNamespaces = channelConfig.getOptionalConfigArray("namespacePermissions");
28
- for (var ns of permNamespaces) {
29
- var namespace = ns.keys()[0];
30
- var identityRefs = ns.getStringArray(namespace);
27
+ let permNamespaces = channelConfig.getOptionalConfigArray("namespacePermissions");
28
+ for (let ns of permNamespaces) {
29
+ let namespace = ns.keys()[0];
30
+ let identityRefs = ns.getStringArray(namespace);
31
31
  identityRefs = identityRefs.map((g) => g.toLowerCase());
32
32
  namespacePermissions.push({ namespace, identityRefs });
33
33
  }
@@ -38,19 +38,19 @@ const loadNamespacePermissions = (channelConfig, logger) => {
38
38
  return namespacePermissions;
39
39
  };
40
40
  const loadPodRules = (config, category) => {
41
- var rules = [];
42
- for (var rule of config.getConfigArray(category)) {
43
- var podsStringArray = rule.getOptionalStringArray("pods") || [".*"];
44
- var podsRegexArray = [];
45
- for (var expr of podsStringArray) {
41
+ let rules = [];
42
+ for (let rule of config.getConfigArray(category)) {
43
+ let podsStringArray = rule.getOptionalStringArray("pods") || [".*"];
44
+ let podsRegexArray = [];
45
+ for (let expr of podsStringArray) {
46
46
  podsRegexArray.push(new RegExp(expr));
47
47
  }
48
- var refsStringArray = rule.getOptionalStringArray("refs") || [".*"];
49
- var refsRegexArray = [];
50
- for (var expr of refsStringArray) {
48
+ let refsStringArray = rule.getOptionalStringArray("refs") || [".*"];
49
+ let refsRegexArray = [];
50
+ for (let expr of refsStringArray) {
51
51
  refsRegexArray.push(new RegExp(expr));
52
52
  }
53
- var prr = {
53
+ let prr = {
54
54
  pods: podsRegexArray,
55
55
  refs: refsRegexArray
56
56
  };
@@ -59,13 +59,13 @@ const loadPodRules = (config, category) => {
59
59
  return rules;
60
60
  };
61
61
  const loadPodPermissions = (channelConfig, logger) => {
62
- var clusterPodPermissions = [];
62
+ let clusterPodPermissions = [];
63
63
  if (channelConfig.has("podPermissions")) {
64
64
  logger.info(` Pod permisson evaluation will be performed.`);
65
- var namespaceList = channelConfig.getConfigArray("podPermissions");
66
- for (var ns of namespaceList) {
67
- var namespaceName = ns.keys()[0];
68
- var podPermissions = { namespace: namespaceName };
65
+ let namespaceList = channelConfig.getConfigArray("podPermissions");
66
+ for (let ns of namespaceList) {
67
+ let namespaceName = ns.keys()[0];
68
+ let podPermissions = { namespace: namespaceName };
69
69
  if (ns.getConfig(namespaceName).has("allow")) {
70
70
  podPermissions.allow = loadPodRules(ns.getConfig(namespaceName), "allow");
71
71
  if (ns.getConfig(namespaceName).has("except")) podPermissions.except = loadPodRules(ns.getConfig(namespaceName), "except");
@@ -91,7 +91,7 @@ const addChannelPermissions = (channel, logger, cluster, kdata) => {
91
91
  if (cluster.has(keyCamelName)) keyname = keyCamelName;
92
92
  if (cluster.has(keyname)) {
93
93
  logger.info(`Load permissions for channel ${channel} (config: ${keyname}).`);
94
- var configChannel = cluster.getConfig(keyname);
94
+ let configChannel = cluster.getConfig(keyname);
95
95
  if (configChannel.has("namespacePermissions")) {
96
96
  logger.info(` Loading namespace permissions.`);
97
97
  kdata.namespacePermissions.set(channel, loadNamespacePermissions(configChannel, logger));
@@ -114,16 +114,16 @@ const addChannelPermissions = (channel, logger, cluster, kdata) => {
114
114
  };
115
115
  const loadClusters = async (logger, config) => {
116
116
  KwirthStaticData.clusterKwirthData.clear();
117
- var locatingMethods = config.getConfigArray("kubernetes.clusterLocatorMethods");
118
- for (var method of locatingMethods) {
119
- var clusters = method.getConfigArray("clusters");
120
- for (var cluster of clusters) {
121
- var name = cluster.getString("name");
117
+ let locatingMethods = config.getConfigArray("kubernetes.clusterLocatorMethods");
118
+ for (let method of locatingMethods) {
119
+ let clusters = method.getConfigArray("clusters");
120
+ for (let cluster of clusters) {
121
+ let name = cluster.getString("name");
122
122
  if (cluster.has("kwirthHome") && cluster.has("kwirthApiKey")) {
123
- var kwirthHome = cluster.getOptionalString("kwirthHome");
124
- var kwirthApiKey = cluster.getOptionalString("kwirthApiKey");
125
- var title = cluster.has("title") ? cluster.getString("title") : "No name";
126
- var kwirthClusterData = {
123
+ let kwirthHome = cluster.getOptionalString("kwirthHome");
124
+ let kwirthApiKey = cluster.getOptionalString("kwirthApiKey");
125
+ let title = cluster.has("title") ? cluster.getString("title") : "No name";
126
+ let kwirthClusterData = {
127
127
  name,
128
128
  kwirthHome,
129
129
  kwirthApiKey,
@@ -146,11 +146,11 @@ const loadClusters = async (logger, config) => {
146
146
  logger.info(`Kwirth for ${name} is located at ${kwirthClusterData.kwirthHome}. Testing connection...`);
147
147
  let enableCluster = false;
148
148
  try {
149
- var response = await fetch(kwirthClusterData.kwirthHome + "/config/info");
149
+ let response = await fetch(kwirthClusterData.kwirthHome + "/config/info");
150
150
  try {
151
- var data = await response.text();
151
+ let data = await response.text();
152
152
  try {
153
- var kwirthData = JSON.parse(data);
153
+ let kwirthData = JSON.parse(data);
154
154
  logger.info(`Kwirth info at cluster '${kwirthClusterData.name}': ${JSON.stringify(kwirthData)}`);
155
155
  kwirthClusterData.kwirthData = kwirthData;
156
156
  if (kwirthCommon.versionGreatOrEqualThan(kwirthData.version, MIN_KWIRTH_VERSION)) {
@@ -197,13 +197,32 @@ const loadClusters = async (logger, config) => {
197
197
  }
198
198
  }
199
199
  logger.info("Kwirth static data has been set including following clusters:");
200
- for (var c of KwirthStaticData.clusterKwirthData.keys()) {
200
+ for (let c of KwirthStaticData.clusterKwirthData.keys()) {
201
201
  logger.info(" " + c);
202
202
  }
203
- for (var c of KwirthStaticData.clusterKwirthData.keys()) {
203
+ for (let c of KwirthStaticData.clusterKwirthData.keys()) {
204
204
  console.log(KwirthStaticData.clusterKwirthData.get(c));
205
205
  }
206
206
  };
207
+ const loadKwirthInfo = async (logger) => {
208
+ try {
209
+ let pkb = await (await fetch("https://registry.npmjs.org/@jfvilas/plugin-kwirth-backend")).json();
210
+ let pkl = await (await fetch("https://registry.npmjs.org/@jfvilas/plugin-kwirth-log")).json();
211
+ let pkm = await (await fetch("https://registry.npmjs.org/@jfvilas/plugin-kwirth-metrics")).json();
212
+ let hubResp = await (await fetch("https://hub.docker.com/v2/repositories/jfvilasoutlook/kwirth/tags?page_size=25&page=1&ordering=last_updated&name=")).json();
213
+ KwirthStaticData.latestVersions = {
214
+ ["plugin-kwirth-backend"]: pkb["dist-tags"].latest,
215
+ ["plugin-kwirth-log"]: pkl["dist-tags"].latest,
216
+ ["plugin-kwirth-metrics"]: pkm["dist-tags"].latest,
217
+ kwirth: hubResp.results[0].name
218
+ };
219
+ logger.info("Latest Kwirth-related artifacts versions are:");
220
+ logger.info(JSON.stringify(KwirthStaticData.latestVersions));
221
+ } catch (err) {
222
+ console.log(err);
223
+ logger.warn("Oops! We couldn't fetch the latest version info. Don't worry \u2014 it's not critical. Kwirth's still here for you.");
224
+ }
225
+ };
207
226
 
208
227
  const checkNamespaceAccess = (channel, cluster, podData, userEntityRef, userGroups) => {
209
228
  let allowedToNamespace = false;
@@ -305,6 +324,8 @@ const checkPodAccess = (reqPod, podPermissionSet, entityName, userEntityRef, use
305
324
  return false;
306
325
  };
307
326
 
327
+ const VERSION = "0.12.9";
328
+
308
329
  async function createRouter(options) {
309
330
  const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;
310
331
  loggerSvc.info("Loading static config");
@@ -314,6 +335,7 @@ async function createRouter(options) {
314
335
  }
315
336
  try {
316
337
  loadClusters(loggerSvc, configSvc);
338
+ loadKwirthInfo(loggerSvc);
317
339
  } catch (err) {
318
340
  let txt = `Errors detected reading static configuration: ${err}`;
319
341
  loggerSvc.error(txt);
@@ -356,17 +378,17 @@ async function createRouter(options) {
356
378
  if (entity.metadata.annotations["backstage.io/kubernetes-id"]) {
357
379
  queryUrl = url + `/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entity.metadata.annotations["backstage.io/kubernetes-id"]}&type=pod&data=containers`;
358
380
  } else if (entity.metadata.annotations["backstage.io/kubernetes-label-selector"]) {
359
- if (kwirthCommon.versionGreatThan(clusterVersion, "0.4.40")) {
381
+ if (kwirthCommon.versionGreaterThan(clusterVersion, "0.4.40")) {
360
382
  let escapedLabelSelector = encodeURIComponent(entity.metadata.annotations["backstage.io/kubernetes-label-selector"]);
361
383
  queryUrl = url + `/managecluster/find?labelselector=${escapedLabelSelector}&type=pod&data=containers`;
362
384
  } else {
363
385
  loggerSvc.error(`Version ${clusterVersion} from cluster ${clusterName} is not valid for using ${pluginKwirthCommon.ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR}`);
364
- clusterList.push({ name: clusterName, url, title, data: [], accessKeys: /* @__PURE__ */ new Map() });
386
+ clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
365
387
  continue;
366
388
  }
367
389
  } else {
368
390
  loggerSvc.error("Received request without labelid/labelselector");
369
- clusterList.push({ name: clusterName, url, title, data: [], accessKeys: /* @__PURE__ */ new Map() });
391
+ clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
370
392
  continue;
371
393
  }
372
394
  try {
@@ -378,23 +400,23 @@ async function createRouter(options) {
378
400
  name: clusterName,
379
401
  url,
380
402
  title,
381
- data: jsonResp,
403
+ pods: jsonResp,
382
404
  accessKeys: /* @__PURE__ */ new Map()
383
405
  };
384
406
  clusterList.push(podData);
385
407
  } else {
386
408
  loggerSvc.warn(`Invalid data received from cluster ${clusterName}`);
387
- clusterList.push({ name: clusterName, url, title, data: [], accessKeys: /* @__PURE__ */ new Map() });
409
+ clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
388
410
  }
389
411
  } else {
390
412
  loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`);
391
413
  let text = await fetchResp.text();
392
414
  if (text) loggerSvc.warn(text);
393
- clusterList.push({ name: clusterName, url, title, data: [], accessKeys: /* @__PURE__ */ new Map() });
415
+ clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
394
416
  }
395
417
  } catch (err) {
396
418
  loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`);
397
- clusterList.push({ name: clusterName, url, title, data: [], accessKeys: /* @__PURE__ */ new Map() });
419
+ clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
398
420
  }
399
421
  }
400
422
  return clusterList;
@@ -435,7 +457,7 @@ async function createRouter(options) {
435
457
  loggerSvc.warn(`Cluster ${foundCluster.name} does not implement channel ${channel} (requested scope: ${reqScope})`);
436
458
  continue;
437
459
  }
438
- for (let podData of foundCluster.data) {
460
+ for (let podData of foundCluster.pods) {
439
461
  let allowedToNamespace = checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups);
440
462
  if (allowedToNamespace) {
441
463
  let clusterDef = KwirthStaticData.clusterKwirthData.get(foundCluster.name);
@@ -475,6 +497,11 @@ async function createRouter(options) {
475
497
  const processVersion = async (_req, res) => {
476
498
  res.status(200).send({ version: VERSION });
477
499
  };
500
+ const processInfo = async (_req, res) => {
501
+ res.status(200).send(
502
+ KwirthStaticData.latestVersions
503
+ );
504
+ };
478
505
  const processAccess = async (req, res) => {
479
506
  if (!req.query["scopes"] || !req.query["channel"]) {
480
507
  res.status(400).send(`'scopes' and 'channel' are required`);
@@ -518,6 +545,9 @@ async function createRouter(options) {
518
545
  router.get(["/version"], (req, res) => {
519
546
  processVersion(req, res);
520
547
  });
548
+ router.get(["/info"], (req, res) => {
549
+ processInfo(req, res);
550
+ });
521
551
  return router;
522
552
  }
523
553
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/model/KwirthStaticData.ts","../src/service/config.ts","../src/service/permissions.ts","../src/service/router.ts","../src/plugin.ts"],"sourcesContent":["import { KwirthClusterData } from './KwirthClusterData'\r\n\r\n/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nconst VERSION='0.0.1'\r\nconst MIN_KWIRTH_VERSION='0.4.11'\r\n\r\nclass KwirthStaticData {\r\n public static clusterKwirthData : Map<string,KwirthClusterData> = new Map()\r\n}\r\n\r\nexport { KwirthStaticData, VERSION, MIN_KWIRTH_VERSION }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\")\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { LoggerService, RootConfigService } from '@backstage/backend-plugin-api'\r\nimport { KwirthStaticData, MIN_KWIRTH_VERSION } from '../model/KwirthStaticData'\r\nimport { KwirthClusterData, KwirthNamespacePermissions, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { Config } from '@backstage/config'\r\nimport { ClusterTypeEnum, InstanceMessageChannelEnum, KwirthData, versionGreatOrEqualThan } from '@jfvilas/kwirth-common'\r\n\r\n\r\n/**\r\n * loads Namespace Permissions setting from app-config xml\r\n * @param channelConfig name of the Kwirth channel to read ('metrics', 'log'...)\r\n * @param logger Logger service\r\n */\r\nconst loadNamespacePermissions = (channelConfig:Config, logger:LoggerService):KwirthNamespacePermissions[] => {\r\n var namespacePermissions:KwirthNamespacePermissions[] = []\r\n if (channelConfig.has('namespacePermissions')) {\r\n logger.info(` Namespace permisson evaluation will be performed.`)\r\n var permNamespaces= (channelConfig.getOptionalConfigArray('namespacePermissions'))!\r\n for (var ns of permNamespaces) {\r\n var namespace=ns.keys()[0]\r\n var identityRefs=ns.getStringArray(namespace)\r\n identityRefs=identityRefs.map(g => g.toLowerCase())\r\n namespacePermissions.push ({ namespace, identityRefs })\r\n }\r\n }\r\n else {\r\n logger.info(` No namespace restrictions.`)\r\n namespacePermissions=[]\r\n }\r\n return namespacePermissions\r\n}\r\n\r\n/**\r\n * read rules about permissions to a pply to a set of pods\r\n * @param config an object config read from app-config\r\n * @param category a permission category, one of: allow, deny unless, except\r\n * @returns an array of PodPermissionRule's\r\n */\r\nconst loadPodRules = (config:Config, category:string):PodPermissionRule[] => {\r\n var rules:PodPermissionRule[]=[]\r\n for (var rule of config.getConfigArray(category)) {\r\n var podsStringArray = rule.getOptionalStringArray('pods') || ['.*']\r\n var podsRegexArray:RegExp[]=[]\r\n for (var expr of podsStringArray) {\r\n podsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n var refsStringArray = rule.getOptionalStringArray('refs') || ['.*']\r\n var refsRegexArray:RegExp[]=[]\r\n for (var expr of refsStringArray) {\r\n refsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n var prr:PodPermissionRule={\r\n pods:podsRegexArray,\r\n refs:refsRegexArray\r\n }\r\n rules.push(prr)\r\n }\r\n return rules\r\n}\r\n\r\n/**\r\n * loads pod permissions (namespace and pod) for a specific Kwirth channel\r\n * @param channelConfig then name of the key (inside app-config) to read config from\r\n * @param logger Logger service\r\n * @returns an array of pod permissions\r\n */\r\nconst loadPodPermissions = (channelConfig:Config, logger:LoggerService):KwirthPodPermissions[] => {\r\n var clusterPodPermissions:KwirthPodPermissions[]=[]\r\n if (channelConfig.has('podPermissions')) {\r\n logger.info(` Pod permisson evaluation will be performed.`)\r\n var namespaceList=channelConfig.getConfigArray('podPermissions')\r\n for (var ns of namespaceList) {\r\n var namespaceName=ns.keys()[0]\r\n var podPermissions:KwirthPodPermissions={ namespace:namespaceName }\r\n\r\n if (ns.getConfig(namespaceName).has('allow')) {\r\n podPermissions.allow=loadPodRules(ns.getConfig(namespaceName), 'allow')\r\n if (ns.getConfig(namespaceName).has('except')) podPermissions.except=loadPodRules(ns.getConfig(namespaceName), 'except')\r\n if (ns.getConfig(namespaceName).has('deny')) podPermissions.deny=loadPodRules(ns.getConfig(namespaceName), 'deny')\r\n if (ns.getConfig(namespaceName).has('unless')) podPermissions.unless=loadPodRules(ns.getConfig(namespaceName), 'unless')\r\n }\r\n else {\r\n podPermissions.allow=[]\r\n podPermissions.allow.push({\r\n pods: [new RegExp('.*')],\r\n refs: [new RegExp('.*')]\r\n })\r\n }\r\n clusterPodPermissions.push(podPermissions)\r\n }\r\n }\r\n else {\r\n logger.info(` No pod permissions will be applied.`)\r\n }\r\n return clusterPodPermissions\r\n}\r\n\r\n/**\r\n * Reads permissions for a kwirth channel (like log, metrics...)\r\n * @param channel name of the channel\r\n * @param logger bs logger service\r\n * @param cluster the app-config object containing the cluster to process\r\n * @param kdata current KwirthClusterData object to add channel permissions\r\n */\r\nconst addChannelPermissions = (channel: string, logger:LoggerService, cluster:Config, kdata:KwirthClusterData) => {\r\n let keyname = 'kwirth' + channel\r\n\r\n let keyCamelName = 'kwirth' + channel[0].toUpperCase() + channel.substring(1)\r\n if (cluster.has(keyCamelName)) keyname = keyCamelName\r\n\r\n if (cluster.has(keyname)) {\r\n logger.info(`Load permissions for channel ${channel} (config: ${keyname}).`)\r\n var configChannel=cluster.getConfig(keyname);\r\n if (configChannel.has('namespacePermissions')) {\r\n logger.info(` Loading namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, loadNamespacePermissions(configChannel, logger))\r\n }\r\n else {\r\n logger.info(` No namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, [])\r\n }\r\n if (configChannel.has('podPermissions')) {\r\n logger.info(` Loading pod permissions.`)\r\n kdata.podPermissions.set(channel, loadPodPermissions(configChannel, logger))\r\n }\r\n else {\r\n logger.info(` No pod permissions.`)\r\n kdata.podPermissions.set(channel, [])\r\n }\r\n }\r\n else {\r\n logger.info(`Cluster ${cluster.getString('name')} will have no channel '${channel}' restrictions.`)\r\n kdata.namespacePermissions.set(channel,[])\r\n kdata.podPermissions.set(channel,[])\r\n }\r\n}\r\n\r\n/**\r\n * reads app-config and builds a list of valid clusters\r\n * @param logger core service for logging\r\n * @param config core service for reading config info\r\n */\r\nconst loadClusters = async (logger:LoggerService, config:RootConfigService) => {\r\n KwirthStaticData.clusterKwirthData.clear()\r\n\r\n var locatingMethods=config.getConfigArray('kubernetes.clusterLocatorMethods')\r\n for (var method of locatingMethods) {\r\n\r\n var clusters=(method.getConfigArray('clusters'))\r\n for (var cluster of clusters) {\r\n\r\n var name=cluster.getString('name')\r\n if (cluster.has('kwirthHome') && cluster.has('kwirthApiKey')) { \r\n var kwirthHome:string = cluster.getOptionalString('kwirthHome')!\r\n var kwirthApiKey:string = cluster.getOptionalString('kwirthApiKey')!\r\n var title:string = (cluster.has('title')?cluster.getString('title'):'No name')\r\n var kwirthClusterData:KwirthClusterData={\r\n name,\r\n kwirthHome,\r\n kwirthApiKey,\r\n kwirthData: {\r\n version: '',\r\n clusterName: '',\r\n inCluster: false,\r\n namespace: '',\r\n deployment: '',\r\n lastVersion: '',\r\n clusterType: ClusterTypeEnum.KUBERNETES,\r\n metricsInterval: 0,\r\n channels: []\r\n },\r\n title,\r\n namespacePermissions: new Map(),\r\n podPermissions: new Map(),\r\n enabled: false\r\n }\r\n\r\n logger.info(`Kwirth for ${name} is located at ${kwirthClusterData.kwirthHome}. Testing connection...`)\r\n let enableCluster = false\r\n try {\r\n /*\r\n /config/info endpoint returns JSON:\r\n {\r\n \"clusterName\": \"inCluster\",\r\n \"namespace\": \"default\",\r\n \"deployment\": \"kwirth\",\r\n \"inCluster\": true,\r\n \"version\": \"0.4.11\",\r\n \"lastVersion\": \"0.4.11\",\r\n \"clusterType\": \"kubernetes\",\r\n \"metricsInterval\": 60,\r\n \"channels\": [\r\n {\r\n \"id\": \"log\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": false,\r\n \"reconnectable\": true,\r\n \"sources\": [\r\n \"docker\",\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"alert\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": false,\r\n \"reconnectable\": true,\r\n \"sources\": [\r\n \"docker\",\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"metrics\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": true,\r\n \"reconnectable\": true,\r\n \"sources\": [\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": true\r\n },\r\n {\r\n \"id\": \"ops\",\r\n \"routable\": true,\r\n \"pauseable\": false,\r\n \"modifyable\": false,\r\n \"reconnectable\": false,\r\n \"sources\": [\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"trivy\",\r\n \"routable\": false,\r\n \"pauseable\": false,\r\n \"modifyable\": false,\r\n \"reconnectable\": false,\r\n \"sources\": [\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"echo\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": false,\r\n \"reconnectable\": true,\r\n \"metrics\": false,\r\n \"sources\": [\r\n \"kubernetes\",\r\n \"docker\"\r\n ]\r\n }\r\n ]\r\n } \r\n */\r\n var response = await fetch (kwirthClusterData.kwirthHome+'/config/info')\r\n try {\r\n var data = await response.text()\r\n try {\r\n var kwirthData = JSON.parse(data) as KwirthData\r\n logger.info(`Kwirth info at cluster '${kwirthClusterData.name}': ${JSON.stringify(kwirthData)}`)\r\n kwirthClusterData.kwirthData=kwirthData\r\n if (versionGreatOrEqualThan(kwirthData.version, MIN_KWIRTH_VERSION)) {\r\n enableCluster = true\r\n }\r\n else {\r\n logger.error(`Unsupported Kwirth version on cluster '${name}' (${title}) [${kwirthData.version}]. Min version is ${MIN_KWIRTH_VERSION}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.error(`Kwirth at cluster ${kwirthClusterData.name} returned errors: ${err}`)\r\n logger.info('Returned data is:')\r\n logger.info(data)\r\n kwirthClusterData.kwirthData = {\r\n version:'0.0.0',\r\n clusterName:'unknown',\r\n inCluster:false,\r\n namespace:'unknown',\r\n deployment:'unknown',\r\n lastVersion:'0.0.0',\r\n clusterType: ClusterTypeEnum.KUBERNETES,\r\n metricsInterval: 0,\r\n channels: []\r\n }\r\n }\r\n }\r\n catch (err) {\r\n logger.warn(`Error parsing version response from cluster '${kwirthClusterData.name}': ${err}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.info(`Kwirth access error: ${err}.`)\r\n logger.warn(`Kwirth home URL (${kwirthClusterData.kwirthHome}) at cluster '${kwirthClusterData.name}' cannot be accessed right now.`)\r\n }\r\n\r\n if (enableCluster) {\r\n [ InstanceMessageChannelEnum.LOG, \r\n InstanceMessageChannelEnum.ALERT,\r\n InstanceMessageChannelEnum.METRICS\r\n ].map (channel => addChannelPermissions(channel,logger, cluster, kwirthClusterData))\r\n KwirthStaticData.clusterKwirthData.set(name, kwirthClusterData)\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} will be disabled`)\r\n }\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} has no Kwirth information (kwirthHome and kwirthApiKey are missing).`)\r\n }\r\n }\r\n }\r\n\r\n logger.info('Kwirth static data has been set including following clusters:')\r\n for (var c of KwirthStaticData.clusterKwirthData.keys()) {\r\n logger.info (' '+c)\r\n }\r\n for (var c of KwirthStaticData.clusterKwirthData.keys()) {\r\n console.log(KwirthStaticData.clusterKwirthData.get(c))\r\n }\r\n\r\n}\r\n\r\nexport { loadClusters }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { ClusterValidPods, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { KwirthClusterData, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { KwirthStaticData } from '../model/KwirthStaticData';\r\n//import { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\nconst checkNamespaceAccess = (channel:string, cluster:ClusterValidPods, podData:PodData, userEntityRef:string, userGroups:string[]) : boolean => {\r\n let allowedToNamespace=false\r\n let namespacePermissions = KwirthStaticData.clusterKwirthData.get(cluster.name)?.namespacePermissions\r\n\r\n if (namespacePermissions?.has(channel)) {\r\n let rule = namespacePermissions?.get(channel)!.find(ns => ns.namespace===podData.namespace)\r\n if (rule) {\r\n if (rule.identityRefs.includes(userEntityRef.toLowerCase())) {\r\n // a user ref has been found on a namespace rule\r\n allowedToNamespace=true\r\n }\r\n else {\r\n var groupResult=rule.identityRefs.some(identityRef => userGroups.includes(identityRef));\r\n if (groupResult) {\r\n // a group ref match has been found\r\n allowedToNamespace=true\r\n }\r\n }\r\n }\r\n else {\r\n // no restrictions for this namespace\r\n allowedToNamespace=true\r\n }\r\n }\r\n else {\r\n console.log(`Invalid channel: ${channel}`)\r\n }\r\n return allowedToNamespace\r\n}\r\n\r\nconst checkPodPermissionRule = (ppr:PodPermissionRule, entityName:string, userEntityRef:string, userGroups:string[]) : boolean => {\r\n var refMatch:boolean=false;\r\n\r\n for (var podNameRegex of ppr.pods) {\r\n if (podNameRegex.test(entityName)) {\r\n for (var refRegex of ppr.refs) {\r\n // find userRef\r\n refMatch=refRegex.test(userEntityRef.toLowerCase())\r\n if (refMatch) {\r\n break;\r\n }\r\n else {\r\n // find group ref\r\n refMatch = userGroups.some(g => refRegex.test(g))\r\n if (refMatch) {\r\n break\r\n }\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n if (refMatch) break\r\n }\r\n return refMatch\r\n}\r\n\r\n// const getPodPermissionSet = (reqScope:InstanceConfigScopeEnum, cluster:KwirthClusterData) => {\r\n// switch (reqScope) {\r\n// // case InstanceConfigScopeEnum.FILTER:\r\n// case InstanceConfigScopeEnum.SNAPSHOT:\r\n// case InstanceConfigScopeEnum.STREAM:\r\n// console.log(cluster.podPermissions)\r\n// return cluster.podPermissions.get(reqScope)\r\n// default:\r\n// console.log(`Invalid scope ${reqScope} for permission set`)\r\n// }\r\n// return undefined\r\n// }\r\nconst getPodPermissionSet = (channel:string, cluster:KwirthClusterData) => {\r\n if (cluster.podPermissions.has(channel)) {\r\n return cluster.podPermissions.get(channel)\r\n }\r\n else {\r\n console.log(`Invalid channel ${channel} for permission set`)\r\n return undefined\r\n }\r\n}\r\n\r\n/**\r\n * This funciton checks permissions according to app-config rules (not kwirth rules), that is, namespace rules,\r\n * viewing rules and restarting rules\r\n * @param loggerSvc Backstage logger\r\n * @param reqCluster the cluster the pod belongs to\r\n * @param reqPod data about the pod the user wants to access\r\n * @param podPermissionSet a set of permission for the cluster (extracted from app-config)\r\n * @param entityName the name of the entity to search for\r\n * @param userEntityRef the canonical identity reference for the user ('type:namespace/ref')\r\n * @param userGroups ana array containing a list of groups the user belongs to\r\n * @returns booelan indicating if the user can access the pod for doing what scaope says (view or restart)\r\n */\r\n\r\nconst checkPodAccess = (reqPod:PodData, podPermissionSet:KwirthPodPermissions[], entityName:string, userEntityRef:string, userGroups:string[]):boolean => {\r\n // we check all pod permissions until one of them evaluates to true (must be true on allow/except and false on deny/unless)\r\n\r\n // we use 'filter' here beacause a namespace can be specified more than once\r\n for (var podPermission of podPermissionSet.filter(pp => pp.namespace===reqPod.namespace)) {\r\n if (podPermission.allow) {\r\n \r\n // **** evaluate allow/except rules ****\r\n var allowMatches=false;\r\n var exceptMatches=false;\r\n // we test all allow rules, we stop if one matches\r\n for (var prr of podPermission.allow) {\r\n allowMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);\r\n }\r\n if (allowMatches) {\r\n if (podPermission.except) {\r\n // we test all except rules, will stop if found one that matches, no need to continue\r\n for (var prr of podPermission.except) {\r\n //exceptMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups);\r\n exceptMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n // if there is a exception the process finishes now for this podPermission)\r\n if (exceptMatches) {\r\n break\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n }\r\n\r\n if (allowMatches && !exceptMatches) {\r\n // **** evaluate deny/unless rules ****\r\n if (podPermission.deny) {\r\n var denyMatches=false\r\n var unlessMatches=false\r\n for (var prr of podPermission.deny) {\r\n //denyMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n denyMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (denyMatches) {\r\n break;\r\n }\r\n }\r\n if (denyMatches && podPermission.unless) {\r\n for (var prr of podPermission.unless) {\r\n //unlessMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n unlessMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (unlessMatches) {\r\n break;\r\n }\r\n }\r\n }\r\n if (!denyMatches || (denyMatches && unlessMatches)) {\r\n return true\r\n }\r\n }\r\n else {\r\n return true\r\n }\r\n }\r\n else {\r\n // do nothing, just continue podpermissionset loop\r\n }\r\n }\r\n else {\r\n // if no 'allow' is specified everybody has access\r\n // we continue loop checking other namespaces\r\n return true\r\n }\r\n }\r\n \r\n return false\r\n}\r\n\r\nexport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport express from 'express'\r\nimport Router from 'express-promise-router'\r\nimport { AuthService, BackstageUserInfo, DiscoveryService, HttpAuthService, LoggerService, RootConfigService, UserInfoService } from '@backstage/backend-plugin-api'\r\nimport { CatalogClient } from '@backstage/catalog-client'\r\nimport { UserEntity } from '@backstage/catalog-model'\r\nimport { FetchApi } from '@backstage/core-plugin-api'\r\n\r\n// Kwirth\r\nimport { ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR, ClusterValidPods, MetricDefinition, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { loadClusters } from './config'\r\nimport { KwirthStaticData, VERSION } from '../model/KwirthStaticData'\r\nimport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet } from './permissions'\r\nimport { accessKeySerialize, InstanceConfigScopeEnum, versionGreatThan } from '@jfvilas/kwirth-common'\r\n\r\nexport type KwirthRouterOptions = {\r\n discoverySvc: DiscoveryService\r\n configSvc: RootConfigService\r\n loggerSvc: LoggerService\r\n userInfoSvc: UserInfoService\r\n authSvc: AuthService\r\n httpAuthSvc: HttpAuthService\r\n}\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\n/**\r\n * \r\n * @param options core services we need for Kwirth to work\r\n * @returns an express Router\r\n */\r\nasync function createRouter(options: KwirthRouterOptions) : Promise<express.Router> {\r\n const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;\r\n\r\n loggerSvc.info('Loading static config')\r\n\r\n if (!configSvc.has('kubernetes.clusterLocatorMethods')) {\r\n loggerSvc.error(`Kwirth will not start, there is no 'clusterLocatorMethods' defined in app-config.`)\r\n throw new Error('Kwirth backend will not be available.')\r\n }\r\n\r\n try {\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch (err) {\r\n let txt=`Errors detected reading static configuration: ${err}`\r\n loggerSvc.error(txt)\r\n throw new Error(txt)\r\n }\r\n\r\n // subscribe to changes on app-config\r\n if (configSvc.subscribe) {\r\n configSvc.subscribe( () => {\r\n try {\r\n loggerSvc.warn('Change detected on app-config, Kwirth will update config.')\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch(err) {\r\n loggerSvc.error(`Errors detected reading new configuration: ${err}`)\r\n }\r\n })\r\n }\r\n else {\r\n loggerSvc.info('Kwirth cannot subscribe to config changes.')\r\n }\r\n\r\n const router = Router()\r\n\r\n router.use(express.json())\r\n\r\n // we need this function to be able to invoke another backend plugin passing an authorization token\r\n const createAuthFetchApi = (token: string) : FetchApi => {\r\n return {\r\n fetch: async (input, init) => {\r\n init = init || {}\r\n init.headers = {\r\n ...init.headers,\r\n Authorization: `Bearer ${token}`,\r\n }\r\n return fetch(input, init)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Invokes Kwirth to obtain a list of pods that are tagged with the kubernetes-id of the entity we are looking for.\r\n * @param entityName name of the tagge dentity\r\n * @returns a ClusterValidPods[] (each ClusterValidPods is a cluster info with a list of pods tagged with the entityName).\r\n */\r\n // const getValidClusters = async (entityName:string) : Promise<ClusterValidPods[]> => {\r\n // let clusterList:ClusterValidPods[]=[]\r\n\r\n // for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {\r\n // let url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome as string\r\n // let apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey\r\n // let title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title\r\n // // ways to select components:\r\n // // label id:\r\n // // 'backstage.io/kubernetes-id': 'xxxxx'\r\n // // label selector:\r\n // // 'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end'\r\n // let queryUrl=url+`/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entityName}&type=pod&data=containers`\r\n // try {\r\n // let fetchResp = await fetch (queryUrl, {headers:{'Authorization':'Bearer '+apiKeyStr}})\r\n // if (fetchResp.status===200) {\r\n // let jsonResp=await fetchResp.json()\r\n // if (jsonResp) {\r\n // let podData:ClusterValidPods = {\r\n // name: clusterName, url, title, data: jsonResp, accessKeys: new Map()\r\n // }\r\n // clusterList.push(podData)\r\n // }\r\n // }\r\n // else {\r\n // loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`)\r\n // console.log(await fetchResp.text())\r\n // clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n // }\r\n\r\n // }\r\n // catch (err) {\r\n // loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`)\r\n // clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n // }\r\n // }\r\n\r\n // return clusterList\r\n // }\r\n\r\n const getValidClustersFromEntity = async (entity:any) : Promise<ClusterValidPods[]> => {\r\n let clusterList:ClusterValidPods[]=[]\r\n\r\n for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {\r\n let url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome as string\r\n let apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey\r\n let title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title\r\n let clusterVersion = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthData.version || '0.0.0'\r\n\r\n // ways to select components:\r\n // label id:\r\n // 'backstage.io/kubernetes-id': 'xxxxx'\r\n // label selector:\r\n // 'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end'\r\n let queryUrl = undefined\r\n if (entity.metadata.annotations['backstage.io/kubernetes-id']) {\r\n queryUrl=url+`/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entity.metadata.annotations['backstage.io/kubernetes-id']}&type=pod&data=containers`\r\n }\r\n else if (entity.metadata.annotations['backstage.io/kubernetes-label-selector']) {\r\n if (versionGreatThan(clusterVersion,'0.4.40')) {\r\n let escapedLabelSelector = encodeURIComponent(entity.metadata.annotations['backstage.io/kubernetes-label-selector'])\r\n queryUrl=url+`/managecluster/find?labelselector=${escapedLabelSelector}&type=pod&data=containers`\r\n }\r\n else {\r\n loggerSvc.error(`Version ${clusterVersion} from cluster ${clusterName} is not valid for using ${ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR}`)\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n continue\r\n }\r\n }\r\n else {\r\n loggerSvc.error('Received request without labelid/labelselector')\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n continue\r\n }\r\n\r\n try {\r\n let fetchResp = await fetch (queryUrl, {headers:{'Authorization':'Bearer '+apiKeyStr}})\r\n if (fetchResp.status===200) {\r\n let jsonResp=await fetchResp.json()\r\n if (jsonResp) {\r\n let podData:ClusterValidPods = {\r\n name: clusterName, url, title, data: jsonResp, accessKeys: new Map()\r\n }\r\n clusterList.push(podData)\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid data received from cluster ${clusterName}`)\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`)\r\n let text = await fetchResp.text()\r\n if (text) loggerSvc.warn(text)\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n\r\n }\r\n catch (err) {\r\n loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`)\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n }\r\n\r\n return clusterList\r\n }\r\n\r\n const createAccessKey = async (reqScope:InstanceConfigScopeEnum, cluster:ClusterValidPods, reqPods:PodData[], userName:string) : Promise<any> => {\r\n let resources = reqPods.map(podData => `${reqScope}:${podData.namespace}::${podData.name}:`).join(';')\r\n\r\n let kwirthHome = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthHome as string\r\n let kwirthApiKey = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthApiKey\r\n let payload= {\r\n description: `Backstage API key for user ${userName}`,\r\n expire: Date.now()+60*60*1000,\r\n days: 1,\r\n accessKey: {\r\n id: '',\r\n type:'bearer',\r\n resources\r\n }\r\n }\r\n\r\n let fetchResp=await fetch(kwirthHome+'/key',{method:'POST', body:JSON.stringify(payload), headers:{'Content-Type':'application/json', Authorization:'Bearer '+kwirthApiKey}})\r\n if (fetchResp.status===200) {\r\n let data = await fetchResp.json()\r\n return data.accessKey\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`)\r\n return undefined\r\n }\r\n }\r\n\r\n const addAccessKeys = async (channel:string, reqScope:InstanceConfigScopeEnum, foundClusters:ClusterValidPods[], entityName:string, userEntityRef:string, userGroups:string[]) => {\r\n if (!reqScope) {\r\n loggerSvc.info(`Invalid scope requested: ${reqScope}`)\r\n return\r\n }\r\n let principal=userEntityRef.split(':')[1]\r\n let username=principal.split('/')[1]\r\n\r\n for (let foundCluster of foundClusters) {\r\n let podList:PodData[]=[]\r\n\r\n if ( !KwirthStaticData.clusterKwirthData.get(foundCluster.name)?.kwirthData.channels.some(c => c.id === channel) ) {\r\n loggerSvc.warn(`Cluster ${foundCluster.name} does not implement channel ${channel} (requested scope: ${reqScope})`)\r\n continue\r\n }\r\n\r\n // for each pod we've found on the cluster we check all namespace permissions\r\n for (let podData of foundCluster.data) {\r\n // first we check if user is allowed to acccess namespace\r\n let allowedToNamespace = checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups)\r\n\r\n if (allowedToNamespace) {\r\n // then we check if required pod namespace has pod access restriccions for requested namespace\r\n let clusterDef = KwirthStaticData.clusterKwirthData.get(foundCluster.name)\r\n let podPermissionSet = getPodPermissionSet(channel, clusterDef!)\r\n if (!podPermissionSet) {\r\n loggerSvc.warn(`Pod permission set not found: ${channel}`)\r\n continue\r\n }\r\n let namespaceRestricted = podPermissionSet.some(pp => pp.namespace===podData.namespace);\r\n if (!namespaceRestricted || checkPodAccess(podData, podPermissionSet, entityName, userEntityRef, userGroups)) {\r\n // there are no namespace restrictions specified in the pod permission set\r\n podList.push(podData)\r\n }\r\n else {\r\n }\r\n }\r\n else {\r\n // user is not allowed to namespace, so we don't need to check pod permissions\r\n // the loop cotinues with other pods\r\n }\r\n }\r\n if (podList.length>0) {\r\n let accessKey = await createAccessKey(reqScope, foundCluster, podList, username)\r\n if (accessKey) foundCluster.accessKeys.set(reqScope, accessKey)\r\n }\r\n else {\r\n loggerSvc.info(`No pods on podList for '${reqScope}' on channel '${channel}' in cluster '${foundCluster.name}' for searching for entity: '${entityName}'`)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * builds a list of groups (expressed as identity refs) that the user belongs to.\r\n * @param userInfo Backstage user info of the user to search groups for\r\n * @returns an array of group refs in canonical form\r\n */\r\n const getUserGroups = async (userInfo:BackstageUserInfo) : Promise<string[]> => {\r\n const { token } = await authSvc.getPluginRequestToken({\r\n onBehalfOf: await authSvc.getOwnServiceCredentials(),\r\n targetPluginId: 'catalog'\r\n });\r\n const catalogClient = new CatalogClient({\r\n discoveryApi: discoverySvc,\r\n fetchApi: createAuthFetchApi(token),\r\n });\r\n\r\n const entity = await catalogClient.getEntityByRef(userInfo.userEntityRef) as UserEntity\r\n let userGroupsRefs:string[]=[]\r\n //+++ future use: recursive memberOf\r\n if (entity?.spec.memberOf) userGroupsRefs=entity?.spec.memberOf\r\n return userGroupsRefs\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processVersion = async (_req:any, res:any) => {\r\n res.status(200).send({ version:VERSION })\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processAccess = async (req:express.Request, res:express.Response) => {\r\n if (!req.query['scopes'] || !req.query['channel']) {\r\n res.status(400).send(`'scopes' and 'channel' are required`)\r\n return\r\n }\r\n let reqScopes = (req.query['scopes'].toString()).split(',')\r\n let reqChannel = req.query['channel']?.toString()!\r\n \r\n // obtain basic user info\r\n const credentials = await httpAuthSvc.credentials(req, { allow: ['user'] })\r\n const userInfo = await userInfoSvc.getUserInfo(credentials)\r\n // get user groups list\r\n let userGroupsRefs=await getUserGroups(userInfo)\r\n\r\n loggerSvc.info(`Checking reqScopes '${req.query['scopes']}' scopes for working with pod: '${req.body.metadata.namespace+'/'+req.body.metadata.name}' for user '${userInfo.userEntityRef}'`)\r\n\r\n //+++ control errors here (maybe we cannot contact the cluster, for example)\r\n // get a list of clusters that contain pods related to entity\r\n //let foundClusters:ClusterValidPods[] = await getValidClustersFromEntity(req.body.metadata.name)\r\n let foundClusters:ClusterValidPods[] = await getValidClustersFromEntity(req.body)\r\n\r\n // add access keys to authorized resources (according to group membership and Kwirth config in app-config (namespace and pod permissions))\r\n for (let reqScopeStr of reqScopes) {\r\n let reqScope = reqScopeStr as InstanceConfigScopeEnum\r\n await addAccessKeys(reqChannel, reqScope, foundClusters, req.body.metadata.name, userInfo.userEntityRef, userGroupsRefs)\r\n if (reqScope === InstanceConfigScopeEnum.STREAM) {\r\n for (let cluster of foundClusters) {\r\n let accessKey = cluster.accessKeys.get(InstanceConfigScopeEnum.STREAM)\r\n if (accessKey) {\r\n let url = cluster.url+'/metrics'\r\n let auth = 'Bearer '+accessKeySerialize(accessKey)\r\n let fetchResp = await fetch (url, {headers:{'Authorization':auth}})\r\n try {\r\n let data = (await fetchResp.json()) as MetricDefinition[]\r\n cluster.metrics = data\r\n }\r\n catch (err) {\r\n loggerSvc.error(`Cannot get metrics on cluster ${cluster.name}: `+err)\r\n }\r\n }\r\n // else {\r\n // loggerSvc.warn(`Couldn't get accessKey for getting metrics list for cluster ${cluster.name}`)\r\n // }\r\n }\r\n }\r\n }\r\n \r\n // *** we build a string of arrays from the Map (Maps cannot be serialized)\r\n for (let c of foundClusters) {\r\n (c as any).accessKeys = JSON.stringify(Array.from(c.accessKeys.entries()))\r\n }\r\n res.status(200).send(foundClusters)\r\n }\r\n\r\n router.post(['/access'], (req, res) => {\r\n processAccess(req,res)\r\n })\r\n\r\n router.get(['/version'], (req, res) => {\r\n processVersion(req,res)\r\n })\r\n\r\n return router\r\n}\r\n\r\nexport { createRouter }\r\n","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\n\r\nimport { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'\r\nimport { createRouter } from './service/router'\r\n\r\nexport const kwirthPlugin = createBackendPlugin({\r\n pluginId: 'kwirth',\r\n register(env) {\r\n env.registerInit({\r\n deps: {\r\n discovery: coreServices.discovery,\r\n config: coreServices.rootConfig,\r\n logger: coreServices.logger,\r\n auth: coreServices.auth,\r\n httpAuth: coreServices.httpAuth,\r\n httpRouter: coreServices.httpRouter,\r\n userInfo: coreServices.userInfo\r\n },\r\n async init({ discovery, config, httpRouter, logger, auth, httpAuth, userInfo }) {\r\n httpRouter.use(\r\n await createRouter({\r\n discoverySvc: discovery,\r\n configSvc: config,\r\n loggerSvc: logger,\r\n authSvc: auth,\r\n httpAuthSvc: httpAuth,\r\n userInfoSvc: userInfo\r\n })\r\n )\r\n }\r\n })\r\n }\r\n})\r\n"],"names":["ClusterTypeEnum","versionGreatOrEqualThan","InstanceMessageChannelEnum","Router","express","versionGreatThan","ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR","catalogClient","CatalogClient","InstanceConfigScopeEnum","accessKeySerialize","createBackendPlugin","coreServices"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/model/KwirthStaticData.ts","../src/service/config.ts","../src/service/permissions.ts","../src/version.ts","../src/service/router.ts","../src/plugin.ts"],"sourcesContent":["import { IBackendInfo } from '@jfvilas/plugin-kwirth-common'\r\nimport { KwirthClusterData } from './KwirthClusterData'\r\n\r\n/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nconst MIN_KWIRTH_VERSION='0.4.63'\r\n\r\nclass KwirthStaticData {\r\n public static clusterKwirthData : Map<string,KwirthClusterData> = new Map()\r\n public static latestVersions : IBackendInfo\r\n}\r\n\r\nexport { KwirthStaticData, MIN_KWIRTH_VERSION }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\")\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { LoggerService, RootConfigService } from '@backstage/backend-plugin-api'\r\nimport { KwirthStaticData, MIN_KWIRTH_VERSION } from '../model/KwirthStaticData'\r\nimport { KwirthClusterData, KwirthNamespacePermissions, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { Config } from '@backstage/config'\r\nimport { ClusterTypeEnum, InstanceMessageChannelEnum, KwirthData, versionGreatOrEqualThan } from '@jfvilas/kwirth-common'\r\n\r\n\r\n/**\r\n * loads Namespace Permissions setting from app-config xml\r\n * @param channelConfig name of the Kwirth channel to read ('metrics', 'log'...)\r\n * @param logger Logger service\r\n */\r\nconst loadNamespacePermissions = (channelConfig:Config, logger:LoggerService):KwirthNamespacePermissions[] => {\r\n let namespacePermissions:KwirthNamespacePermissions[] = []\r\n if (channelConfig.has('namespacePermissions')) {\r\n logger.info(` Namespace permisson evaluation will be performed.`)\r\n let permNamespaces= (channelConfig.getOptionalConfigArray('namespacePermissions'))!\r\n for (let ns of permNamespaces) {\r\n let namespace=ns.keys()[0]\r\n let identityRefs=ns.getStringArray(namespace)\r\n identityRefs=identityRefs.map(g => g.toLowerCase())\r\n namespacePermissions.push ({ namespace, identityRefs })\r\n }\r\n }\r\n else {\r\n logger.info(` No namespace restrictions.`)\r\n namespacePermissions=[]\r\n }\r\n return namespacePermissions\r\n}\r\n\r\n/**\r\n * read rules about permissions to a pply to a set of pods\r\n * @param config an object config read from app-config\r\n * @param category a permission category, one of: allow, deny unless, except\r\n * @returns an array of PodPermissionRule's\r\n */\r\nconst loadPodRules = (config:Config, category:string):PodPermissionRule[] => {\r\n let rules:PodPermissionRule[]=[]\r\n for (let rule of config.getConfigArray(category)) {\r\n let podsStringArray = rule.getOptionalStringArray('pods') || ['.*']\r\n let podsRegexArray:RegExp[]=[]\r\n for (let expr of podsStringArray) {\r\n podsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n let refsStringArray = rule.getOptionalStringArray('refs') || ['.*']\r\n let refsRegexArray:RegExp[]=[]\r\n for (let expr of refsStringArray) {\r\n refsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n let prr:PodPermissionRule={\r\n pods:podsRegexArray,\r\n refs:refsRegexArray\r\n }\r\n rules.push(prr)\r\n }\r\n return rules\r\n}\r\n\r\n/**\r\n * loads pod permissions (namespace and pod) for a specific Kwirth channel\r\n * @param channelConfig then name of the key (inside app-config) to read config from\r\n * @param logger Logger service\r\n * @returns an array of pod permissions\r\n */\r\nconst loadPodPermissions = (channelConfig:Config, logger:LoggerService):KwirthPodPermissions[] => {\r\n let clusterPodPermissions:KwirthPodPermissions[]=[]\r\n if (channelConfig.has('podPermissions')) {\r\n logger.info(` Pod permisson evaluation will be performed.`)\r\n let namespaceList=channelConfig.getConfigArray('podPermissions')\r\n for (let ns of namespaceList) {\r\n let namespaceName=ns.keys()[0]\r\n let podPermissions:KwirthPodPermissions={ namespace:namespaceName }\r\n\r\n if (ns.getConfig(namespaceName).has('allow')) {\r\n podPermissions.allow=loadPodRules(ns.getConfig(namespaceName), 'allow')\r\n if (ns.getConfig(namespaceName).has('except')) podPermissions.except=loadPodRules(ns.getConfig(namespaceName), 'except')\r\n if (ns.getConfig(namespaceName).has('deny')) podPermissions.deny=loadPodRules(ns.getConfig(namespaceName), 'deny')\r\n if (ns.getConfig(namespaceName).has('unless')) podPermissions.unless=loadPodRules(ns.getConfig(namespaceName), 'unless')\r\n }\r\n else {\r\n podPermissions.allow=[]\r\n podPermissions.allow.push({\r\n pods: [new RegExp('.*')],\r\n refs: [new RegExp('.*')]\r\n })\r\n }\r\n clusterPodPermissions.push(podPermissions)\r\n }\r\n }\r\n else {\r\n logger.info(` No pod permissions will be applied.`)\r\n }\r\n return clusterPodPermissions\r\n}\r\n\r\n/**\r\n * Reads permissions for a kwirth channel (like log, metrics...)\r\n * @param channel name of the channel\r\n * @param logger bs logger service\r\n * @param cluster the app-config object containing the cluster to process\r\n * @param kdata current KwirthClusterData object to add channel permissions\r\n */\r\nconst addChannelPermissions = (channel: string, logger:LoggerService, cluster:Config, kdata:KwirthClusterData) => {\r\n let keyname = 'kwirth' + channel\r\n\r\n let keyCamelName = 'kwirth' + channel[0].toUpperCase() + channel.substring(1)\r\n if (cluster.has(keyCamelName)) keyname = keyCamelName\r\n\r\n if (cluster.has(keyname)) {\r\n logger.info(`Load permissions for channel ${channel} (config: ${keyname}).`)\r\n let configChannel=cluster.getConfig(keyname);\r\n if (configChannel.has('namespacePermissions')) {\r\n logger.info(` Loading namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, loadNamespacePermissions(configChannel, logger))\r\n }\r\n else {\r\n logger.info(` No namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, [])\r\n }\r\n if (configChannel.has('podPermissions')) {\r\n logger.info(` Loading pod permissions.`)\r\n kdata.podPermissions.set(channel, loadPodPermissions(configChannel, logger))\r\n }\r\n else {\r\n logger.info(` No pod permissions.`)\r\n kdata.podPermissions.set(channel, [])\r\n }\r\n }\r\n else {\r\n logger.info(`Cluster ${cluster.getString('name')} will have no channel '${channel}' restrictions.`)\r\n kdata.namespacePermissions.set(channel,[])\r\n kdata.podPermissions.set(channel,[])\r\n }\r\n}\r\n\r\n/**\r\n * reads app-config and builds a list of valid clusters\r\n * @param logger core service for logging\r\n * @param config core service for reading config info\r\n */\r\nconst loadClusters = async (logger:LoggerService, config:RootConfigService) => {\r\n KwirthStaticData.clusterKwirthData.clear()\r\n\r\n let locatingMethods=config.getConfigArray('kubernetes.clusterLocatorMethods')\r\n for (let method of locatingMethods) {\r\n\r\n let clusters=(method.getConfigArray('clusters'))\r\n for (let cluster of clusters) {\r\n\r\n let name=cluster.getString('name')\r\n if (cluster.has('kwirthHome') && cluster.has('kwirthApiKey')) { \r\n let kwirthHome:string = cluster.getOptionalString('kwirthHome')!\r\n let kwirthApiKey:string = cluster.getOptionalString('kwirthApiKey')!\r\n let title:string = (cluster.has('title')?cluster.getString('title'):'No name')\r\n let kwirthClusterData:KwirthClusterData={\r\n name,\r\n kwirthHome,\r\n kwirthApiKey,\r\n kwirthData: {\r\n version: '',\r\n clusterName: '',\r\n inCluster: false,\r\n namespace: '',\r\n deployment: '',\r\n lastVersion: '',\r\n clusterType: ClusterTypeEnum.KUBERNETES,\r\n metricsInterval: 0,\r\n channels: []\r\n },\r\n title,\r\n namespacePermissions: new Map(),\r\n podPermissions: new Map(),\r\n enabled: false\r\n }\r\n\r\n logger.info(`Kwirth for ${name} is located at ${kwirthClusterData.kwirthHome}. Testing connection...`)\r\n let enableCluster = false\r\n try {\r\n /*\r\n /config/info endpoint returns JSON:\r\n {\r\n \"clusterName\": \"inCluster\",\r\n \"namespace\": \"default\",\r\n \"deployment\": \"kwirth\",\r\n \"inCluster\": true,\r\n \"version\": \"0.4.11\",\r\n \"lastVersion\": \"0.4.11\",\r\n \"clusterType\": \"kubernetes\",\r\n \"metricsInterval\": 60,\r\n \"channels\": [\r\n {\r\n \"id\": \"log\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": false,\r\n \"reconnectable\": true,\r\n \"sources\": [\r\n \"docker\",\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"alert\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": false,\r\n \"reconnectable\": true,\r\n \"sources\": [\r\n \"docker\",\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"metrics\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": true,\r\n \"reconnectable\": true,\r\n \"sources\": [\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": true\r\n },\r\n {\r\n \"id\": \"ops\",\r\n \"routable\": true,\r\n \"pauseable\": false,\r\n \"modifyable\": false,\r\n \"reconnectable\": false,\r\n \"sources\": [\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"trivy\",\r\n \"routable\": false,\r\n \"pauseable\": false,\r\n \"modifyable\": false,\r\n \"reconnectable\": false,\r\n \"sources\": [\r\n \"kubernetes\"\r\n ],\r\n \"metrics\": false\r\n },\r\n {\r\n \"id\": \"echo\",\r\n \"routable\": false,\r\n \"pauseable\": true,\r\n \"modifyable\": false,\r\n \"reconnectable\": true,\r\n \"metrics\": false,\r\n \"sources\": [\r\n \"kubernetes\",\r\n \"docker\"\r\n ]\r\n }\r\n ]\r\n } \r\n */\r\n let response = await fetch (kwirthClusterData.kwirthHome+'/config/info')\r\n try {\r\n let data = await response.text()\r\n try {\r\n let kwirthData = JSON.parse(data) as KwirthData\r\n logger.info(`Kwirth info at cluster '${kwirthClusterData.name}': ${JSON.stringify(kwirthData)}`)\r\n kwirthClusterData.kwirthData=kwirthData\r\n if (versionGreatOrEqualThan(kwirthData.version, MIN_KWIRTH_VERSION)) {\r\n enableCluster = true\r\n }\r\n else {\r\n logger.error(`Unsupported Kwirth version on cluster '${name}' (${title}) [${kwirthData.version}]. Min version is ${MIN_KWIRTH_VERSION}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.error(`Kwirth at cluster ${kwirthClusterData.name} returned errors: ${err}`)\r\n logger.info('Returned data is:')\r\n logger.info(data)\r\n kwirthClusterData.kwirthData = {\r\n version:'0.0.0',\r\n clusterName:'unknown',\r\n inCluster:false,\r\n namespace:'unknown',\r\n deployment:'unknown',\r\n lastVersion:'0.0.0',\r\n clusterType: ClusterTypeEnum.KUBERNETES,\r\n metricsInterval: 0,\r\n channels: []\r\n }\r\n }\r\n }\r\n catch (err) {\r\n logger.warn(`Error parsing version response from cluster '${kwirthClusterData.name}': ${err}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.info(`Kwirth access error: ${err}.`)\r\n logger.warn(`Kwirth home URL (${kwirthClusterData.kwirthHome}) at cluster '${kwirthClusterData.name}' cannot be accessed right now.`)\r\n }\r\n\r\n if (enableCluster) {\r\n [ InstanceMessageChannelEnum.LOG, \r\n InstanceMessageChannelEnum.ALERT,\r\n InstanceMessageChannelEnum.METRICS\r\n ].map (channel => addChannelPermissions(channel,logger, cluster, kwirthClusterData))\r\n KwirthStaticData.clusterKwirthData.set(name, kwirthClusterData)\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} will be disabled`)\r\n }\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} has no Kwirth information (kwirthHome and kwirthApiKey are missing).`)\r\n }\r\n }\r\n }\r\n\r\n logger.info('Kwirth static data has been set including following clusters:')\r\n for (let c of KwirthStaticData.clusterKwirthData.keys()) {\r\n logger.info (' '+c)\r\n }\r\n for (let c of KwirthStaticData.clusterKwirthData.keys()) {\r\n console.log(KwirthStaticData.clusterKwirthData.get(c))\r\n }\r\n\r\n}\r\n\r\nconst loadKwirthInfo = async (logger:LoggerService) => {\r\n try {\r\n let pkb = await (await fetch ('https://registry.npmjs.org/@jfvilas/plugin-kwirth-backend')).json() as any\r\n let pkl = await (await fetch ('https://registry.npmjs.org/@jfvilas/plugin-kwirth-log')).json() as any\r\n let pkm = await (await fetch ('https://registry.npmjs.org/@jfvilas/plugin-kwirth-metrics')).json() as any\r\n let hubResp = await (await fetch ('https://hub.docker.com/v2/repositories/jfvilasoutlook/kwirth/tags?page_size=25&page=1&ordering=last_updated&name=')).json() as any\r\n KwirthStaticData.latestVersions = {\r\n ['plugin-kwirth-backend']: pkb['dist-tags'].latest,\r\n ['plugin-kwirth-log']: pkl['dist-tags'].latest,\r\n ['plugin-kwirth-metrics']: pkm['dist-tags'].latest,\r\n kwirth: hubResp.results[0].name\r\n }\r\n logger.info('Latest Kwirth-related artifacts versions are:')\r\n logger.info(JSON.stringify(KwirthStaticData.latestVersions))\r\n }\r\n catch (err) {\r\n console.log(err)\r\n logger.warn(\"Oops! We couldn't fetch the latest version info. Don't worry — it's not critical. Kwirth's still here for you.\")\r\n }\r\n}\r\n\r\nexport { loadClusters, loadKwirthInfo }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { ClusterValidPods, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { KwirthClusterData, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { KwirthStaticData } from '../model/KwirthStaticData';\r\n//import { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\nconst checkNamespaceAccess = (channel:string, cluster:ClusterValidPods, podData:PodData, userEntityRef:string, userGroups:string[]) : boolean => {\r\n let allowedToNamespace=false\r\n let namespacePermissions = KwirthStaticData.clusterKwirthData.get(cluster.name)?.namespacePermissions\r\n\r\n if (namespacePermissions?.has(channel)) {\r\n let rule = namespacePermissions?.get(channel)!.find(ns => ns.namespace===podData.namespace)\r\n if (rule) {\r\n if (rule.identityRefs.includes(userEntityRef.toLowerCase())) {\r\n // a user ref has been found on a namespace rule\r\n allowedToNamespace=true\r\n }\r\n else {\r\n var groupResult=rule.identityRefs.some(identityRef => userGroups.includes(identityRef));\r\n if (groupResult) {\r\n // a group ref match has been found\r\n allowedToNamespace=true\r\n }\r\n }\r\n }\r\n else {\r\n // no restrictions for this namespace\r\n allowedToNamespace=true\r\n }\r\n }\r\n else {\r\n console.log(`Invalid channel: ${channel}`)\r\n }\r\n return allowedToNamespace\r\n}\r\n\r\nconst checkPodPermissionRule = (ppr:PodPermissionRule, entityName:string, userEntityRef:string, userGroups:string[]) : boolean => {\r\n var refMatch:boolean=false;\r\n\r\n for (var podNameRegex of ppr.pods) {\r\n if (podNameRegex.test(entityName)) {\r\n for (var refRegex of ppr.refs) {\r\n // find userRef\r\n refMatch=refRegex.test(userEntityRef.toLowerCase())\r\n if (refMatch) {\r\n break;\r\n }\r\n else {\r\n // find group ref\r\n refMatch = userGroups.some(g => refRegex.test(g))\r\n if (refMatch) {\r\n break\r\n }\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n if (refMatch) break\r\n }\r\n return refMatch\r\n}\r\n\r\n// const getPodPermissionSet = (reqScope:InstanceConfigScopeEnum, cluster:KwirthClusterData) => {\r\n// switch (reqScope) {\r\n// // case InstanceConfigScopeEnum.FILTER:\r\n// case InstanceConfigScopeEnum.SNAPSHOT:\r\n// case InstanceConfigScopeEnum.STREAM:\r\n// console.log(cluster.podPermissions)\r\n// return cluster.podPermissions.get(reqScope)\r\n// default:\r\n// console.log(`Invalid scope ${reqScope} for permission set`)\r\n// }\r\n// return undefined\r\n// }\r\nconst getPodPermissionSet = (channel:string, cluster:KwirthClusterData) => {\r\n if (cluster.podPermissions.has(channel)) {\r\n return cluster.podPermissions.get(channel)\r\n }\r\n else {\r\n console.log(`Invalid channel ${channel} for permission set`)\r\n return undefined\r\n }\r\n}\r\n\r\n/**\r\n * This funciton checks permissions according to app-config rules (not kwirth rules), that is, namespace rules,\r\n * viewing rules and restarting rules\r\n * @param loggerSvc Backstage logger\r\n * @param reqCluster the cluster the pod belongs to\r\n * @param reqPod data about the pod the user wants to access\r\n * @param podPermissionSet a set of permission for the cluster (extracted from app-config)\r\n * @param entityName the name of the entity to search for\r\n * @param userEntityRef the canonical identity reference for the user ('type:namespace/ref')\r\n * @param userGroups ana array containing a list of groups the user belongs to\r\n * @returns booelan indicating if the user can access the pod for doing what scaope says (view or restart)\r\n */\r\n\r\nconst checkPodAccess = (reqPod:PodData, podPermissionSet:KwirthPodPermissions[], entityName:string, userEntityRef:string, userGroups:string[]):boolean => {\r\n // we check all pod permissions until one of them evaluates to true (must be true on allow/except and false on deny/unless)\r\n\r\n // we use 'filter' here beacause a namespace can be specified more than once\r\n for (var podPermission of podPermissionSet.filter(pp => pp.namespace===reqPod.namespace)) {\r\n if (podPermission.allow) {\r\n \r\n // **** evaluate allow/except rules ****\r\n var allowMatches=false;\r\n var exceptMatches=false;\r\n // we test all allow rules, we stop if one matches\r\n for (var prr of podPermission.allow) {\r\n allowMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);\r\n }\r\n if (allowMatches) {\r\n if (podPermission.except) {\r\n // we test all except rules, will stop if found one that matches, no need to continue\r\n for (var prr of podPermission.except) {\r\n //exceptMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups);\r\n exceptMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n // if there is a exception the process finishes now for this podPermission)\r\n if (exceptMatches) {\r\n break\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n }\r\n\r\n if (allowMatches && !exceptMatches) {\r\n // **** evaluate deny/unless rules ****\r\n if (podPermission.deny) {\r\n var denyMatches=false\r\n var unlessMatches=false\r\n for (var prr of podPermission.deny) {\r\n //denyMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n denyMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (denyMatches) {\r\n break;\r\n }\r\n }\r\n if (denyMatches && podPermission.unless) {\r\n for (var prr of podPermission.unless) {\r\n //unlessMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n unlessMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (unlessMatches) {\r\n break;\r\n }\r\n }\r\n }\r\n if (!denyMatches || (denyMatches && unlessMatches)) {\r\n return true\r\n }\r\n }\r\n else {\r\n return true\r\n }\r\n }\r\n else {\r\n // do nothing, just continue podpermissionset loop\r\n }\r\n }\r\n else {\r\n // if no 'allow' is specified everybody has access\r\n // we continue loop checking other namespaces\r\n return true\r\n }\r\n }\r\n \r\n return false\r\n}\r\n\r\nexport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet }","export const VERSION='0.12.9'\r\n","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport express from 'express'\r\nimport Router from 'express-promise-router'\r\nimport { AuthService, BackstageUserInfo, DiscoveryService, HttpAuthService, LoggerService, RootConfigService, UserInfoService } from '@backstage/backend-plugin-api'\r\nimport { CatalogClient } from '@backstage/catalog-client'\r\nimport { UserEntity } from '@backstage/catalog-model'\r\nimport { FetchApi } from '@backstage/core-plugin-api'\r\n\r\n// Kwirth\r\nimport { ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR, ClusterValidPods, MetricDefinition, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { loadClusters, loadKwirthInfo } from './config'\r\nimport { KwirthStaticData } from '../model/KwirthStaticData'\r\nimport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet } from './permissions'\r\nimport { accessKeySerialize, InstanceConfigScopeEnum, versionGreaterThan } from '@jfvilas/kwirth-common'\r\nimport { VERSION } from '../version'\r\n\r\nexport type KwirthRouterOptions = {\r\n discoverySvc: DiscoveryService\r\n configSvc: RootConfigService\r\n loggerSvc: LoggerService\r\n userInfoSvc: UserInfoService\r\n authSvc: AuthService\r\n httpAuthSvc: HttpAuthService\r\n}\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\n/**\r\n * \r\n * @param options core services we need for Kwirth to work\r\n * @returns an express Router\r\n */\r\nasync function createRouter(options: KwirthRouterOptions) : Promise<express.Router> {\r\n const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;\r\n\r\n loggerSvc.info('Loading static config')\r\n\r\n if (!configSvc.has('kubernetes.clusterLocatorMethods')) {\r\n loggerSvc.error(`Kwirth will not start, there is no 'clusterLocatorMethods' defined in app-config.`)\r\n throw new Error('Kwirth backend will not be available.')\r\n }\r\n\r\n try {\r\n loadClusters(loggerSvc, configSvc)\r\n loadKwirthInfo(loggerSvc)\r\n }\r\n catch (err) {\r\n let txt=`Errors detected reading static configuration: ${err}`\r\n loggerSvc.error(txt)\r\n throw new Error(txt)\r\n }\r\n\r\n // subscribe to changes on app-config\r\n if (configSvc.subscribe) {\r\n configSvc.subscribe( () => {\r\n try {\r\n loggerSvc.warn('Change detected on app-config, Kwirth will update config.')\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch(err) {\r\n loggerSvc.error(`Errors detected reading new configuration: ${err}`)\r\n }\r\n })\r\n }\r\n else {\r\n loggerSvc.info('Kwirth cannot subscribe to config changes.')\r\n }\r\n\r\n const router = Router()\r\n\r\n router.use(express.json())\r\n\r\n // we need this function to be able to invoke another backend plugin passing an authorization token\r\n const createAuthFetchApi = (token: string) : FetchApi => {\r\n return {\r\n fetch: async (input, init) => {\r\n init = init || {}\r\n init.headers = {\r\n ...init.headers,\r\n Authorization: `Bearer ${token}`,\r\n }\r\n return fetch(input, init)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Invokes Kwirth to obtain a list of pods that are tagged with the kubernetes-id of the entity we are looking for.\r\n * @param entityName name of the tagge dentity\r\n * @returns a ClusterValidPods[] (each ClusterValidPods is a cluster info with a list of pods tagged with the entityName).\r\n */\r\n // const getValidClusters = async (entityName:string) : Promise<ClusterValidPods[]> => {\r\n // let clusterList:ClusterValidPods[]=[]\r\n\r\n // for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {\r\n // let url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome as string\r\n // let apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey\r\n // let title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title\r\n // // ways to select components:\r\n // // label id:\r\n // // 'backstage.io/kubernetes-id': 'xxxxx'\r\n // // label selector:\r\n // // 'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end'\r\n // let queryUrl=url+`/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entityName}&type=pod&data=containers`\r\n // try {\r\n // let fetchResp = await fetch (queryUrl, {headers:{'Authorization':'Bearer '+apiKeyStr}})\r\n // if (fetchResp.status===200) {\r\n // let jsonResp=await fetchResp.json()\r\n // if (jsonResp) {\r\n // let podData:ClusterValidPods = {\r\n // name: clusterName, url, title, data: jsonResp, accessKeys: new Map()\r\n // }\r\n // clusterList.push(podData)\r\n // }\r\n // }\r\n // else {\r\n // loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`)\r\n // console.log(await fetchResp.text())\r\n // clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n // }\r\n\r\n // }\r\n // catch (err) {\r\n // loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`)\r\n // clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n // }\r\n // }\r\n\r\n // return clusterList\r\n // }\r\n\r\n const getValidClustersFromEntity = async (entity:any) : Promise<ClusterValidPods[]> => {\r\n let clusterList:ClusterValidPods[]=[]\r\n\r\n for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {\r\n let url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome as string\r\n let apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey\r\n let title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title\r\n let clusterVersion = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthData.version || '0.0.0'\r\n\r\n // ways to select components:\r\n // label id:\r\n // 'backstage.io/kubernetes-id': 'xxxxx'\r\n // label selector:\r\n // 'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end'\r\n let queryUrl = undefined\r\n if (entity.metadata.annotations['backstage.io/kubernetes-id']) {\r\n queryUrl=url+`/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entity.metadata.annotations['backstage.io/kubernetes-id']}&type=pod&data=containers`\r\n }\r\n else if (entity.metadata.annotations['backstage.io/kubernetes-label-selector']) {\r\n if (versionGreaterThan(clusterVersion,'0.4.40')) {\r\n let escapedLabelSelector = encodeURIComponent(entity.metadata.annotations['backstage.io/kubernetes-label-selector'])\r\n queryUrl=url+`/managecluster/find?labelselector=${escapedLabelSelector}&type=pod&data=containers`\r\n }\r\n else {\r\n loggerSvc.error(`Version ${clusterVersion} from cluster ${clusterName} is not valid for using ${ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR}`)\r\n clusterList.push({ name: clusterName, url, title, pods:[], accessKeys:new Map() })\r\n continue\r\n }\r\n }\r\n else {\r\n loggerSvc.error('Received request without labelid/labelselector')\r\n clusterList.push({ name: clusterName, url, title, pods:[], accessKeys:new Map() })\r\n continue\r\n }\r\n\r\n try {\r\n let fetchResp = await fetch (queryUrl, {headers:{'Authorization':'Bearer '+apiKeyStr}})\r\n if (fetchResp.status===200) {\r\n let jsonResp=await fetchResp.json()\r\n if (jsonResp) {\r\n let podData:ClusterValidPods = {\r\n name: clusterName, url, title, pods: jsonResp, accessKeys: new Map()\r\n }\r\n clusterList.push(podData)\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid data received from cluster ${clusterName}`)\r\n clusterList.push({ name: clusterName, url, title, pods:[], accessKeys:new Map() })\r\n }\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`)\r\n let text = await fetchResp.text()\r\n if (text) loggerSvc.warn(text)\r\n clusterList.push({ name: clusterName, url, title, pods:[], accessKeys:new Map() })\r\n }\r\n\r\n }\r\n catch (err) {\r\n loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`)\r\n clusterList.push({ name: clusterName, url, title, pods:[], accessKeys:new Map() })\r\n }\r\n }\r\n\r\n return clusterList\r\n }\r\n\r\n const createAccessKey = async (reqScope:InstanceConfigScopeEnum, cluster:ClusterValidPods, reqPods:PodData[], userName:string) : Promise<any> => {\r\n let resources = reqPods.map(podData => `${reqScope}:${podData.namespace}::${podData.name}:`).join(';')\r\n\r\n let kwirthHome = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthHome as string\r\n let kwirthApiKey = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthApiKey\r\n let payload= {\r\n description: `Backstage API key for user ${userName}`,\r\n expire: Date.now()+60*60*1000,\r\n days: 1,\r\n accessKey: {\r\n id: '',\r\n type:'bearer',\r\n resources\r\n }\r\n }\r\n\r\n let fetchResp=await fetch(kwirthHome+'/key',{method:'POST', body:JSON.stringify(payload), headers:{'Content-Type':'application/json', Authorization:'Bearer '+kwirthApiKey}})\r\n if (fetchResp.status===200) {\r\n let data = await fetchResp.json()\r\n return data.accessKey\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`)\r\n return undefined\r\n }\r\n }\r\n\r\n const addAccessKeys = async (channel:string, reqScope:InstanceConfigScopeEnum, foundClusters:ClusterValidPods[], entityName:string, userEntityRef:string, userGroups:string[]) => {\r\n if (!reqScope) {\r\n loggerSvc.info(`Invalid scope requested: ${reqScope}`)\r\n return\r\n }\r\n let principal=userEntityRef.split(':')[1]\r\n let username=principal.split('/')[1]\r\n\r\n for (let foundCluster of foundClusters) {\r\n let podList:PodData[]=[]\r\n\r\n if ( !KwirthStaticData.clusterKwirthData.get(foundCluster.name)?.kwirthData.channels.some(c => c.id === channel) ) {\r\n loggerSvc.warn(`Cluster ${foundCluster.name} does not implement channel ${channel} (requested scope: ${reqScope})`)\r\n continue\r\n }\r\n\r\n // for each pod we've found on the cluster we check all namespace permissions\r\n for (let podData of foundCluster.pods) {\r\n // first we check if user is allowed to acccess namespace\r\n let allowedToNamespace = checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups)\r\n\r\n if (allowedToNamespace) {\r\n // then we check if required pod namespace has pod access restriccions for requested namespace\r\n let clusterDef = KwirthStaticData.clusterKwirthData.get(foundCluster.name)\r\n let podPermissionSet = getPodPermissionSet(channel, clusterDef!)\r\n if (!podPermissionSet) {\r\n loggerSvc.warn(`Pod permission set not found: ${channel}`)\r\n continue\r\n }\r\n let namespaceRestricted = podPermissionSet.some(pp => pp.namespace===podData.namespace);\r\n if (!namespaceRestricted || checkPodAccess(podData, podPermissionSet, entityName, userEntityRef, userGroups)) {\r\n // there are no namespace restrictions specified in the pod permission set\r\n podList.push(podData)\r\n }\r\n else {\r\n }\r\n }\r\n else {\r\n // user is not allowed to namespace, so we don't need to check pod permissions\r\n // the loop cotinues with other pods\r\n }\r\n }\r\n if (podList.length>0) {\r\n let accessKey = await createAccessKey(reqScope, foundCluster, podList, username)\r\n if (accessKey) foundCluster.accessKeys.set(reqScope, accessKey)\r\n }\r\n else {\r\n loggerSvc.info(`No pods on podList for '${reqScope}' on channel '${channel}' in cluster '${foundCluster.name}' for searching for entity: '${entityName}'`)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * builds a list of groups (expressed as identity refs) that the user belongs to.\r\n * @param userInfo Backstage user info of the user to search groups for\r\n * @returns an array of group refs in canonical form\r\n */\r\n const getUserGroups = async (userInfo:BackstageUserInfo) : Promise<string[]> => {\r\n const { token } = await authSvc.getPluginRequestToken({\r\n onBehalfOf: await authSvc.getOwnServiceCredentials(),\r\n targetPluginId: 'catalog'\r\n });\r\n const catalogClient = new CatalogClient({\r\n discoveryApi: discoverySvc,\r\n fetchApi: createAuthFetchApi(token),\r\n });\r\n\r\n const entity = await catalogClient.getEntityByRef(userInfo.userEntityRef) as UserEntity\r\n let userGroupsRefs:string[]=[]\r\n //+++ future use: recursive memberOf\r\n if (entity?.spec.memberOf) userGroupsRefs=entity?.spec.memberOf\r\n return userGroupsRefs\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processVersion = async (_req:any, res:any) => {\r\n res.status(200).send({ version:VERSION })\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processInfo = async (_req:any, res:any) => {\r\n res.status(200).send(\r\n KwirthStaticData.latestVersions\r\n )\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processAccess = async (req:express.Request, res:express.Response) => {\r\n if (!req.query['scopes'] || !req.query['channel']) {\r\n res.status(400).send(`'scopes' and 'channel' are required`)\r\n return\r\n }\r\n let reqScopes = (req.query['scopes'].toString()).split(',')\r\n let reqChannel = req.query['channel']?.toString()!\r\n \r\n // obtain basic user info\r\n const credentials = await httpAuthSvc.credentials(req, { allow: ['user'] })\r\n const userInfo = await userInfoSvc.getUserInfo(credentials)\r\n // get user groups list\r\n let userGroupsRefs=await getUserGroups(userInfo)\r\n\r\n loggerSvc.info(`Checking reqScopes '${req.query['scopes']}' scopes for working with pod: '${req.body.metadata.namespace+'/'+req.body.metadata.name}' for user '${userInfo.userEntityRef}'`)\r\n\r\n //+++ control errors here (maybe we cannot contact the cluster, for example)\r\n // get a list of clusters that contain pods related to entity\r\n //let foundClusters:ClusterValidPods[] = await getValidClustersFromEntity(req.body.metadata.name)\r\n let foundClusters:ClusterValidPods[] = await getValidClustersFromEntity(req.body)\r\n\r\n // add access keys to authorized resources (according to group membership and Kwirth config in app-config (namespace and pod permissions))\r\n for (let reqScopeStr of reqScopes) {\r\n let reqScope = reqScopeStr as InstanceConfigScopeEnum\r\n await addAccessKeys(reqChannel, reqScope, foundClusters, req.body.metadata.name, userInfo.userEntityRef, userGroupsRefs)\r\n if (reqScope === InstanceConfigScopeEnum.STREAM) {\r\n for (let cluster of foundClusters) {\r\n let accessKey = cluster.accessKeys.get(InstanceConfigScopeEnum.STREAM)\r\n if (accessKey) {\r\n let url = cluster.url+'/metrics'\r\n let auth = 'Bearer '+accessKeySerialize(accessKey)\r\n let fetchResp = await fetch (url, {headers:{'Authorization':auth}})\r\n try {\r\n let data = (await fetchResp.json()) as MetricDefinition[]\r\n cluster.metrics = data\r\n }\r\n catch (err) {\r\n loggerSvc.error(`Cannot get metrics on cluster ${cluster.name}: `+err)\r\n }\r\n }\r\n // else {\r\n // loggerSvc.warn(`Couldn't get accessKey for getting metrics list for cluster ${cluster.name}`)\r\n // }\r\n }\r\n }\r\n }\r\n \r\n // *** we build a string of arrays from the Map (Maps cannot be serialized)\r\n for (let c of foundClusters) {\r\n (c as any).accessKeys = JSON.stringify(Array.from(c.accessKeys.entries()))\r\n }\r\n res.status(200).send(foundClusters)\r\n }\r\n\r\n router.post(['/access'], (req, res) => {\r\n processAccess(req,res)\r\n })\r\n\r\n router.get(['/version'], (req, res) => {\r\n processVersion(req,res)\r\n })\r\n\r\n router.get(['/info'], (req, res) => {\r\n processInfo(req,res)\r\n })\r\n\r\n return router\r\n}\r\n\r\nexport { createRouter }\r\n","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\n\r\nimport { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'\r\nimport { createRouter } from './service/router'\r\n\r\nexport const kwirthPlugin = createBackendPlugin({\r\n pluginId: 'kwirth',\r\n register(env) {\r\n env.registerInit({\r\n deps: {\r\n discovery: coreServices.discovery,\r\n config: coreServices.rootConfig,\r\n logger: coreServices.logger,\r\n auth: coreServices.auth,\r\n httpAuth: coreServices.httpAuth,\r\n httpRouter: coreServices.httpRouter,\r\n userInfo: coreServices.userInfo\r\n },\r\n async init({ discovery, config, httpRouter, logger, auth, httpAuth, userInfo }) {\r\n httpRouter.use(\r\n await createRouter({\r\n discoverySvc: discovery,\r\n configSvc: config,\r\n loggerSvc: logger,\r\n authSvc: auth,\r\n httpAuthSvc: httpAuth,\r\n userInfoSvc: userInfo\r\n })\r\n )\r\n }\r\n })\r\n }\r\n})\r\n"],"names":["ClusterTypeEnum","versionGreatOrEqualThan","InstanceMessageChannelEnum","Router","express","versionGreaterThan","ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR","catalogClient","CatalogClient","InstanceConfigScopeEnum","accessKeySerialize","createBackendPlugin","coreServices"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -19,5 +19,7 @@ declare function createRouter(options: KwirthRouterOptions): Promise<express.Rou
19
19
 
20
20
  declare const kwirthPlugin: _backstage_backend_plugin_api.BackendFeatureCompat;
21
21
 
22
- export { createRouter, kwirthPlugin as default };
22
+ declare const VERSION = "0.12.9";
23
+
24
+ export { VERSION, createRouter, kwirthPlugin as default };
23
25
  export type { KwirthRouterOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jfvilas/plugin-kwirth-backend",
3
- "version": "0.12.8",
3
+ "version": "0.13.1",
4
4
  "description": "Backstage backend plugin for Kwirth plugins",
5
5
  "keywords": [
6
6
  "Backstage",