mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 14:28:56 +00:00
revamp UI for access requests
This commit is contained in:
+77
-30
@@ -6,6 +6,8 @@ import {
|
|||||||
faCheck,
|
faCheck,
|
||||||
faClock,
|
faClock,
|
||||||
faLockOpen,
|
faLockOpen,
|
||||||
|
faPencil,
|
||||||
|
faPlus,
|
||||||
faTrash
|
faTrash
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
@@ -50,6 +52,7 @@ import {
|
|||||||
useDeleteProjectUserAdditionalPrivilege
|
useDeleteProjectUserAdditionalPrivilege
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { TAccessApprovalPolicy } from "@app/hooks/api/types";
|
import { TAccessApprovalPolicy } from "@app/hooks/api/types";
|
||||||
|
import { faEye, faTrashCan } from "@fortawesome/free-regular-svg-icons";
|
||||||
|
|
||||||
const secretPermissionSchema = z.object({
|
const secretPermissionSchema = z.object({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
@@ -205,7 +208,7 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
if (!data.secretPath) {
|
if (!data.secretPath) {
|
||||||
createNotification({
|
createNotification({
|
||||||
type: "error",
|
type: "error",
|
||||||
text: "Please select a secret path",
|
text: "Please select a secret path...",
|
||||||
title: "Error"
|
title: "Error"
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -264,18 +267,18 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4 w-full">
|
<div className="w-full">
|
||||||
<form onSubmit={privilegeForm.handleSubmit(handleSubmit)}>
|
<form onSubmit={privilegeForm.handleSubmit(handleSubmit)}>
|
||||||
<div className={twMerge("flex items-start gap-4", !privilege && "flex-wrap")}>
|
<div className={twMerge("flex items-start", !privilege && "flex-col")}>
|
||||||
<Controller
|
<Controller
|
||||||
control={privilegeForm.control}
|
control={privilegeForm.control}
|
||||||
name="environmentSlug"
|
name="environmentSlug"
|
||||||
render={({ field: { onChange, ...field } }) => (
|
render={({ field: { onChange, ...field } }) => (
|
||||||
<FormControl label="Environment">
|
<FormControl label="Environment" className="w-full">
|
||||||
<Select
|
<Select
|
||||||
{...field}
|
{...field}
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
className="bg-mineshaft-600 hover:bg-mineshaft-500"
|
className="bg-mineshaft-900 hover:bg-mineshaft-800 w-full"
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => onChange(e)}
|
||||||
>
|
>
|
||||||
{currentWorkspace?.environments?.map(({ slug, id, name }) => (
|
{currentWorkspace?.environments?.map(({ slug, id, name }) => (
|
||||||
@@ -297,12 +300,13 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
isDisabled={!!selectablePaths.length}
|
isDisabled={!!selectablePaths.length}
|
||||||
content="The selected environment doesn't have any policies."
|
content="The selected environment doesn't have any policies."
|
||||||
>
|
>
|
||||||
<div>
|
<div className="w-full">
|
||||||
<FormControl label="Secret Path">
|
<FormControl label="Secret Path">
|
||||||
<Select
|
<Select
|
||||||
{...field}
|
{...field}
|
||||||
isDisabled={isMemberEditDisabled || !selectablePaths.length}
|
isDisabled={isMemberEditDisabled || !selectablePaths.length}
|
||||||
className="w-48"
|
className="w-full hover:bg-mineshaft-800"
|
||||||
|
placeholder="Select a secret path"
|
||||||
onValueChange={(e) => field.onChange(e)}
|
onValueChange={(e) => field.onChange(e)}
|
||||||
>
|
>
|
||||||
{selectablePaths.map((path) => (
|
{selectablePaths.map((path) => (
|
||||||
@@ -328,85 +332,127 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-grow justify-between">
|
<FormControl label="Permissions" className="w-full">
|
||||||
|
<div className="flex flex-col w-full justify-between">
|
||||||
|
<div className="flex flex-row gap-2 w-full">
|
||||||
<Controller
|
<Controller
|
||||||
control={privilegeForm.control}
|
control={privilegeForm.control}
|
||||||
name="read"
|
name="read"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex flex-col items-center">
|
<label
|
||||||
<FormLabel label="View" className="mb-4" />
|
className={`group cursor-pointer w-full flex flex-row justify-start items-center gap-2 border border-mineshaft-600 rounded-md p-3 my-1 bg-mineshaft-900 hover:bg-primary/10 hover:border-primary/30 duration-100 ${field.value ? "bg-primary/10 border-primary/50 hover:border-primary/50" : ""}`}
|
||||||
|
htmlFor="secret-read"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
id="secret-read"
|
id="secret-read"
|
||||||
className="h-5 w-5"
|
className={`h-5 w-5 mx-2 ${field.value ? "bg-primary hover:bg-primary/80" : ""}`}
|
||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex flex-col text-mineshaft-300 ml-1">
|
||||||
|
<div className="flex flex-row items-center gap-1">
|
||||||
|
<FontAwesomeIcon icon={faEye} className={`text-sm ${field.value ? "text-primary-200" : ""}`} />
|
||||||
|
<FormLabel label="View" className={`ml-0.5 text-mineshaft-300 mt-0 ${field.value ? "text-primary-200" : ""}`} />
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-mineshaft-400">Read secret values</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={privilegeForm.control}
|
control={privilegeForm.control}
|
||||||
name="create"
|
name="create"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex flex-col items-center">
|
<label
|
||||||
<FormLabel label="Create" className="mb-4" />
|
className={`group cursor-pointer w-full flex flex-row justify-start items-center gap-2 border border-mineshaft-600 rounded-md p-3 my-1 bg-mineshaft-900 hover:bg-primary/10 hover:border-primary/30 duration-100 ${field.value ? "bg-primary/10 border-primary/50 hover:border-primary/50" : ""}`}
|
||||||
|
htmlFor="secret-change"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
id="secret-create"
|
id="secret-change"
|
||||||
className="h-5 w-5"
|
className={`h-5 w-5 mx-2 ${field.value ? "bg-primary hover:bg-primary/80" : ""}`}
|
||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex flex-col text-mineshaft-300 ml-1">
|
||||||
|
<div className="flex flex-row items-center gap-1">
|
||||||
|
<FontAwesomeIcon icon={faPlus} className={`text-sm ${field.value ? "text-primary-200" : ""}`} />
|
||||||
|
<FormLabel label="Create" className={`ml-0.5 text-mineshaft-300 mt-0 ${field.value ? "text-primary-200" : ""}`} />
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-mineshaft-400">Create new secrets</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-2 w-full">
|
||||||
<Controller
|
<Controller
|
||||||
control={privilegeForm.control}
|
control={privilegeForm.control}
|
||||||
name="edit"
|
name="edit"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex flex-col items-center">
|
<label
|
||||||
<FormLabel label="Modify" className="mb-4" />
|
className={`group cursor-pointer w-full flex flex-row justify-start items-center gap-2 border border-mineshaft-600 rounded-md p-3 my-1 bg-mineshaft-900 hover:bg-primary/10 hover:border-primary/30 duration-100 ${field.value ? "bg-primary/10 border-primary/50 hover:border-primary/50" : ""}`}
|
||||||
|
htmlFor="secret-modify"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
id="secret-modify"
|
id="secret-modify"
|
||||||
className="h-5 w-5"
|
className={`h-5 w-5 mx-2 ${field.value ? "bg-primary hover:bg-primary/80" : ""}`}
|
||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex flex-col text-mineshaft-300 ml-1">
|
||||||
|
<div className="flex flex-row items-center gap-1">
|
||||||
|
<FontAwesomeIcon icon={faPencil} className={`text-sm ${field.value ? "text-primary-200" : ""}`} />
|
||||||
|
<FormLabel label="Modify" className={`ml-0.5 text-mineshaft-300 mt-0 ${field.value ? "text-primary-200" : ""}`} />
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-mineshaft-400">Update existing secrets</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={privilegeForm.control}
|
control={privilegeForm.control}
|
||||||
name="delete"
|
name="delete"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex flex-col items-center">
|
<label
|
||||||
<FormLabel label="Delete" className="mb-4" />
|
className={`group cursor-pointer w-full flex flex-row justify-start items-center gap-2 border border-mineshaft-600 rounded-md p-3 my-1 bg-mineshaft-900 hover:bg-primary/10 hover:border-primary/30 duration-100 ${field.value ? "bg-primary/10 border-primary/50 hover:border-primary/50" : ""}`}
|
||||||
|
htmlFor="secret-delete"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
id="secret-delete"
|
id="secret-delete"
|
||||||
className="h-5 w-5"
|
className={`h-5 w-5 mx-2 ${field.value ? "bg-primary hover:bg-primary/80" : ""}`}
|
||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
onCheckedChange={(isChecked) => field.onChange(isChecked)}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex flex-col text-mineshaft-300 ml-1">
|
||||||
|
<div className="flex flex-row items-center gap-1">
|
||||||
|
<FontAwesomeIcon icon={faTrashCan} className={`text-sm ${field.value ? "text-primary-200" : ""}`} />
|
||||||
|
<FormLabel label="Delete" className={`ml-0.5 text-mineshaft-300 mt-0 ${field.value ? "text-primary-200" : ""}`} />
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-mineshaft-400">Delete existing secrets</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex items-center space-x-2">
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl label="Time Period" className="w-full">
|
||||||
|
<div className="mt-1 flex items-center space-x-2 w-full">
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger disabled={isMemberEditDisabled}>
|
<PopoverTrigger disabled={isMemberEditDisabled}>
|
||||||
<div>
|
<div className="w-full">
|
||||||
<Tooltip content={getAccessLabel(true)}>
|
<Tooltip content={getAccessLabel(true)}>
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
||||||
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-4" />}
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"border-none bg-mineshaft-600 py-2.5 text-xs capitalize hover:bg-mineshaft-500",
|
"border-mineshaft-600 text-mineshaft-300 w-full hover:border-mineshaft-600 bg-mineshaft-900 py-2.5 text-sm capitalize hover:bg-mineshaft-800",
|
||||||
isTemporary && "text-primary",
|
|
||||||
isExpired && "text-red-600"
|
isExpired && "text-red-600"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -416,14 +462,14 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
</div>
|
</div>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
arrowClassName="fill-gray-600"
|
arrowClassName="fill-mineshaft-600"
|
||||||
side="right"
|
side="right"
|
||||||
sideOffset={12}
|
sideOffset={12}
|
||||||
hideCloseBtn
|
hideCloseBtn
|
||||||
className="border border-gray-600 pt-4"
|
className="border border-mineshaft-600 pt-4 bg-mineshaft-800"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col space-y-4">
|
<div className="flex flex-col space-y-4">
|
||||||
<div className="border-b border-b-gray-700 pb-2 text-sm text-mineshaft-300">
|
<div className="text-sm text-mineshaft-300">
|
||||||
Configure timed access
|
Configure timed access
|
||||||
</div>
|
</div>
|
||||||
{isExpired && <Tag colorSchema="red">Expired</Tag>}
|
{isExpired && <Tag colorSchema="red">Expired</Tag>}
|
||||||
@@ -544,6 +590,7 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
<div />
|
<div />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4 flex w-full">
|
<div className="mb-4 flex w-full">
|
||||||
<Controller
|
<Controller
|
||||||
@@ -552,7 +599,7 @@ export const SpecificPrivilegeSecretForm = ({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<FormLabel label="Note" className="mb-2" />
|
<FormLabel label="Note" className="mb-2" />
|
||||||
<Input {...field} isDisabled={isMemberEditDisabled} maxLength={255} />
|
<Input {...field} isDisabled={isMemberEditDisabled} maxLength={255} placeholder="Add the reason for this access request..." className="text-mineshaft-300" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+2
-2
@@ -158,8 +158,8 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
|||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"mr-4 h-10 border border-primary",
|
"mr-4 h-10 border",
|
||||||
isDirty && "bg-primary text-black"
|
isDirty && "bg-primary text-black hover:bg-primary hover:opacity-80"
|
||||||
)}
|
)}
|
||||||
isDisabled={isSubmitting || !isDirty}
|
isDisabled={isSubmitting || !isDirty}
|
||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
|
|||||||
+2
-2
@@ -14,9 +14,9 @@ export const RequestAccessModal = ({
|
|||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
className="max-w-4xl"
|
className="max-w-xl"
|
||||||
title="Request Access"
|
title="Request Access"
|
||||||
subTitle="Your role has limited permissions, please contact your administrator to gain access"
|
subTitle="Request access to any secrets and resources based on the predefined policies."
|
||||||
>
|
>
|
||||||
<SpecificPrivilegeSecretForm onClose={() => onOpenChange(false)} policies={policies} />
|
<SpecificPrivilegeSecretForm onClose={() => onOpenChange(false)} policies={policies} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user