Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
153 changes: 153 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 @@ -117,4 +117,157 @@ describe("AddRoleForm", () => {
// Then
expect(routerMocks.push).toHaveBeenCalledWith("/roles");
});

it("explains Unlimited Visibility independently from admin permissions", () => {
// Given / When
render(<AddRoleForm groups={[]} />);

// Then
expect(
screen.getByRole("heading", { name: "Unlimited Visibility" }),
).toBeInTheDocument();
expect(
screen.getByText(
/grants visibility into every provider, account, resource, finding, scan, and compliance result/i,
),
).toBeInTheDocument();
expect(
screen.getByText(
/does not grant admin actions such as managing users, providers, scans, integrations, billing, or alerts/i,
),
).toBeInTheDocument();
expect(
screen.getByText(
/enable it only for roles that need tenant-wide security visibility/i,
),
).toBeInTheDocument();
expect(
screen.getByText(
/manage providers enables unlimited visibility in this form because provider administration needs tenant-wide provider-group context/i,
),
).toBeInTheDocument();
});

it("enables Unlimited Visibility when Manage Providers is selected", async () => {
// Given
const user = userEvent.setup();
render(<AddRoleForm groups={[]} />);

// When
await user.click(
screen.getByRole("checkbox", { name: "Manage Providers" }),
);

// Then
expect(
screen.getByRole("checkbox", {
name: "Enable Unlimited Visibility for this role",
}),
).toBeChecked();
expect(
screen.getByText(
/Manage Providers is selected, so Unlimited Visibility stays enabled in this form/i,
),
).toBeInTheDocument();
});

it("enables Unlimited Visibility through Manage Providers when granting all admin permissions", async () => {
// Given
const user = userEvent.setup();
render(<AddRoleForm groups={[]} />);

// When
await user.click(
screen.getByRole("checkbox", { name: "Grant all admin permissions" }),
);

// Then
expect(
screen.getByRole("checkbox", { name: "Manage Providers" }),
).toBeChecked();
expect(
screen.getByRole("checkbox", {
name: "Enable Unlimited Visibility for this role",
}),
).toBeChecked();
});

it("clears Unlimited Visibility when all admin permissions are toggled off after only auto-enabling it", async () => {
// Given
const user = userEvent.setup();
render(<AddRoleForm groups={[]} />);

// When
await user.click(
screen.getByRole("checkbox", { name: "Grant all admin permissions" }),
);
await user.click(
screen.getByRole("checkbox", { name: "Grant all admin permissions" }),
);

// Then
expect(
screen.getByRole("checkbox", { name: "Manage Providers" }),
).not.toBeChecked();
expect(
screen.getByRole("checkbox", {
name: "Enable Unlimited Visibility for this role",
}),
).not.toBeChecked();
});

it("keeps explicitly enabled Unlimited Visibility when all admin permissions are toggled off", async () => {
// Given
const user = userEvent.setup();
render(<AddRoleForm groups={[]} />);

// When
await user.click(
screen.getByRole("checkbox", {
name: "Enable Unlimited Visibility for this role",
}),
);
await user.click(
screen.getByRole("checkbox", { name: "Grant all admin permissions" }),
);
await user.click(
screen.getByRole("checkbox", { name: "Grant all admin permissions" }),
);

// Then
expect(
screen.getByRole("checkbox", { name: "Manage Providers" }),
).not.toBeChecked();
expect(
screen.getByRole("checkbox", {
name: "Enable Unlimited Visibility for this role",
}),
).toBeChecked();
});

