From 47d82e4d626327f49dc1a05a482e1b85603f4fcc Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Sun, 22 Mar 2020 10:21:48 +0200 Subject: [PATCH] move config route to main Signed-off-by: Jari Kolehmainen --- dashboard/server/app.ts | 3 +- dashboard/server/common/cluster.ts | 9 +--- dashboard/server/routes/config-route.ts | 48 ------------------ dashboard/server/routes/index.ts | 1 - src/main/router.ts | 2 + src/main/routes/config.ts | 66 +++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 58 deletions(-) delete mode 100644 dashboard/server/routes/config-route.ts create mode 100644 src/main/routes/config.ts diff --git a/dashboard/server/app.ts b/dashboard/server/app.ts index 6de90d466a..9f6d98c58c 100644 --- a/dashboard/server/app.ts +++ b/dashboard/server/app.ts @@ -8,7 +8,7 @@ import compression from "compression" import helmet from "helmet" import morgan from "morgan" import { logger } from "../server/utils/logger" -import { configRoute, kubewatchRoute, metricsRoute, readyStateRoute } from "../server/routes"; +import { kubewatchRoute, metricsRoute, readyStateRoute } from "../server/routes"; import { useRequestHeaderToken } from "../server/middlewares"; const { @@ -23,7 +23,6 @@ const outputDir = path.resolve(process.cwd(), BUILD_DIR, CLIENT_DIR); app.set('trust proxy', 1); // trust first proxy localApis.use( - configRoute(), readyStateRoute(), kubewatchRoute(), metricsRoute() diff --git a/dashboard/server/common/cluster.ts b/dashboard/server/common/cluster.ts index 6ab519df14..fad6991626 100644 --- a/dashboard/server/common/cluster.ts +++ b/dashboard/server/common/cluster.ts @@ -1,9 +1,4 @@ -export interface IClusterConfigMap { - clusterName: string; - clusterUrl: string; -} - -export interface IClusterInfo extends IClusterConfigMap { +export interface IClusterInfo { kubeVersion?: string; - pharosVersion?: string; + clusterName?: string; } diff --git a/dashboard/server/routes/config-route.ts b/dashboard/server/routes/config-route.ts deleted file mode 100644 index 25b8dccbc6..0000000000 --- a/dashboard/server/routes/config-route.ts +++ /dev/null @@ -1,48 +0,0 @@ -//-- Config route - -import config from "../config"; -import { IConfig } from "../common/config" -import { Router } from "express"; -import { userSession } from "../user-session"; -import { getClusterInfo } from "../api/get-cluster-info"; -import { isClusterAdmin } from "../api/is-cluster-admin"; -import { getAllowedNamespaces } from "../api/get-namespaces"; -import { parseJwt } from "../utils/parse-jwt"; - -export function configRoute() { - const router = Router(); - - router.route('/config') - .get(async (req, res) => { - const { username, authHeader } = userSession.get(req); - const authToken = userSession.getToken(req); - - const data: IConfig = { - clusterName: config.KUBE_CLUSTER_NAME, - lensVersion: config.LENS_VERSION, - lensTheme: config.LENS_THEME, - chartsEnabled: !!config.CHARTS_ENABLED, - kubectlAccess: !!req.headers["x-lens-kubectl-token"] - }; - - // load config data from other places - const loading: Promise[] = [ - getClusterInfo().then(info => Object.assign(data, info)), - ]; - - // validate user token from session and fetch more config data - if (authToken) { - const { sub, email } = parseJwt(authToken); - data.username = email || sub || username; - data.token = authToken; - loading.push( - isClusterAdmin({ authHeader }).then(isAdmin => data.isClusterAdmin = isAdmin), - getAllowedNamespaces({ authHeader }).then(list => data.allowedNamespaces = list), - ); - } - await Promise.all(loading); - res.json(data); - }); - - return router; -} diff --git a/dashboard/server/routes/index.ts b/dashboard/server/routes/index.ts index 3b2fdaaf8b..59fa4e1663 100644 --- a/dashboard/server/routes/index.ts +++ b/dashboard/server/routes/index.ts @@ -1,4 +1,3 @@ -export * from "./config-route" export * from "./kubewatch-route" export * from "./metrics-route" export * from "./ready-state-route" diff --git a/src/main/router.ts b/src/main/router.ts index 7093189f2c..39b3d559f1 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -1,5 +1,6 @@ import * as http from "http"; import { Cluster } from "./cluster"; +import { configRoute } from "./routes/config" import { helmApi } from "./helm-api" import { resourceApplierApi } from "./resource-applier-api" import { kubeconfigRoute } from "./routes/kubeconfig" @@ -66,6 +67,7 @@ export class Router { } protected addRoutes() { + this.router.add({ method: 'get', path: '/api/config' }, configRoute.routeConfig.bind(configRoute)) this.router.add({ method: 'get', path: '/api/kubeconfig/service-account/{namespace}/{account}' }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute)) // Helm API diff --git a/src/main/routes/config.ts b/src/main/routes/config.ts new file mode 100644 index 0000000000..28b328c1a7 --- /dev/null +++ b/src/main/routes/config.ts @@ -0,0 +1,66 @@ +import { LensApiRequest } from "../router" +import { LensApi } from "../lens-api" +import { userStore } from "../../common/user-store" +import { getAppVersion } from "../../common/app-utils" +import { CoreV1Api, AuthorizationV1Api } from "@kubernetes/client-node" +import { Cluster } from "../cluster" + + +function selfSubjectAccessReview(authApi: AuthorizationV1Api, namespace: string) { + return authApi.createSelfSubjectAccessReview({ + apiVersion: "authorization.k8s.io/v1", + kind: "SelfSubjectAccessReview", + spec: { + resourceAttributes: { + namespace: namespace, + resource: "pod", + verb: "list", + } + } + }) +} + +async function getAllowedNamespaces(cluster: Cluster) { + const api = cluster.contextHandler.kc.makeApiClient(CoreV1Api) + const authApi = cluster.contextHandler.kc.makeApiClient(AuthorizationV1Api) + try { + const namespaceList = await api.listNamespace() + const nsAccessStatuses = await Promise.all( + namespaceList.body.items.map(ns => { + return selfSubjectAccessReview(authApi, ns.metadata.name) + }) + ) + return namespaceList.body.items + .filter((ns, i) => nsAccessStatuses[i].body.status.allowed) + .map(ns => ns.metadata.name) + } catch(error) { + const kc = cluster.contextHandler.kc + const ctx = kc.getContextObject(kc.currentContext) + if (ctx.namespace) { + return [ctx.namespace] + } else { + return [] + } + } +} + +class ConfigRoute extends LensApi { + + public async routeConfig(request: LensApiRequest) { + const { params, response, cluster} = request + + const data = { + clusterName: cluster.contextName, + lensVersion: getAppVersion(), + lensTheme: `kontena-${userStore.getPreferences().colorTheme}`, + kubeVersion: cluster.version, + chartsEnabled: true, + isClusterAdmin: cluster.isAdmin, + allowedNamespaces: await getAllowedNamespaces(cluster) + }; + + this.respondJson(response, data) + } +} + +export const configRoute = new ConfigRoute()