@jfvilas/plugin-kwirth-backend 0.13.5 → 0.14.2
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 +4 -580
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/model/KwirthStaticData.cjs.js +11 -0
- package/dist/model/KwirthStaticData.cjs.js.map +1 -0
- package/dist/plugin.cjs.js +36 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/service/config.cjs.js +215 -0
- package/dist/service/config.cjs.js.map +1 -0
- package/dist/service/permissions.cjs.js +108 -0
- package/dist/service/permissions.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +244 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/dist/version.cjs.js +6 -0
- package/dist/version.cjs.js.map +1 -0
- package/package.json +21 -11
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.cjs.js","sources":["../../src/service/permissions.ts"],"sourcesContent":["/*\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 }"],"names":["KwirthStaticData"],"mappings":"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"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var express = require('express');
|
|
4
|
+
var Router = require('express-promise-router');
|
|
5
|
+
var catalogClient = require('@backstage/catalog-client');
|
|
6
|
+
var pluginKwirthCommon = require('@jfvilas/plugin-kwirth-common');
|
|
7
|
+
var config = require('./config.cjs.js');
|
|
8
|
+
var KwirthStaticData = require('../model/KwirthStaticData.cjs.js');
|
|
9
|
+
var permissions = require('./permissions.cjs.js');
|
|
10
|
+
var kwirthCommon = require('@jfvilas/kwirth-common');
|
|
11
|
+
var version = require('../version.cjs.js');
|
|
12
|
+
|
|
13
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
14
|
+
|
|
15
|
+
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
16
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
17
|
+
|
|
18
|
+
async function createRouter(options) {
|
|
19
|
+
const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;
|
|
20
|
+
loggerSvc.info("Loading static config");
|
|
21
|
+
if (!configSvc.has("kubernetes.clusterLocatorMethods")) {
|
|
22
|
+
loggerSvc.error(`Kwirth will not start, there is no 'clusterLocatorMethods' defined in app-config.`);
|
|
23
|
+
throw new Error("Kwirth backend will not be available.");
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
config.loadClusters(loggerSvc, configSvc);
|
|
27
|
+
config.loadKwirthInfo(loggerSvc);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
let txt = `Errors detected reading static configuration: ${err}`;
|
|
30
|
+
loggerSvc.error(txt);
|
|
31
|
+
throw new Error(txt);
|
|
32
|
+
}
|
|
33
|
+
if (configSvc.subscribe) {
|
|
34
|
+
configSvc.subscribe(() => {
|
|
35
|
+
try {
|
|
36
|
+
loggerSvc.warn("Change detected on app-config, Kwirth will update config.");
|
|
37
|
+
config.loadClusters(loggerSvc, configSvc);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
loggerSvc.error(`Errors detected reading new configuration: ${err}`);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
loggerSvc.info("Kwirth cannot subscribe to config changes.");
|
|
44
|
+
}
|
|
45
|
+
const router = Router__default.default();
|
|
46
|
+
router.use(express__default.default.json());
|
|
47
|
+
const createAuthFetchApi = (token) => {
|
|
48
|
+
return {
|
|
49
|
+
fetch: async (input, init) => {
|
|
50
|
+
init = init || {};
|
|
51
|
+
init.headers = {
|
|
52
|
+
...init.headers,
|
|
53
|
+
Authorization: `Bearer ${token}`
|
|
54
|
+
};
|
|
55
|
+
return fetch(input, init);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const getValidClustersFromEntity = async (entity) => {
|
|
60
|
+
let clusterList = [];
|
|
61
|
+
for (const clusterName of KwirthStaticData.KwirthStaticData.clusterKwirthData.keys()) {
|
|
62
|
+
let url = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome;
|
|
63
|
+
let apiKeyStr = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey;
|
|
64
|
+
let title = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(clusterName)?.title;
|
|
65
|
+
let clusterVersion = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthData.version || "0.0.0";
|
|
66
|
+
let queryUrl = void 0;
|
|
67
|
+
if (entity.metadata.annotations["backstage.io/kubernetes-id"]) {
|
|
68
|
+
queryUrl = url + `/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entity.metadata.annotations["backstage.io/kubernetes-id"]}&type=pod&data=containers`;
|
|
69
|
+
} else if (entity.metadata.annotations["backstage.io/kubernetes-label-selector"]) {
|
|
70
|
+
if (kwirthCommon.versionGreaterThan(clusterVersion, "0.4.40")) {
|
|
71
|
+
let escapedLabelSelector = encodeURIComponent(entity.metadata.annotations["backstage.io/kubernetes-label-selector"]);
|
|
72
|
+
queryUrl = url + `/managecluster/find?labelselector=${escapedLabelSelector}&type=pod&data=containers`;
|
|
73
|
+
} else {
|
|
74
|
+
loggerSvc.error(`Version ${clusterVersion} from cluster ${clusterName} is not valid for using ${pluginKwirthCommon.ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR}`);
|
|
75
|
+
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
loggerSvc.error("Received request without labelid/labelselector");
|
|
80
|
+
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
let fetchResp = await fetch(queryUrl, { headers: { "Authorization": "Bearer " + apiKeyStr } });
|
|
85
|
+
if (fetchResp.status === 200) {
|
|
86
|
+
let jsonResp = await fetchResp.json();
|
|
87
|
+
if (jsonResp) {
|
|
88
|
+
let podData = {
|
|
89
|
+
name: clusterName,
|
|
90
|
+
url,
|
|
91
|
+
title,
|
|
92
|
+
pods: jsonResp,
|
|
93
|
+
accessKeys: /* @__PURE__ */ new Map()
|
|
94
|
+
};
|
|
95
|
+
clusterList.push(podData);
|
|
96
|
+
} else {
|
|
97
|
+
loggerSvc.warn(`Invalid data received from cluster ${clusterName}`);
|
|
98
|
+
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`);
|
|
102
|
+
let text = await fetchResp.text();
|
|
103
|
+
if (text) loggerSvc.warn(text);
|
|
104
|
+
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`);
|
|
108
|
+
clusterList.push({ name: clusterName, url, title, pods: [], accessKeys: /* @__PURE__ */ new Map() });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return clusterList;
|
|
112
|
+
};
|
|
113
|
+
const createAccessKey = async (reqScope, cluster, reqPods, userName) => {
|
|
114
|
+
let resources = reqPods.map((podData) => `${reqScope}:${podData.namespace}::${podData.name}:`).join(";");
|
|
115
|
+
let kwirthHome = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthHome;
|
|
116
|
+
let kwirthApiKey = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthApiKey;
|
|
117
|
+
let payload = {
|
|
118
|
+
description: `Backstage API key for user ${userName}`,
|
|
119
|
+
expire: Date.now() + 60 * 60 * 1e3,
|
|
120
|
+
days: 1,
|
|
121
|
+
accessKey: {
|
|
122
|
+
id: "",
|
|
123
|
+
type: "bearer",
|
|
124
|
+
resources
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
let fetchResp = await fetch(kwirthHome + "/key", { method: "POST", body: JSON.stringify(payload), headers: { "Content-Type": "application/json", Authorization: "Bearer " + kwirthApiKey } });
|
|
128
|
+
if (fetchResp.status === 200) {
|
|
129
|
+
let data = await fetchResp.json();
|
|
130
|
+
return data.accessKey;
|
|
131
|
+
} else {
|
|
132
|
+
loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`);
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const addAccessKeys = async (channel, reqScope, foundClusters, entityName, userEntityRef, userGroups) => {
|
|
137
|
+
if (!reqScope) {
|
|
138
|
+
loggerSvc.info(`Invalid scope requested: ${reqScope}`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
let principal = userEntityRef.split(":")[1];
|
|
142
|
+
let username = principal.split("/")[1];
|
|
143
|
+
for (let foundCluster of foundClusters) {
|
|
144
|
+
let podList = [];
|
|
145
|
+
if (!KwirthStaticData.KwirthStaticData.clusterKwirthData.get(foundCluster.name)?.kwirthData.channels.some((c) => c.id === channel)) {
|
|
146
|
+
loggerSvc.warn(`Cluster ${foundCluster.name} does not implement channel ${channel} (requested scope: ${reqScope})`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
for (let podData of foundCluster.pods) {
|
|
150
|
+
let allowedToNamespace = permissions.checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups);
|
|
151
|
+
if (allowedToNamespace) {
|
|
152
|
+
let clusterDef = KwirthStaticData.KwirthStaticData.clusterKwirthData.get(foundCluster.name);
|
|
153
|
+
let podPermissionSet = permissions.getPodPermissionSet(channel, clusterDef);
|
|
154
|
+
if (!podPermissionSet) {
|
|
155
|
+
loggerSvc.warn(`Pod permission set not found: ${channel}`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
let namespaceRestricted = podPermissionSet.some((pp) => pp.namespace === podData.namespace);
|
|
159
|
+
if (!namespaceRestricted || permissions.checkPodAccess(podData, podPermissionSet, entityName, userEntityRef, userGroups)) {
|
|
160
|
+
podList.push(podData);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (podList.length > 0) {
|
|
165
|
+
let accessKey = await createAccessKey(reqScope, foundCluster, podList, username);
|
|
166
|
+
if (accessKey) foundCluster.accessKeys.set(reqScope, accessKey);
|
|
167
|
+
} else {
|
|
168
|
+
loggerSvc.info(`No pods on podList for '${reqScope}' on channel '${channel}' in cluster '${foundCluster.name}' for searching for entity: '${entityName}'`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const getUserGroups = async (userInfo) => {
|
|
173
|
+
const { token } = await authSvc.getPluginRequestToken({
|
|
174
|
+
onBehalfOf: await authSvc.getOwnServiceCredentials(),
|
|
175
|
+
targetPluginId: "catalog"
|
|
176
|
+
});
|
|
177
|
+
const catalogClient$1 = new catalogClient.CatalogClient({
|
|
178
|
+
discoveryApi: discoverySvc,
|
|
179
|
+
fetchApi: createAuthFetchApi(token)
|
|
180
|
+
});
|
|
181
|
+
const entity = await catalogClient$1.getEntityByRef(userInfo.userEntityRef);
|
|
182
|
+
let userGroupsRefs = [];
|
|
183
|
+
if (entity?.spec.memberOf) userGroupsRefs = entity?.spec.memberOf;
|
|
184
|
+
return userGroupsRefs;
|
|
185
|
+
};
|
|
186
|
+
const processVersion = async (_req, res) => {
|
|
187
|
+
res.status(200).send({ version: version.VERSION });
|
|
188
|
+
};
|
|
189
|
+
const processInfo = async (_req, res) => {
|
|
190
|
+
res.status(200).send(
|
|
191
|
+
KwirthStaticData.KwirthStaticData.latestVersions
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
const processAccess = async (req, res) => {
|
|
195
|
+
if (!req.query["scopes"] || !req.query["channel"]) {
|
|
196
|
+
res.status(400).send(`'scopes' and 'channel' are required`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
let reqScopes = req.query["scopes"].toString().split(",");
|
|
200
|
+
let reqChannel = req.query["channel"]?.toString();
|
|
201
|
+
const credentials = await httpAuthSvc.credentials(req, { allow: ["user"] });
|
|
202
|
+
const userInfo = await userInfoSvc.getUserInfo(credentials);
|
|
203
|
+
let userGroupsRefs = await getUserGroups(userInfo);
|
|
204
|
+
loggerSvc.info(`Checking reqScopes '${req.query["scopes"]}' scopes for working with pod: '${req.body.metadata.namespace + "/" + req.body.metadata.name}' for user '${userInfo.userEntityRef}'`);
|
|
205
|
+
let foundClusters = await getValidClustersFromEntity(req.body);
|
|
206
|
+
for (let reqScopeStr of reqScopes) {
|
|
207
|
+
let reqScope = reqScopeStr;
|
|
208
|
+
await addAccessKeys(reqChannel, reqScope, foundClusters, req.body.metadata.name, userInfo.userEntityRef, userGroupsRefs);
|
|
209
|
+
if (reqScope === kwirthCommon.InstanceConfigScopeEnum.STREAM) {
|
|
210
|
+
for (let cluster of foundClusters) {
|
|
211
|
+
let accessKey = cluster.accessKeys.get(kwirthCommon.InstanceConfigScopeEnum.STREAM);
|
|
212
|
+
if (accessKey) {
|
|
213
|
+
let url = cluster.url + "/metrics";
|
|
214
|
+
let auth = "Bearer " + kwirthCommon.accessKeySerialize(accessKey);
|
|
215
|
+
let fetchResp = await fetch(url, { headers: { "Authorization": auth } });
|
|
216
|
+
try {
|
|
217
|
+
let data = await fetchResp.json();
|
|
218
|
+
cluster.metrics = data;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
loggerSvc.error(`Cannot get metrics on cluster ${cluster.name}: ` + err);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (let c of foundClusters) {
|
|
227
|
+
c.accessKeys = JSON.stringify(Array.from(c.accessKeys.entries()));
|
|
228
|
+
}
|
|
229
|
+
res.status(200).send(foundClusters);
|
|
230
|
+
};
|
|
231
|
+
router.post(["/access"], (req, res) => {
|
|
232
|
+
processAccess(req, res);
|
|
233
|
+
});
|
|
234
|
+
router.get(["/version"], (req, res) => {
|
|
235
|
+
processVersion(req, res);
|
|
236
|
+
});
|
|
237
|
+
router.get(["/info"], (req, res) => {
|
|
238
|
+
processInfo(req, res);
|
|
239
|
+
});
|
|
240
|
+
return router;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
exports.createRouter = createRouter;
|
|
244
|
+
//# sourceMappingURL=router.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\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"],"names":["loadClusters","loadKwirthInfo","Router","express","KwirthStaticData","versionGreaterThan","ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR","checkNamespaceAccess","getPodPermissionSet","checkPodAccess","catalogClient","CatalogClient","VERSION","InstanceConfigScopeEnum","accessKeySerialize"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.cjs.js","sources":["../src/version.ts"],"sourcesContent":["export const VERSION:string=\"0.14.2\"; \r\n"],"names":[],"mappings":";;AAAO,MAAM,OAAA,GAAe;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jfvilas/plugin-kwirth-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "Backstage backend plugin for Kwirth plugins",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Backstage",
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
"pluginPackages": [
|
|
17
17
|
"@jfvilas/plugin-kwirth-backend",
|
|
18
18
|
"@jfvilas/plugin-kwirth-common"
|
|
19
|
-
]
|
|
19
|
+
],
|
|
20
|
+
"features": {
|
|
21
|
+
".": "@backstage/BackendFeature"
|
|
22
|
+
}
|
|
20
23
|
},
|
|
21
24
|
"repository": {
|
|
22
25
|
"type": "git",
|
|
@@ -45,13 +48,13 @@
|
|
|
45
48
|
"postpack": "backstage-cli package postpack"
|
|
46
49
|
},
|
|
47
50
|
"dependencies": {
|
|
48
|
-
"@backstage/backend-common": "^0.
|
|
49
|
-
"@backstage/backend-plugin-api": "^
|
|
50
|
-
"@backstage/catalog-client": "^1.
|
|
51
|
-
"@backstage/catalog-model": "^1.
|
|
52
|
-
"@backstage/config": "^1.
|
|
53
|
-
"@backstage/errors": "^1.2.
|
|
54
|
-
"@backstage/integration": "^
|
|
51
|
+
"@backstage/backend-common": "^0.25.0",
|
|
52
|
+
"@backstage/backend-plugin-api": "^1.8.0",
|
|
53
|
+
"@backstage/catalog-client": "^1.14.0",
|
|
54
|
+
"@backstage/catalog-model": "^1.7.7",
|
|
55
|
+
"@backstage/config": "^1.3.6",
|
|
56
|
+
"@backstage/errors": "^1.2.7",
|
|
57
|
+
"@backstage/integration": "^2.0.0",
|
|
55
58
|
"@types/express": "^4.17.6",
|
|
56
59
|
"express": "^4.17.1",
|
|
57
60
|
"express-promise-router": "^4.1.0",
|
|
@@ -59,12 +62,19 @@
|
|
|
59
62
|
"yn": "^4.0.0"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
|
-
"@backstage/cli": "^0.
|
|
65
|
+
"@backstage/cli": "^0.36.0",
|
|
63
66
|
"@types/node-fetch": "^2.5.12",
|
|
64
67
|
"@types/supertest": "^2.0.8",
|
|
65
68
|
"supertest": "^6.1.3"
|
|
66
69
|
},
|
|
67
70
|
"files": [
|
|
68
71
|
"dist"
|
|
69
|
-
]
|
|
72
|
+
],
|
|
73
|
+
"typesVersions": {
|
|
74
|
+
"*": {
|
|
75
|
+
"package.json": [
|
|
76
|
+
"package.json"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
70
80
|
}
|