mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add behavioural tests for Cluster Menu K8s Resources in Sidebar menu not being shown (#7280)
* Add behavioural tests to cover bug fix Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove previous fix to fix last test Signed-off-by: Sebastian Malton <sebastian@malton.name> * More consistent impl of flushPromises Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove ContextHandler test (dead code) Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix PrometheusHandler describe text Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add useful case test-utils helper Signed-off-by: Sebastian Malton <sebastian@malton.name> * Rename file to match token Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup tests to fix type errors and use tables Signed-off-by: Sebastian Malton <sebastian@malton.name> --------- Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
5409324236
commit
8a80607d85
32 files changed
+1104
-774
No files matched your search
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node";
|
||||
import { AuthorizationV1Api } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
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 type CreateAuthorizationReview = (proxyConfig: KubeConfig) => CanI;
|
||||
|
||||
const createAuthorizationReviewInjectable = getInjectable({
|
||||
id: "authorization-review",
|
||||
instantiate: (di): CreateAuthorizationReview => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default createAuthorizationReviewInjectable;
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { AuthorizationV1Api } from "@kubernetes/client-node";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
export type CreateAuthorizationApi = (config: KubeConfig) => AuthorizationV1Api;
|
||||
|
||||
const createAuthorizationApiInjectable = getInjectable({
|
||||
id: "create-authorization-api",
|
||||
instantiate: (): CreateAuthorizationApi => (config) => config.makeApiClient(AuthorizationV1Api),
|
||||
});
|
||||
|
||||
export default createAuthorizationApiInjectable;
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { AuthorizationV1Api, V1ResourceAttributes } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
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>;
|
||||
|
||||
export type CreateCanI = (api: AuthorizationV1Api) => CanI;
|
||||
|
||||
const createCanIInjectable = getInjectable({
|
||||
id: "create-can-i",
|
||||
instantiate: (di): CreateCanI => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return (api) => 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 default createCanIInjectable;
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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 { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
export type CreateCoreApi = (config: KubeConfig) => CoreV1Api;
|
||||
|
||||
const createCoreApiInjectable = getInjectable({
|
||||
id: "create-core-api",
|
||||
instantiate: (): CreateCoreApi => config => config.makeApiClient(CoreV1Api),
|
||||
});
|
||||
|
||||
export default createCoreApiInjectable;
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { AuthorizationV1Api } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import loggerInjectable from "../logger.injectable";
|
||||
import type { KubeApiResource } from "../rbac";
|
||||
|
||||
export type CanListResource = (resource: KubeApiResource) => boolean;
|
||||
|
||||
/**
|
||||
* Requests the permissions for actions on the kube cluster
|
||||
* @param namespace The namespace of the resources
|
||||
*/
|
||||
export type RequestNamespaceListPermissions = (namespace: string) => Promise<CanListResource>;
|
||||
|
||||
export type CreateRequestNamespaceListPermissions = (api: AuthorizationV1Api) => RequestNamespaceListPermissions;
|
||||
|
||||
const createRequestNamespaceListPermissionsInjectable = getInjectable({
|
||||
id: "create-request-namespace-list-permissions",
|
||||
instantiate: (di): CreateRequestNamespaceListPermissions => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return (api) => async (namespace) => {
|
||||
try {
|
||||
const { body: { status }} = await api.createSelfSubjectRulesReview({
|
||||
apiVersion: "authorization.k8s.io/v1",
|
||||
kind: "SelfSubjectRulesReview",
|
||||
spec: { namespace },
|
||||
});
|
||||
|
||||
if (!status || status.incomplete) {
|
||||
logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
|
||||
|
||||
return () => true;
|
||||
}
|
||||
|
||||
const { resourceRules } = status;
|
||||
|
||||
return (resource) => (
|
||||
resourceRules
|
||||
.filter(({ apiGroups = ["*"] }) => apiGroups.includes("*") || apiGroups.includes(resource.group))
|
||||
.filter(({ resources = ["*"] }) => resources.includes("*") || resources.includes(resource.apiName))
|
||||
.some(({ verbs }) => verbs.includes("*") || verbs.includes("list"))
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error });
|
||||
|
||||
return () => true;
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default createRequestNamespaceListPermissionsInjectable;
|
||||
@@ -2,27 +2,21 @@
|
||||
* 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 { CoreV1Api } from "@kubernetes/client-node";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { isDefined } from "@k8slens/utilities";
|
||||
|
||||
export type ListNamespaces = () => Promise<string[]>;
|
||||
|
||||
export type CreateListNamespaces = (config: KubeConfig) => ListNamespaces;
|
||||
export type CreateListNamespaces = (api: CoreV1Api) => ListNamespaces;
|
||||
|
||||
const createListNamespacesInjectable = getInjectable({
|
||||
id: "create-list-namespaces",
|
||||
instantiate: (): CreateListNamespaces => (config) => {
|
||||
const coreApi = config.makeApiClient(CoreV1Api);
|
||||
instantiate: (): CreateListNamespaces => (api) => async () => {
|
||||
const { body: { items }} = await api.listNamespace();
|
||||
|
||||
return async () => {
|
||||
const { body: { items }} = await coreApi.listNamespace();
|
||||
|
||||
return items
|
||||
.map(ns => ns.metadata?.name)
|
||||
.filter(isDefined);
|
||||
};
|
||||
return items
|
||||
.map(ns => ns.metadata?.name)
|
||||
.filter(isDefined);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* 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 loggerInjectable from "../logger.injectable";
|
||||
import type { KubeApiResource } from "../rbac";
|
||||
|
||||
export type CanListResource = (resource: KubeApiResource) => boolean;
|
||||
|
||||
/**
|
||||
* Requests the permissions for actions on the kube cluster
|
||||
* @param namespace The namespace of the resources
|
||||
*/
|
||||
export type RequestNamespaceListPermissions = (namespace: string) => Promise<CanListResource>;
|
||||
|
||||
/**
|
||||
* @param proxyConfig This config's `currentContext` field must be set, and will be used as the target cluster
|
||||
*/
|
||||
export type RequestNamespaceListPermissionsFor = (proxyConfig: KubeConfig) => RequestNamespaceListPermissions;
|
||||
|
||||
const requestNamespaceListPermissionsForInjectable = getInjectable({
|
||||
id: "request-namespace-list-permissions-for",
|
||||
instantiate: (di): RequestNamespaceListPermissionsFor => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return (proxyConfig) => {
|
||||
const api = proxyConfig.makeApiClient(AuthorizationV1Api);
|
||||
|
||||
return async (namespace) => {
|
||||
try {
|
||||
const { body: { status }} = await api.createSelfSubjectRulesReview({
|
||||
apiVersion: "authorization.k8s.io/v1",
|
||||
kind: "SelfSubjectRulesReview",
|
||||
spec: { namespace },
|
||||
});
|
||||
|
||||
if (!status || status.incomplete) {
|
||||
logger.warn(`[AUTHORIZATION-NAMESPACE-REVIEW]: allowing all resources in namespace="${namespace}" due to incomplete SelfSubjectRulesReview: ${status?.evaluationError}`);
|
||||
|
||||
return () => true;
|
||||
}
|
||||
|
||||
const { resourceRules } = status;
|
||||
|
||||
return (resource) => {
|
||||
const rules = resourceRules.filter(({
|
||||
apiGroups = ["*"],
|
||||
resources = ["*"],
|
||||
}) => {
|
||||
const isAboutRelevantApiGroup = apiGroups.includes("*") || apiGroups.includes(resource.group);
|
||||
const isAboutResource = resources.includes("*") || resources.includes(resource.apiName);
|
||||
|
||||
return isAboutRelevantApiGroup && isAboutResource;
|
||||
});
|
||||
|
||||
return rules.some(({ verbs }) => verbs.includes("*") || verbs.includes("list"));
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`[AUTHORIZATION-NAMESPACE-REVIEW]: failed to create subject rules review`, { namespace, error });
|
||||
|
||||
return () => true;
|
||||
}
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default requestNamespaceListPermissionsForInjectable;
|
||||
@@ -3,334 +3,225 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { V1SubjectRulesReviewStatus } from "@kubernetes/client-node";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import type { AuthorizationV1Api, V1SubjectRulesReviewStatus } from "@kubernetes/client-node";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { IncomingMessage } from "http";
|
||||
import { anyObject } from "jest-mock-extended";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import type { RequestNamespaceListPermissionsFor } from "./request-namespace-list-permissions.injectable";
|
||||
import requestNamespaceListPermissionsForInjectable from "./request-namespace-list-permissions.injectable";
|
||||
import { cast } from "../../test-utils/cast";
|
||||
import type { KubeApiResource } from "../rbac";
|
||||
import type { RequestNamespaceListPermissions } from "./create-request-namespace-list-permissions.injectable";
|
||||
import createRequestNamespaceListPermissionsInjectable from "./create-request-namespace-list-permissions.injectable";
|
||||
|
||||
const createStubProxyConfig = (statusResponse: Promise<{ body: { status: V1SubjectRulesReviewStatus }}>) => ({
|
||||
makeApiClient: () => ({
|
||||
createSelfSubjectRulesReview: (): Promise<{ body: { status: V1SubjectRulesReviewStatus }}> => statusResponse,
|
||||
}),
|
||||
});
|
||||
interface TestCase {
|
||||
description: string;
|
||||
status: V1SubjectRulesReviewStatus;
|
||||
expected: boolean;
|
||||
}
|
||||
|
||||
describe("requestNamespaceListPermissions", () => {
|
||||
let di: DiContainer;
|
||||
let requestNamespaceListPermissions: RequestNamespaceListPermissionsFor;
|
||||
let createSelfSubjectRulesReviewMock: AsyncFnMock<AuthorizationV1Api["createSelfSubjectRulesReview"]>;
|
||||
let requestNamespaceListPermissions: RequestNamespaceListPermissions;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
requestNamespaceListPermissions = di.inject(requestNamespaceListPermissionsForInjectable);
|
||||
|
||||
const createRequestNamespaceListPermissions = di.inject(createRequestNamespaceListPermissionsInjectable);
|
||||
|
||||
createSelfSubjectRulesReviewMock = asyncFn();
|
||||
|
||||
requestNamespaceListPermissions = createRequestNamespaceListPermissions(cast<AuthorizationV1Api>({
|
||||
createSelfSubjectRulesReview: createSelfSubjectRulesReviewMock,
|
||||
}));
|
||||
});
|
||||
|
||||
describe("when api returns incomplete data", () => {
|
||||
it("returns truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: true,
|
||||
resourceRules: [],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
describe("when a request for list permissions in a namespace has been started", () => {
|
||||
let request: ReturnType<RequestNamespaceListPermissions>;
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
beforeEach(() => {
|
||||
request = requestNamespaceListPermissions("irrelevant-namespace");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when api rejects", () => {
|
||||
it("returns truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve, reject) => reject("unknown error")),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
it("should request the creation of a SelfSubjectRulesReview", () => {
|
||||
expect(createSelfSubjectRulesReviewMock).toBeCalledWith(anyObject({
|
||||
spec: {
|
||||
namespace: "irrelevant-namespace",
|
||||
},
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("when first resourceRule has all permissions for everything", () => {
|
||||
it("return truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["*"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
([
|
||||
{
|
||||
description: "incomplete data",
|
||||
status: {
|
||||
incomplete: true,
|
||||
resourceRules: [],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "first resourceRule has all permissions for everything",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["*"],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "first resourceRule has list permissions for everything",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "first resourceRule has list permissions for asked resource",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["some-api-group"],
|
||||
resources: ["some-kind"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "last resourceRule has all permissions for everything",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["*"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "last resourceRule has list permissions for asked resource",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["some-api-group"],
|
||||
resources: ["some-kind"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
description: "resourceRules has matching resource without list verb",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["some-api-group"],
|
||||
resources: ["some-kind"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
description: "resourceRules has no matching resource with list verb",
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: [""],
|
||||
resources: ["services"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
] as TestCase[]).forEach(({ description, status, expected }) => {
|
||||
describe(`when api returns ${description}`, () => {
|
||||
beforeEach(async () => {
|
||||
await createSelfSubjectRulesReviewMock.resolve({
|
||||
body: {
|
||||
status,
|
||||
spec: {},
|
||||
},
|
||||
response: null as unknown as IncomingMessage,
|
||||
});
|
||||
});
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
it(`allows the request to complete, and 'canListResource' will return ${expected}`, async () => {
|
||||
const canListResource = await request;
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
expect(canListResource(someKubeResource)).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when first resourceRule has list permissions for everything", () => {
|
||||
it("return truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
describe("when api rejects", () => {
|
||||
beforeEach(async () => {
|
||||
await createSelfSubjectRulesReviewMock.reject(new Error("unknown error"));
|
||||
});
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
it("allows the request to complete, and 'canListResource' will return true", async () => {
|
||||
const canListResource = await request;
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when first resourceRule has list permissions for asked resource", () => {
|
||||
it("return truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: [""],
|
||||
resources: ["pods"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when last resourceRule has all permissions for everything", () => {
|
||||
it("return truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["*"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when last resourceRule has list permissions for everything", () => {
|
||||
it("return truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when last resourceRule has list permissions for asked resource", () => {
|
||||
it("return truthy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: ["*"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
{
|
||||
apiGroups: [""],
|
||||
resources: ["pods"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when resourceRules has matching resource without list verb", () => {
|
||||
it("return falsy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: [""],
|
||||
resources: ["pods"],
|
||||
verbs: ["get"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when resourceRules has no matching resource with list verb", () => {
|
||||
it("return falsy function", async () => {
|
||||
const requestPermissions = requestNamespaceListPermissions(createStubProxyConfig(
|
||||
new Promise((resolve) => resolve({
|
||||
body: {
|
||||
status: {
|
||||
incomplete: false,
|
||||
resourceRules: [
|
||||
{
|
||||
apiGroups: [""],
|
||||
resources: ["services"],
|
||||
verbs: ["list"],
|
||||
},
|
||||
],
|
||||
nonResourceRules: [],
|
||||
},
|
||||
},
|
||||
})),
|
||||
) as any);
|
||||
|
||||
const permissionCheck = await requestPermissions("irrelevant-namespace");
|
||||
|
||||
expect(permissionCheck({
|
||||
apiName: "pods",
|
||||
group: "",
|
||||
kind: "Pod",
|
||||
namespaced: true,
|
||||
})).toBeFalsy();
|
||||
expect(canListResource(someKubeResource)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const someKubeResource: KubeApiResource = {
|
||||
apiName: "some-kind",
|
||||
group: "some-api-group",
|
||||
kind: "SomeKind",
|
||||
namespaced: true,
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
import copyInjectable from "./copy.injectable";
|
||||
|
||||
export default getGlobalOverride(copyInjectable, () => async () => {
|
||||
throw new Error("tried to copy filepaths without override");
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
import lstatInjectable from "./lstat.injectable";
|
||||
|
||||
export default getGlobalOverride(lstatInjectable, () => async () => {
|
||||
throw new Error("tried to lstat a filepath without override");
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
import readDirectoryInjectable from "./read-directory.injectable";
|
||||
|
||||
export default getGlobalOverride(readDirectoryInjectable, () => async () => {
|
||||
throw new Error("tried to read a directory's content without override");
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
import removePathInjectable from "./remove.injectable";
|
||||
|
||||
export default getGlobalOverride(removePathInjectable, () => async () => {
|
||||
throw new Error("tried to remove path without override");
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
import writeFileInjectable from "./write-file.injectable";
|
||||
|
||||
export default getGlobalOverride(writeFileInjectable, () => async () => {
|
||||
throw new Error("tried to write file without override");
|
||||
});
|
||||
Reference in new issue
Block a user