Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions ui/actions/auth/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ vi.mock("@/lib/sentry-breadcrumbs", () => ({

import { createNewUser, getUserByMe } from "./auth";

const userMeResponse = (roleAttributes: Record<string, boolean>) => ({
const userMeResponse = (roleAttributes: Record<string, unknown>) => ({
data: {
type: "users",
id: "019b1234-5678-7abc-9def-0123456789ab",
Expand All @@ -43,7 +43,7 @@ const userMeResponse = (roleAttributes: Record<string, boolean>) => ({
],
});

const mockUserMe = (roleAttributes: Record<string, boolean>) => {
const mockUserMe = (roleAttributes: Record<string, unknown>) => {
fetchMock.mockResolvedValue(
new Response(JSON.stringify(userMeResponse(roleAttributes)), {
status: 200,
Expand Down Expand Up @@ -154,4 +154,29 @@ describe("auth actions", () => {
expect(result.permissions.manage_lighthouse_ai_configuration).toBe(false);
expect(result.permissions.manage_users).toBe(true);
});

it("should carry an exact manage_registry permission into the session", async () => {
// Given
mockUserMe({ manage_registry: true });

// When
const result = await getUserByMe("access-token");

// Then
expect(result.permissions.manage_registry).toBe(true);
});

it.each([undefined, "true", "TRUE", 1])(
"should deny a malformed manage_registry value of %j",
async (manageRegistry) => {
// Given
mockUserMe({ manage_registry: manageRegistry });

// When
const result = await getUserByMe("access-token");

// Then
expect(result.permissions.manage_registry).toBe(false);
},
);
});
1 change: 1 addition & 0 deletions ui/actions/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export const getUserByMe = async (accessToken: string) => {
manage_alerts: userRole.attributes.manage_alerts || false,
manage_lighthouse_ai_configuration:
userRole.attributes.manage_lighthouse_ai_configuration || false,
manage_registry: userRole.attributes.manage_registry === true,
unlimited_visibility: userRole.attributes.unlimited_visibility || false,
};

Expand Down
31 changes: 31 additions & 0 deletions ui/actions/roles/roles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const makeRoleFormData = () => {
formData.set("manage_scans", "false");
formData.set("manage_alerts", "true");
formData.set("manage_lighthouse_ai_configuration", "true");
formData.set("manage_registry", "true");
formData.set("unlimited_visibility", "false");
return formData;
};
Expand All @@ -73,6 +74,36 @@ describe("role actions", () => {
vi.unstubAllEnvs();
});

it("includes manage_registry when creating and updating a role in Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");

// When
await addRole(makeRoleFormData());
const createAttributes = lastRequestBody().data.attributes;
await updateRole(makeRoleFormData(), "role-1");
const updateAttributes = lastRequestBody().data.attributes;

// Then
expect(createAttributes.manage_registry).toBe(true);
expect(updateAttributes.manage_registry).toBe(true);
});

it("omits manage_registry when creating and updating a role outside Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "false");

// When
await addRole(makeRoleFormData());
const createAttributes = lastRequestBody().data.attributes;
await updateRole(makeRoleFormData(), "role-1");
const updateAttributes = lastRequestBody().data.attributes;

// Then
expect(createAttributes).not.toHaveProperty("manage_registry");
expect(updateAttributes).not.toHaveProperty("manage_registry");
});

it("includes manage_alerts when creating a role in Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
Expand Down
4 changes: 4 additions & 0 deletions ui/actions/roles/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export const addRole = async (formData: FormData) => {
formData.get("manage_alerts") === "true";
payload.data.attributes.manage_lighthouse_ai_configuration =
formData.get("manage_lighthouse_ai_configuration") === "true";
payload.data.attributes.manage_registry =
formData.get("manage_registry") === "true";
}

// Add provider groups relationships only if there are items
Expand Down Expand Up @@ -175,6 +177,8 @@ export const updateRole = async (formData: FormData, roleId: string) => {
formData.get("manage_alerts") === "true";
payload.data.attributes.manage_lighthouse_ai_configuration =
formData.get("manage_lighthouse_ai_configuration") === "true";
payload.data.attributes.manage_registry =
formData.get("manage_registry") === "true";
}

// Add provider groups relationships only if there are items
Expand Down
21 changes: 21 additions & 0 deletions ui/auth.config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ const RESTRICTED_PERMISSIONS: RolePermissionAttributes = {
manage_scans: false,
manage_integrations: false,
manage_alerts: false,
manage_registry: false,
unlimited_visibility: false,
};

const ELEVATED_PERMISSIONS: RolePermissionAttributes = {
...RESTRICTED_PERMISSIONS,
manage_users: true,
manage_registry: true,
manage_scans: true,
};

Expand Down Expand Up @@ -141,6 +143,25 @@ describe("authConfig JWT callback", () => {
});
});

it("should default manage_registry to false when a sign-in user omits it", async () => {
// Given
const jwtCallback = authConfig.callbacks?.jwt;
if (!jwtCallback) throw new Error("JWT callback is not configured");

// When
const result = await jwtCallback({
token: {},
account: {} as Parameters<typeof jwtCallback>[0]["account"],
user: {
accessToken: "access-token",
refreshToken: "refresh-token",
} as Parameters<typeof jwtCallback>[0]["user"],
});

// Then
expect(result.user?.permissions.manage_registry).toBe(false);
});

it("should report a tenant switch failure while preserving the current session", async () => {
// Given
vi.spyOn(console, "warn").mockImplementation(() => undefined);
Expand Down
1 change: 1 addition & 0 deletions ui/auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const DEFAULT_PERMISSIONS: RolePermissionAttributes = {
manage_billing: false,
manage_alerts: false,
manage_lighthouse_ai_configuration: false,
manage_registry: false,
unlimited_visibility: false,
};

Expand Down
22 changes: 22 additions & 0 deletions ui/components/roles/workflow/forms/add-role-form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ vi.mock("@/lib", () => ({
description:
"Allows configuring Lighthouse AI, including its provider credentials, default model and business context",
},
{
field: "manage_registry",
label: "Manage Registry",
description: "Allows managing tenant Registry credentials and artifacts",
},
{
field: "manage_billing",
label: "Manage Billing",
Expand Down Expand Up @@ -147,9 +152,25 @@ describe("AddRoleForm", () => {
// Then
expect(screen.queryByText("Manage Alerts")).not.toBeInTheDocument();
expect(screen.queryByText("Manage Lighthouse AI")).not.toBeInTheDocument();
expect(screen.queryByText("Manage Registry")).not.toBeInTheDocument();
expect(screen.queryByText("Manage Billing")).not.toBeInTheDocument();
});

it("submits manage_registry when granted in Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
const user = userEvent.setup();
render(<AddRoleForm groups={[]} />);

// When
await user.type(screen.getByPlaceholderText("Enter role name"), "New role");
await user.click(screen.getByRole("checkbox", { name: "Manage Registry" }));
await user.click(screen.getByRole("button", { name: "Add Role" }));

// Then
expect(submittedFormData().get("manage_registry")).toBe("true");
});

it("submits manage_lighthouse_ai_configuration when granted in Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
Expand Down Expand Up @@ -193,6 +214,7 @@ describe("AddRoleForm", () => {
expect(submittedFormData().has("manage_lighthouse_ai_configuration")).toBe(
false,
);
expect(submittedFormData().has("manage_registry")).toBe(false);
});

it("navigates back to roles when cancel is clicked", async () => {
Expand Down
2 changes: 2 additions & 0 deletions ui/components/roles/workflow/forms/add-role-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const AddRoleForm = ({ groups }: { groups: RoleGroupOption[] }) => {
manage_billing: false,
manage_alerts: false,
manage_lighthouse_ai_configuration: false,
manage_registry: false,
}),
};

Expand Down Expand Up @@ -56,6 +57,7 @@ export const AddRoleForm = ({ groups }: { groups: RoleGroupOption[] }) => {
"manage_lighthouse_ai_configuration",
String(values.manage_lighthouse_ai_configuration),
);
formData.append("manage_registry", String(values.manage_registry));
}

if (values.groups && values.groups.length > 0) {
Expand Down
22 changes: 22 additions & 0 deletions ui/components/roles/workflow/forms/edit-role-form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ vi.mock("@/lib", () => ({
description:
"Allows configuring Lighthouse AI, including its provider credentials, default model and business context",
},
{
field: "manage_registry",
label: "Manage Registry",
description: "Allows managing tenant Registry credentials and artifacts",
},
{
field: "manage_billing",
label: "Manage Billing",
Expand Down Expand Up @@ -97,9 +102,11 @@ beforeAll(() => {

const roleData = ({
manageProviders = false,
manageRegistry = false,
unlimitedVisibility = false,
}: {
manageProviders?: boolean;
manageRegistry?: boolean;
unlimitedVisibility?: boolean;
} = {}) => ({
data: {
Expand All @@ -109,6 +116,7 @@ const roleData = ({
manage_account: false,
manage_providers: manageProviders,
manage_integrations: false,
manage_registry: manageRegistry,
manage_scans: false,
unlimited_visibility: unlimitedVisibility,
groups: [],
Expand Down Expand Up @@ -139,6 +147,19 @@ describe("EditRoleForm", () => {
vi.unstubAllEnvs();
});

it("retains manage_registry when updating a role in Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
const user = userEvent.setup();
renderEditRoleForm({ manageRegistry: true });

// When
await user.click(screen.getByRole("button", { name: "Update Role" }));

// Then
expect(submittedFormData().get("manage_registry")).toBe("true");
});

it("submits manage_lighthouse_ai_configuration when granted in Prowler Cloud", async () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
Expand Down Expand Up @@ -186,6 +207,7 @@ describe("EditRoleForm", () => {
expect(submittedFormData().has("manage_lighthouse_ai_configuration")).toBe(
false,
);
expect(submittedFormData().has("manage_registry")).toBe(false);
});

it("shows the subtle Unlimited Visibility description inside Visibility", () => {
Expand Down
4 changes: 4 additions & 0 deletions ui/components/roles/workflow/forms/edit-role-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export const EditRoleForm = ({

const defaultValues: DefaultValues<RoleFormValues> = {
...roleData.data.attributes,
...(isCloudEnvironment && {
manage_registry: roleData.data.attributes.manage_registry ?? false,
}),
groups:
roleData.data.relationships?.provider_groups?.data.map((g) => g.id) || [],
};
Expand Down Expand Up @@ -62,6 +65,7 @@ export const EditRoleForm = ({
updatedFields.manage_alerts = values.manage_alerts;
updatedFields.manage_lighthouse_ai_configuration =
values.manage_lighthouse_ai_configuration;
updatedFields.manage_registry = values.manage_registry;
}

if (
Expand Down
1 change: 1 addition & 0 deletions ui/hooks/use-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function useAuth() {
manage_billing: false,
manage_alerts: false,
manage_lighthouse_ai_configuration: false,
manage_registry: false,
unlimited_visibility: false,
};

Expand Down
13 changes: 13 additions & 0 deletions ui/lib/helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ describe("getErrorMessage", () => {
});

describe("permissionFormFields", () => {
it("describes Manage Registry", () => {
// Given
const field = permissionFormFields.find(
({ field }) => field === "manage_registry",
);

// When / Then
expect(field).toMatchObject({
label: "Manage Registry",
description: expect.stringContaining("Registry"),
});
});

it("describes Unlimited Visibility as organization-wide", () => {
// Given
const field = permissionFormFields.find(
Expand Down
5 changes: 5 additions & 0 deletions ui/lib/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,11 @@ export const permissionFormFields: PermissionInfo[] = [
description:
"Allows configuring Lighthouse AI, including its provider credentials, default model and business context",
},
{
field: "manage_registry",
label: "Manage Registry",
description: "Allows managing tenant Registry credentials and artifacts",
},

{
field: "manage_billing",
Expand Down
29 changes: 29 additions & 0 deletions ui/lib/permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const attributes = {
manage_billing: false,
manage_alerts: true,
manage_lighthouse_ai_configuration: true,
manage_registry: true,
unlimited_visibility: false,
} satisfies RolePermissionAttributes;

Expand All @@ -21,6 +22,34 @@ describe("getRolePermissions", () => {
vi.unstubAllEnvs();
});

it("includes Manage Registry in Prowler Cloud when role attributes provide it", () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");

// When
const permissions = getRolePermissions(attributes);

// Then
expect(permissions).toContainEqual({
key: "manage_registry",
label: "Manage Registry",
enabled: true,
});
});

it("hides Manage Registry outside Prowler Cloud", () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "false");

// When
const permissions = getRolePermissions(attributes);

// Then
expect(
permissions.some((permission) => permission.key === "manage_registry"),
).toBe(false);
});

it("includes Manage Alerts in Prowler Cloud when role attributes provide it", () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
Expand Down
Loading