Files
prowler/ui/lib/registry/access.server.test.ts
T

178 lines
5.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const { fetchCurrentUserMock } = vi.hoisted(() => ({
fetchCurrentUserMock: vi.fn(),
}));
vi.mock("server-only", () => ({}));
vi.mock("@/lib/auth/current-user", () => ({
fetchCurrentUser: fetchCurrentUserMock,
}));
import { REGISTRY_ACCESS } from "./access";
import {
evaluateRegistryAccess,
evaluateRegistryProviderAccess,
} from "./access.server";
describe("Registry provider onboarding access", () => {
beforeEach(() => {
vi.stubEnv("UI_CLOUD_ENABLED", "true");
vi.stubEnv("UI_REGISTRY_ENABLED", "true");
});
it.each([false, undefined])(
"allows provider managers without Registry permission (%s)",
async (manageRegistry) => {
// Given
fetchCurrentUserMock.mockResolvedValue({
manageRegistry,
permissions: { manage_providers: true },
});
// When / Then
await expect(
evaluateRegistryProviderAccess("access-token"),
).resolves.toEqual({
status: REGISTRY_ACCESS.ELIGIBLE,
});
await expect(evaluateRegistryAccess("access-token")).resolves.not.toEqual(
{
status: REGISTRY_ACCESS.ELIGIBLE,
},
);
},
);
it("denies onboarding to a Registry manager without manage_providers", async () => {
// Given
fetchCurrentUserMock.mockResolvedValue({
manageRegistry: true,
permissions: { manage_providers: false },
});
// When / Then
await expect(
evaluateRegistryProviderAccess("access-token"),
).resolves.toEqual({
status: REGISTRY_ACCESS.INELIGIBLE,
});
await expect(evaluateRegistryAccess("access-token")).resolves.toEqual({
status: REGISTRY_ACCESS.ELIGIBLE,
});
});
it.each([
["false", "true", "access-token"],
["true", "false", "access-token"],
["true", "true", ""],
])(
"requires enabled flags and a token: %j / %j / %j",
async (cloud, flag, token) => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", cloud);
vi.stubEnv("UI_REGISTRY_ENABLED", flag);
// When / Then
await expect(evaluateRegistryProviderAccess(token)).resolves.toEqual({
status: REGISTRY_ACCESS.INELIGIBLE,
});
expect(fetchCurrentUserMock).not.toHaveBeenCalled();
},
);
it("checks current provider permission again after revocation", async () => {
// Given
fetchCurrentUserMock
.mockResolvedValueOnce({ permissions: { manage_providers: true } })
.mockResolvedValueOnce({ permissions: { manage_providers: false } });
// When / Then
await expect(
evaluateRegistryProviderAccess("access-token"),
).resolves.toEqual({
status: REGISTRY_ACCESS.ELIGIBLE,
});
await expect(
evaluateRegistryProviderAccess("access-token"),
).resolves.toEqual({
status: REGISTRY_ACCESS.INELIGIBLE,
});
});
});
describe("evaluateRegistryAccess", () => {
beforeEach(() => {
vi.stubEnv("UI_CLOUD_ENABLED", "true");
vi.stubEnv("UI_REGISTRY_ENABLED", "true");
fetchCurrentUserMock.mockResolvedValue({ manageRegistry: true });
});
it("allows a fresh exact-true current permission without lease metadata", async () => {
// Given / When
const result = await evaluateRegistryAccess("access-token");
// Then
expect(result).toStrictEqual({ status: REGISTRY_ACCESS.ELIGIBLE });
});
it.each([
[undefined, "true", "access-token", true, REGISTRY_ACCESS.INELIGIBLE, 0],
["true", "false", "access-token", true, REGISTRY_ACCESS.INELIGIBLE, 0],
["true", "true", "access-token", false, REGISTRY_ACCESS.INELIGIBLE, 1],
["true", "true", "access-token", undefined, REGISTRY_ACCESS.UNKNOWN, 1],
["true", "true", "", true, REGISTRY_ACCESS.INELIGIBLE, 0],
])(
"fails closed without trusting stale JWT authority",
async (cloud, flag, token, permission, expected, calls) => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", cloud);
vi.stubEnv("UI_REGISTRY_ENABLED", flag);
fetchCurrentUserMock.mockResolvedValue({ manageRegistry: permission });
// When / Then
await expect(evaluateRegistryAccess(token)).resolves.toMatchObject({
status: expected,
});
expect(fetchCurrentUserMock).toHaveBeenCalledTimes(calls);
},
);
it.each([
[" true ", "true"],
["true", " true "],
["\ttrue\n", "\ttrue\n"],
])(
"accepts whitespace around enabled flags: %j / %j",
async (cloud, flag) => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", cloud);
vi.stubEnv("UI_REGISTRY_ENABLED", flag);
// When / Then
await expect(evaluateRegistryAccess("access-token")).resolves.toEqual({
status: REGISTRY_ACCESS.ELIGIBLE,
});
},
);
it("returns unknown for malformed, network, abort, and timeout evidence", async () => {
// Given
fetchCurrentUserMock
.mockResolvedValueOnce({ manageRegistry: undefined })
.mockRejectedValueOnce(new Error("network"))
.mockRejectedValueOnce(new DOMException("aborted", "AbortError"))
.mockImplementationOnce(
(_token, { signal }) =>
new Promise((_, reject) => signal.addEventListener("abort", reject)),
);
// When / Then
const expectUnknown = () =>
expect(evaluateRegistryAccess("access-token")).resolves.toMatchObject({
status: REGISTRY_ACCESS.UNKNOWN,
});
await expectUnknown();
await expectUnknown();
await expectUnknown();
vi.useFakeTimers();
const result = evaluateRegistryAccess("access-token");
await vi.advanceTimersByTimeAsync(5_000);
await expect(result).resolves.toMatchObject({
status: REGISTRY_ACCESS.UNKNOWN,
});
vi.useRealTimers();
});
});