diff --git a/src/main/cluster.ts b/src/main/cluster.ts index b6e2c13d4c..a1b4762b24 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -519,20 +519,25 @@ export class Cluster implements ClusterModel, ClusterState { return getClusterResources(await this.getProxyKubeconfig()); }, 60 * 1000); // 1min - private isAllowedCheckers = new ExtendedObservableMap Promise>>(); + private isAllowedCheckers = new ExtendedObservableMap Promise>>(); - private async getIsAllowedResourcesInNamespace(namespace: string): Promise> { + private async getIsAllowedResourcesInNamespace(namespace: string): Promise> { + console.log("getIsAllowedResourcesInNamespace", Date.now()); const groups = await this.getApiResourceMap(); - const isAllowed = new Map(); + const isAllowed = new Set(); for (const group of groups.values()) { for (const versions of group.values()) { for (const resource of versions.keys()) { - isAllowed.set(resource, await this.canI({ - name: resource, + const canList = await this.canI({ + resource, namespace, verb: "list", - })); + }); + + if (canList) { + isAllowed.add(resource); + } } } } @@ -540,7 +545,7 @@ export class Cluster implements ClusterModel, ClusterState { return isAllowed; } - async getIsAllowedResources(namespace: string): Promise> { + async getIsAllowedResources(namespace: string): Promise> { return this.isAllowedCheckers.getOrInsert( namespace, () => asyncThrottle( @@ -566,7 +571,7 @@ export class Cluster implements ClusterModel, ClusterState { const accessReview = await this.canIApiLimit(() => authApi.createSelfSubjectAccessReview({ apiVersion: "authorization.k8s.io/v1", kind: "SelfSubjectAccessReview", - spec: { resourceAttributes } + spec: { resourceAttributes }, })); return accessReview.body.status.allowed; diff --git a/src/main/initializers/ipc.ts b/src/main/initializers/ipc.ts index 1fe05646d4..3b2fb1b729 100644 --- a/src/main/initializers/ipc.ts +++ b/src/main/initializers/ipc.ts @@ -119,19 +119,19 @@ export function initIpcMainHandlers() { ?.getApiResourceMap(); }); - ipcMainHandle(ClusterResourceIsAllowedChannel, async (event, clusterId: ClusterId, namespaces: string[]): Promise<[string, boolean][]> => { + ipcMainHandle(ClusterResourceIsAllowedChannel, async (event, clusterId: ClusterId, namespaces: string[]): Promise => { const cluster = ClusterStore.getInstance().getById(clusterId); if (!cluster) { return []; } - const isAllowed = new Map(); + const isAllowed = new Set(); await Promise.all( namespaces.map(async namespace => { - for (const [resource, canList] of await cluster.getIsAllowedResources(namespace)) { - isAllowed.set(resource, Boolean(isAllowed.get(resource)) || canList); + for (const resource of await cluster.getIsAllowedResources(namespace)) { + isAllowed.add(resource); } }) ); diff --git a/src/renderer/api/allowed-resources.ts b/src/renderer/api/allowed-resources.ts index f33c8829a7..8758f576ab 100644 --- a/src/renderer/api/allowed-resources.ts +++ b/src/renderer/api/allowed-resources.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { action, makeObservable, observable, ObservableMap, reaction } from "mobx"; +import { action, makeObservable, observable, reaction } from "mobx"; import type { ClusterId } from "../../common/cluster-store"; import { ClusterResourceIsAllowedChannel, ClusterGetResourcesChannel, requestMain } from "../../common/ipc"; import { Disposer, Singleton } from "../utils"; @@ -31,7 +31,7 @@ type NamespaceName = string; type ResourceName = string; export class AllowedResources extends Singleton { - protected allowedResourceMap = new ObservableMap(); + protected allowedResources = observable.set(); @observable public resources: ApiResourceMap; /** @@ -65,9 +65,10 @@ export class AllowedResources extends Singleton { ); } + @action private async refresh(namespaces: NamespaceName[]) { try { - this.allowedResourceMap.replace(await requestMain(ClusterResourceIsAllowedChannel, this.clusterId, namespaces)); + this.allowedResources.replace(await requestMain(ClusterResourceIsAllowedChannel, this.clusterId, namespaces)); } catch (error) { console.error("[ALLOWED-RESOURCES]: failed to refresh", error, { namespaces }); Notifications.error("Failed to refresh allowed resources"); @@ -81,7 +82,7 @@ export class AllowedResources extends Singleton { * @returns `true` if the resource exists; is cluster scoped and can be listed, or is namespaced and can be listed in at least one of the namespaces */ isAllowed(name: ResourceName): boolean { - return this.allowedResourceMap.get(name) ?? false; + return this.allowedResources.has(name); } } diff --git a/src/renderer/components/+workloads/workloads.tsx b/src/renderer/components/+workloads/workloads.tsx index c5f7a4949b..a911a82be2 100644 --- a/src/renderer/components/+workloads/workloads.tsx +++ b/src/renderer/components/+workloads/workloads.tsx @@ -39,14 +39,7 @@ import * as routes from "../../../common/routes"; @observer export class Workloads extends React.Component { @computed static get tabRoutes(): TabLayoutRoute[] { - const tabs: TabLayoutRoute[] = [ - { - title: "Overview", - component: WorkloadsOverview, - url: routes.overviewURL(), - routePath: routes.overviewRoute.path.toString() - } - ]; + const tabs: TabLayoutRoute[] = []; if (isAllowedResource("pods")) { tabs.push({ @@ -111,6 +104,15 @@ export class Workloads extends React.Component { }); } + if (tabs.length > 0) { + tabs.unshift({ + title: "Overview", + component: WorkloadsOverview, + url: routes.overviewURL(), + routePath: routes.overviewRoute.path.toString() + }); + } + return tabs; }