diff --git a/src/common/rbac.ts b/src/common/rbac.ts new file mode 100644 index 0000000000..667e49c9bc --- /dev/null +++ b/src/common/rbac.ts @@ -0,0 +1,50 @@ +import { getHostedCluster } from "./cluster-store"; + +export type KubeResource = + "namespaces" | "nodes" | "events" | "resourcequotas" | + "services" | "secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumes" | "storageclasses" | + "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" | + "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" + +export interface KubeApiResource { + resource: KubeResource; // valid resource name + group?: string; // api-group +} + +// TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7) +export const apiResources: KubeApiResource[] = [ + { resource: "configmaps" }, + { resource: "cronjobs", group: "batch" }, + { resource: "customresourcedefinitions", group: "apiextensions.k8s.io" }, + { resource: "daemonsets", group: "apps" }, + { resource: "deployments", group: "apps" }, + { resource: "endpoints" }, + { resource: "events" }, + { resource: "horizontalpodautoscalers" }, + { resource: "ingresses", group: "networking.k8s.io" }, + { resource: "jobs", group: "batch" }, + { resource: "namespaces" }, + { resource: "networkpolicies", group: "networking.k8s.io" }, + { resource: "nodes" }, + { resource: "persistentvolumes" }, + { resource: "pods" }, + { resource: "podsecuritypolicies" }, + { resource: "resourcequotas" }, + { resource: "secrets" }, + { resource: "services" }, + { resource: "statefulsets", group: "apps" }, + { resource: "storageclasses", group: "storage.k8s.io" }, +]; + +export function isAllowedResource(resources: KubeResource | KubeResource[]) { + if (!Array.isArray(resources)) { + resources = [resources]; + } + const { allowedResources = [] } = getHostedCluster() || {}; + for (const resource of resources) { + if (!allowedResources.includes(resource)) { + return false; + } + } + return true; +} diff --git a/src/main/cluster.ts b/src/main/cluster.ts index f7b0735039..7ee6c93eb1 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -11,6 +11,7 @@ import { KubeconfigManager } from "./kubeconfig-manager" import { getNodeWarningConditions, loadConfig, podHasIssues } from "../common/kube-helpers" import { getFeatures, installFeature, uninstallFeature, upgradeFeature } from "./feature-manager"; import request, { RequestPromiseOptions } from "request-promise-native" +import { apiResources } from "../common/rbac"; import logger from "./logger" export enum ClusterStatus { @@ -422,30 +423,6 @@ export class Cluster implements ClusterModel { } protected async getAllowedResources() { - // todo: auto-populate all resources dynamically (e.g. kubectl api-resources -o=wide -v=7) - const apiResources = [ - { resource: "configmaps" }, - { resource: "cronjobs", group: "batch" }, - { resource: "customresourcedefinitions", group: "apiextensions.k8s.io" }, - { resource: "daemonsets", group: "apps" }, - { resource: "deployments", group: "apps" }, - { resource: "endpoints" }, - { resource: "events" }, - { resource: "horizontalpodautoscalers" }, - { resource: "ingresses", group: "networking.k8s.io" }, - { resource: "jobs", group: "batch" }, - { resource: "namespaces" }, - { resource: "networkpolicies", group: "networking.k8s.io" }, - { resource: "nodes" }, - { resource: "persistentvolumes" }, - { resource: "pods" }, - { resource: "podsecuritypolicies" }, - { resource: "resourcequotas" }, - { resource: "secrets" }, - { resource: "services" }, - { resource: "statefulsets", group: "apps" }, - { resource: "storageclasses", group: "storage.k8s.io" }, - ] try { if (!this.allowedNamespaces.length) { return []; diff --git a/src/renderer/api/rbac.ts b/src/renderer/api/rbac.ts deleted file mode 100644 index 0490d24d1b..0000000000 --- a/src/renderer/api/rbac.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getHostedCluster } from "../../common/cluster-store"; - -// todo: refactor / move to cluster-store.ts? - -export function isAllowedResource(resources: string | string[]) { - if (!Array.isArray(resources)) { - resources = [resources]; - } - const { allowedResources = [] } = getHostedCluster() || {}; - for (const resource of resources) { - if (!allowedResources.includes(resource)) { - return false; - } - } - return true; -} diff --git a/src/renderer/components/+cluster/cluster.tsx b/src/renderer/components/+cluster/cluster.tsx index 29a661d68f..a5d6e80579 100644 --- a/src/renderer/components/+cluster/cluster.tsx +++ b/src/renderer/components/+cluster/cluster.tsx @@ -13,7 +13,7 @@ import { nodesStore } from "../+nodes/nodes.store"; import { podsStore } from "../+workloads-pods/pods.store"; import { clusterStore } from "./cluster.store"; import { eventStore } from "../+events/event.store"; -import { isAllowedResource } from "../../api/rbac"; +import { isAllowedResource } from "../../../common/rbac"; @observer export class Cluster extends React.Component { diff --git a/src/renderer/components/+config/config.tsx b/src/renderer/components/+config/config.tsx index 71c6ead907..5b6b240a00 100644 --- a/src/renderer/components/+config/config.tsx +++ b/src/renderer/components/+config/config.tsx @@ -10,7 +10,7 @@ import { resourceQuotaRoute, ResourceQuotas, resourceQuotaURL } from "../+config import { configURL } from "./config.route"; import { HorizontalPodAutoscalers, hpaRoute, hpaURL } from "../+config-autoscalers"; import { buildURL } from "../../navigation"; -import { isAllowedResource } from "../../api/rbac" +import { isAllowedResource } from "../../../common/rbac" export const certificatesURL = buildURL("/certificates"); export const issuersURL = buildURL("/issuers"); diff --git a/src/renderer/components/+namespaces/namespace-select.tsx b/src/renderer/components/+namespaces/namespace-select.tsx index 925835ff19..4d4dcc22da 100644 --- a/src/renderer/components/+namespaces/namespace-select.tsx +++ b/src/renderer/components/+namespaces/namespace-select.tsx @@ -11,7 +11,6 @@ import { namespaceStore } from "./namespace.store"; import { _i18n } from "../../i18n"; import { FilterIcon } from "../item-object-list/filter-icon"; import { FilterType } from "../item-object-list/page-filters.store"; -import { isAllowedResource } from "../../api/rbac" interface Props extends SelectProps { showIcons?: boolean; diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index 7f6e22748e..624c823a52 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -4,7 +4,7 @@ import { KubeObjectStore } from "../../kube-object.store"; import { Namespace, namespacesApi } from "../../api/endpoints"; import { IQueryParams, navigation, setQueryParams } from "../../navigation"; import { apiManager } from "../../api/api-manager"; -import { isAllowedResource } from "../..//api/rbac"; +import { isAllowedResource } from "../../../common/rbac"; @autobind() export class NamespaceStore extends KubeObjectStore { diff --git a/src/renderer/components/+network/network.tsx b/src/renderer/components/+network/network.tsx index 7e0cfa99f6..96ea5bc27f 100644 --- a/src/renderer/components/+network/network.tsx +++ b/src/renderer/components/+network/network.tsx @@ -12,7 +12,7 @@ import { Ingresses, ingressRoute, ingressURL } from "../+network-ingresses"; import { NetworkPolicies, networkPoliciesRoute, networkPoliciesURL } from "../+network-policies"; import { namespaceStore } from "../+namespaces/namespace.store"; import { networkURL } from "./network.route"; -import { isAllowedResource } from "../../api/rbac"; +import { isAllowedResource } from "../../../common/rbac"; interface Props extends RouteComponentProps<{}> { } diff --git a/src/renderer/components/+storage/storage.tsx b/src/renderer/components/+storage/storage.tsx index 973c9c35fc..9da302b908 100644 --- a/src/renderer/components/+storage/storage.tsx +++ b/src/renderer/components/+storage/storage.tsx @@ -11,7 +11,7 @@ import { StorageClasses, storageClassesRoute, storageClassesURL } from "../+stor import { PersistentVolumeClaims, volumeClaimsRoute, volumeClaimsURL } from "../+storage-volume-claims"; import { namespaceStore } from "../+namespaces/namespace.store"; import { storageURL } from "./storage.route"; -import { isAllowedResource } from "../../api/rbac"; +import { isAllowedResource } from "../../../common/rbac"; interface Props extends RouteComponentProps<{}> { } diff --git a/src/renderer/components/+user-management/user-management.tsx b/src/renderer/components/+user-management/user-management.tsx index f8caa438eb..cff902c84f 100644 --- a/src/renderer/components/+user-management/user-management.tsx +++ b/src/renderer/components/+user-management/user-management.tsx @@ -11,7 +11,7 @@ import { ServiceAccounts } from "../+user-management-service-accounts"; import { roleBindingsRoute, roleBindingsURL, rolesRoute, rolesURL, serviceAccountsRoute, serviceAccountsURL, usersManagementURL } from "./user-management.routes"; import { namespaceStore } from "../+namespaces/namespace.store"; import { PodSecurityPolicies, podSecurityPoliciesRoute, podSecurityPoliciesURL } from "../+pod-security-policies"; -import { isAllowedResource } from "../../api/rbac"; +import { isAllowedResource } from "../../../common/rbac"; interface Props extends RouteComponentProps<{}> { } diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx index db5c1f53c6..be770cd39a 100644 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx @@ -15,7 +15,7 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import { namespaceStore } from "../+namespaces/namespace.store"; import { PageFiltersList } from "../item-object-list/page-filters-list"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select"; -import { isAllowedResource } from "../../api/rbac"; +import { isAllowedResource } from "../../../common/rbac"; @observer export class OverviewStatuses extends React.Component { diff --git a/src/renderer/components/+workloads-overview/overview.tsx b/src/renderer/components/+workloads-overview/overview.tsx index c3f08d13c2..9dd201cbf2 100644 --- a/src/renderer/components/+workloads-overview/overview.tsx +++ b/src/renderer/components/+workloads-overview/overview.tsx @@ -17,7 +17,7 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import { Spinner } from "../spinner"; import { Events } from "../+events"; import { KubeObjectStore } from "../../kube-object.store"; -import { isAllowedResource } from "../../api/rbac" +import { isAllowedResource } from "../../../common/rbac" interface Props extends RouteComponentProps { } diff --git a/src/renderer/components/+workloads/workloads.tsx b/src/renderer/components/+workloads/workloads.tsx index ebf5c9d348..94d1755eaa 100644 --- a/src/renderer/components/+workloads/workloads.tsx +++ b/src/renderer/components/+workloads/workloads.tsx @@ -15,7 +15,7 @@ import { DaemonSets } from "../+workloads-daemonsets"; import { StatefulSets } from "../+workloads-statefulsets"; import { Jobs } from "../+workloads-jobs"; import { CronJobs } from "../+workloads-cronjobs"; -import { isAllowedResource } from "../../api/rbac" +import { isAllowedResource } from "../../../common/rbac" interface Props extends RouteComponentProps { } diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index ef41b4b7e2..f0180af17c 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -26,7 +26,7 @@ import { PodLogsDialog } from "./+workloads-pods/pod-logs-dialog"; import { DeploymentScaleDialog } from "./+workloads-deployments/deployment-scale-dialog"; import { CustomResources } from "./+custom-resources/custom-resources"; import { crdRoute } from "./+custom-resources"; -import { isAllowedResource } from "../api/rbac"; +import { isAllowedResource } from "../../common/rbac"; import { AddCluster, addClusterRoute } from "./+add-cluster"; import { LandingPage, landingRoute, landingURL } from "./+landing-page"; import { ClusterSettings, clusterSettingsRoute } from "./+cluster-settings"; diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx index 2b43829d8b..54378118b0 100644 --- a/src/renderer/components/layout/sidebar.tsx +++ b/src/renderer/components/layout/sidebar.tsx @@ -27,7 +27,7 @@ import { crdStore } from "../+custom-resources/crd.store"; import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources"; import { CustomResources } from "../+custom-resources/custom-resources"; import { navigation } from "../../navigation"; -import { isAllowedResource } from "../../api/rbac" +import { isAllowedResource } from "../../../common/rbac" const SidebarContext = React.createContext({ pinned: false }); type SidebarContextValue = {