it("does not describe clearing Manage Providers as removing explicitly enabled Unlimited Visibility", async () => {
// Given
const user = userEvent.setup();
render(<AddRoleForm groups={[]} />);

// When
await user.click(
screen.getByRole("checkbox", {
name: "Enable Unlimited Visibility for this role",
}),
);
await user.click(
screen.getByRole("checkbox", { name: "Manage Providers" }),
);

// Then
expect(
screen.getByText(
/Manage Providers is selected, so Unlimited Visibility stays enabled in this form/i,
),
).toBeInTheDocument();
expect(
screen.queryByText(/remove this automatic visibility grant/i),
).not.toBeInTheDocument();
});
});
77 changes: 50 additions & 27 deletions ui/components/roles/workflow/forms/add-role-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
import clsx from "clsx";
import { InfoIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";

Expand All @@ -16,9 +15,16 @@ import { EnhancedMultiSelect } from "@/components/shadcn/select/enhanced-multi-s
import { useToast } from "@/components/ui";
import { CustomInput } from "@/components/ui/custom";
import { Form, FormButtons } from "@/components/ui/form";
import { getErrorMessage, permissionFormFields } from "@/lib";
import { getErrorMessage } from "@/lib";
import { addRoleFormSchema, ApiError } from "@/types";

import {
getUnlimitedVisibilityField,
getVisiblePermissionFormFields,
useManageProvidersUnlimitedVisibility,
} from "./role-permissions";
import { UnlimitedVisibilitySection } from "./unlimited-visibility-section";

type FormValues = z.input<typeof addRoleFormSchema>;

export const AddRoleForm = ({
Expand All @@ -29,11 +35,9 @@ export const AddRoleForm = ({
const { toast } = useToast();
const router = useRouter();
const isCloudEnvironment = process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true";
const visiblePermissionFormFields = permissionFormFields.filter(
(permission) =>
!["manage_billing", "manage_alerts"].includes(permission.field) ||
isCloudEnvironment,
);
const visiblePermissionFormFields =
getVisiblePermissionFormFields(isCloudEnvironment);
const unlimitedVisibilityField = getUnlimitedVisibilityField();

const form = useForm<FormValues>({
resolver: zodResolver(addRoleFormSchema),
Expand All @@ -52,30 +56,18 @@ export const AddRoleForm = ({
},
});

const { watch, setValue } = form;

const manageProviders = watch("manage_providers");
const unlimitedVisibility = watch("unlimited_visibility");

useEffect(() => {
if (manageProviders && !unlimitedVisibility) {
setValue("unlimited_visibility", true, {
shouldValidate: true,
shouldDirty: true,
shouldTouch: true,
});
}
}, [manageProviders, unlimitedVisibility, setValue]);
const {
isUnlimitedVisibilityRequiredByManageProviders,
setPermissionValue,
setUnlimitedVisibility,
} = useManageProvidersUnlimitedVisibility(form);
const unlimitedVisibility = form.watch("unlimited_visibility");

const isLoading = form.formState.isSubmitting;

const onSelectAllChange = (checked: boolean) => {
visiblePermissionFormFields.forEach(({ field }) => {
form.setValue(field as keyof FormValues, checked, {
shouldValidate: true,
shouldDirty: true,
shouldTouch: true,
});
setPermissionValue(field, checked);
});
};

Expand Down Expand Up @@ -160,6 +152,34 @@ export const AddRoleForm = ({
isRequired
/>

{unlimitedVisibilityField && (
Comment thread
HugoPBrito marked this conversation as resolved.
Outdated
<UnlimitedVisibilitySection>
<Checkbox
{...form.register("unlimited_visibility")}
isSelected={!!form.watch("unlimited_visibility")}
isDisabled={isUnlimitedVisibilityRequiredByManageProviders}
onValueChange={setUnlimitedVisibility}
classNames={{
label: "text-small font-medium",
wrapper: "checkbox-update",
}}
color="default"
>
Enable Unlimited Visibility for this role
</Checkbox>
{isUnlimitedVisibilityRequiredByManageProviders && (
<p className="text-small mt-2 text-orange-900 dark:text-orange-100">
Manage Providers is selected, so Unlimited Visibility stays
enabled in this form. If Manage Providers enabled it
automatically, clearing Manage Providers also clears that
automatic selection. If Unlimited Visibility was already
enabled, clearing Manage Providers only lets you edit it
separately.
</p>
)}
</UnlimitedVisibilitySection>
)}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

<div className="flex flex-col gap-4">
<span className="text-lg font-semibold">Admin Permissions</span>

Expand All @@ -168,7 +188,7 @@ export const AddRoleForm = ({
isSelected={visiblePermissionFormFields.every((perm) =>
form.watch(perm.field as keyof FormValues),
)}
onChange={(e) => onSelectAllChange(e.target.checked)}
onValueChange={onSelectAllChange}
classNames={{
label: "text-small",
wrapper: "checkbox-update",
Expand All @@ -186,6 +206,9 @@ export const AddRoleForm = ({
<Checkbox
{...form.register(field as keyof FormValues)}
isSelected={!!form.watch(field as keyof FormValues)}
onValueChange={(checked) =>
setPermissionValue(field, checked)
}
classNames={{
label: "text-small",
wrapper: "checkbox-update",
Expand Down
Loading
Loading