Skip to content

Commit

Permalink
fix: getAllowedResources for all namespaces using SelfSubjectRulesRev…
Browse files Browse the repository at this point in the history
…iew (#6614)

* fix: getAllowedResources for all namespaces using SelfSubjectRulesReview

Signed-off-by: Andreas Hippler <andreas.hippler@goto.com>

* fix: refresh accessibility every 15 min

Signed-off-by: Andreas Hippler <andreas.hippler@goto.com>

* chore: remove unused clusterRefreshHandler

Signed-off-by: Andreas Hippler <andreas.hippler@goto.com>

* fix: resolve SelfSubjectRulesReview globs

Signed-off-by: Andreas Hippler <andreas.hippler@goto.com>

Signed-off-by: Andreas Hippler <andreas.hippler@goto.com>
Co-authored-by: Andreas Hippler <andreas.hippler@goto.com>
  • Loading branch information
ahippler and ahippler authored Nov 22, 2022
1 parent 612538d commit 6d7090f
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 109 deletions.
10 changes: 2 additions & 8 deletions integration/__tests__/cluster-pages.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,12 +390,6 @@ const scenarios = [
sidebarItemTestId: "sidebar-item-link-for-service-accounts",
},

{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-roles",
},

{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
Expand All @@ -405,7 +399,7 @@ const scenarios = [
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-role-bindings",
sidebarItemTestId: "sidebar-item-link-for-roles",
},

{
Expand All @@ -417,7 +411,7 @@ const scenarios = [
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-pod-security-policies",
sidebarItemTestId: "sidebar-item-link-for-role-bindings",
},

{
Expand Down
7 changes: 0 additions & 7 deletions src/common/cluster-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,6 @@ export enum ClusterMetricsResourceType {
*/
export const initialNodeShellImage = "docker.io/alpine:3.13";

/**
* The arguments for requesting to refresh a cluster's metadata
*/
export interface ClusterRefreshOptions {
refreshMetadata?: boolean;
}

/**
* The data representing a cluster's state, for passing between main and renderer
*/
Expand Down
87 changes: 87 additions & 0 deletions src/common/cluster/authorization-namespace-review.injectable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/

import type { KubeConfig } from "@kubernetes/client-node";
import { AuthorizationV1Api } from "@kubernetes/client-node";
import { getInjectable } from "@ogre-tools/injectable";
import type { Logger } from "../logger";
import loggerInjectable from "../logger.injectable";
import type { KubeApiResource } from "../rbac";

/**
* Requests the permissions for actions on the kube cluster
* @param namespace The namespace of the resources
* @param availableResources List of available resources in the cluster to resolve glob values fir api groups
* @returns list of allowed resources names
*/
export type RequestNamespaceResources = (namespace: string, availableResources: KubeApiResource[]) => Promise<string[]>;

/**
* @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
*/
export type AuthorizationNamespaceReview = (proxyConfig: KubeConfig) => RequestNamespaceResources;

interface Dependencies {
logger: Logger;
}

const authorizationNamespaceReview = ({ logger }: Dependencies): AuthorizationNamespaceReview => {
return (proxyConfig) => {

const api = proxyConfig.makeApiClient(AuthorizationV1Api);

return async (namespace, availableResources) => {
try {
const { body } = await api.createSelfSubjectRulesReview({
apiVersion: "authorization.k8s.io/v1",
kind: "SelfSubjectRulesReview",
spec: { namespace },
});

const resources = new Set<string>();

body.status?.resourceRules.forEach(resourceRule => {
if (!resourceRule.verbs.some(verb => ["*", "list"].includes(verb)) || !resourceRule.resources) {
return;
}

const apiGroups = resourceRule.apiGroups;

if (resourceRule.resources.length === 1 && resourceRule.resources[0] === "*" && apiGroups) {
if (apiGroups[0] === "*") {
availableResources.forEach(resource => resources.add(resource.apiName));
} else {
availableResources.forEach((apiResource)=> {
if (apiGroups.includes(apiResource.group || "")) {
resources.add(apiResource.apiName);
}
});
}
} else {
resourceRule.resources.forEach(resource => resources.add(resource));
}

});

return [...resources];
} catch (error) {
logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review: ${error}`, { namespace });

return [];
}
};
};
};

const authorizationNamespaceReviewInjectable = getInjectable({
id: "authorization-namespace-review",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);

return authorizationNamespaceReview({ logger });
},
});

export default authorizationNamespaceReviewInjectable;
65 changes: 39 additions & 26 deletions src/common/cluster/authorization-review.injectable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,55 @@

import type { KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node";
import { AuthorizationV1Api } from "@kubernetes/client-node";
import logger from "../logger";
import { getInjectable } from "@ogre-tools/injectable";
import type { Logger } from "../logger";
import loggerInjectable from "../logger.injectable";

/**
* Requests the permissions for actions on the kube cluster
* @param resourceAttributes The descriptor of the action that is desired to be known if it is allowed
* @returns `true` if the actions described are allowed
*/
export type CanI = (resourceAttributes: V1ResourceAttributes) => Promise<boolean>;

/**
* @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
*/
export function authorizationReview(proxyConfig: KubeConfig): CanI {
const api = proxyConfig.makeApiClient(AuthorizationV1Api);

/**
* Requests the permissions for actions on the kube cluster
* @param resourceAttributes The descriptor of the action that is desired to be known if it is allowed
* @returns `true` if the actions described are allowed
*/
return async (resourceAttributes: V1ResourceAttributes): Promise<boolean> => {
try {
const { body } = await api.createSelfSubjectAccessReview({
apiVersion: "authorization.k8s.io/v1",
kind: "SelfSubjectAccessReview",
spec: { resourceAttributes },
});

return body.status?.allowed ?? false;
} catch (error) {
logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${error}`, { resourceAttributes });

return false;
}
};
*/
export type AuthorizationReview = (proxyConfig: KubeConfig) => CanI;

interface Dependencies {
logger: Logger;
}

const authorizationReview = ({ logger }: Dependencies): AuthorizationReview => {
return (proxyConfig) => {
const api = proxyConfig.makeApiClient(AuthorizationV1Api);

return async (resourceAttributes: V1ResourceAttributes): Promise<boolean> => {
try {
const { body } = await api.createSelfSubjectAccessReview({
apiVersion: "authorization.k8s.io/v1",
kind: "SelfSubjectAccessReview",
spec: { resourceAttributes },
});

return body.status?.allowed ?? false;
} catch (error) {
logger.error(`[AUTHORIZATION-REVIEW]: failed to create access review: ${error}`, { resourceAttributes });

return false;
}
};
};
};

const authorizationReviewInjectable = getInjectable({
id: "authorization-review",
instantiate: () => authorizationReview,
instantiate: (di) => {
const logger = di.inject(loggerInjectable);

return authorizationReview({ logger });
},
});

export default authorizationReviewInjectable;
Loading

0 comments on commit 6d7090f

Please sign in to comment.