mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-20 10:23:57 +00:00
feat(rbac): implemented granular blocking of actions based on permissions on org level ui
This commit is contained in:
+79
-94
@@ -3,17 +3,9 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
Modal,
|
||||
ModalContent
|
||||
} from "@app/components/v2";
|
||||
import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import {
|
||||
useAddIncidentContact
|
||||
} from "@app/hooks/api";
|
||||
import { useAddIncidentContact } from "@app/hooks/api";
|
||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -24,97 +16,90 @@ const addContactFormSchema = yup.object({
|
||||
type TAddContactForm = yup.InferType<typeof addContactFormSchema>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["addContact"]>;
|
||||
handlePopUpClose: (popUpName: keyof UsePopUpState<["addContact"]>) => void;
|
||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["addContact"]>, state?: boolean) => void;
|
||||
popUp: UsePopUpState<["addContact"]>;
|
||||
handlePopUpClose: (popUpName: keyof UsePopUpState<["addContact"]>) => void;
|
||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["addContact"]>, state?: boolean) => void;
|
||||
};
|
||||
|
||||
export const AddOrgIncidentContactModal = ({
|
||||
popUp,
|
||||
handlePopUpClose,
|
||||
handlePopUpToggle
|
||||
popUp,
|
||||
handlePopUpClose,
|
||||
handlePopUpToggle
|
||||
}: Props) => {
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { currentOrg } = useOrganization();
|
||||
const { data: serverDetails } = useFetchServerStatus()
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
reset
|
||||
} = useForm<TAddContactForm>({ resolver: yupResolver(addContactFormSchema) });
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { currentOrg } = useOrganization();
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
const { control, handleSubmit, reset } = useForm<TAddContactForm>({
|
||||
resolver: yupResolver(addContactFormSchema)
|
||||
});
|
||||
|
||||
const { mutateAsync, isLoading } = useAddIncidentContact();
|
||||
|
||||
const onFormSubmit = async ({ email }: TAddContactForm) => {
|
||||
try {
|
||||
if (!currentOrg?._id) return;
|
||||
|
||||
await mutateAsync({
|
||||
orgId: currentOrg._id,
|
||||
email
|
||||
});
|
||||
const { mutateAsync, isLoading } = useAddIncidentContact();
|
||||
|
||||
createNotification({
|
||||
text: "Successfully added incident contact",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (serverDetails?.emailConfigured){
|
||||
handlePopUpClose("addContact");
|
||||
}
|
||||
const onFormSubmit = async ({ email }: TAddContactForm) => {
|
||||
try {
|
||||
if (!currentOrg?._id) return;
|
||||
|
||||
reset();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to add incident contact",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
await mutateAsync({
|
||||
orgId: currentOrg._id,
|
||||
email
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully added incident contact",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (serverDetails?.emailConfigured) {
|
||||
handlePopUpClose("addContact");
|
||||
}
|
||||
|
||||
reset();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to add incident contact",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.addContact?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle("addContact", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title="Add an Incident Contact"
|
||||
subTitle="This contact will be notified in the unlikely event of a severe incident."
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.addContact?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle("addContact", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title="Add an Incident Contact"
|
||||
subTitle="This contact will be notified in the unlikely event of a severe incident."
|
||||
>
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="email"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl label="Email" isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center space-x-4">
|
||||
<Button size="sm" type="submit" isLoading={isLoading} isDisabled={isLoading}>
|
||||
Add Incident Contact
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose("addContact")}
|
||||
>
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="email"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl label="Email" isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
size="sm"
|
||||
type="submit"
|
||||
isLoading={isLoading}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
Add Incident Contact
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose("addContact")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
+38
-27
@@ -2,42 +2,53 @@ import { useTranslation } from "react-i18next";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import {
|
||||
Button
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { OrgGeneralPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
|
||||
import { AddOrgIncidentContactModal } from "./AddOrgIncidentContactModal";
|
||||
import { OrgIncidentContactsTable } from "./OrgIncidentContactsTable";
|
||||
|
||||
export const OrgIncidentContactsSection = () => {
|
||||
export const OrgIncidentContactsSection = withPermission(
|
||||
() => {
|
||||
const { t } = useTranslation();
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
"addContact"
|
||||
"addContact"
|
||||
] as const);
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600">
|
||||
<div className="flex justify-between mb-4">
|
||||
<p className="min-w-max text-xl font-semibold">
|
||||
{t("section.incident.incident-contacts")}
|
||||
</p>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("addContact")}
|
||||
>
|
||||
Add contact
|
||||
</Button>
|
||||
</div>
|
||||
<OrgIncidentContactsTable />
|
||||
<AddOrgIncidentContactModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<div className="p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600">
|
||||
<div className="flex justify-between mb-4">
|
||||
<p className="min-w-max text-xl font-semibold">
|
||||
{t("section.incident.incident-contacts")}
|
||||
</p>
|
||||
<OrgPermissionCan
|
||||
I={OrgGeneralPermissionActions.Create}
|
||||
a={OrgPermissionSubjects.IncidentAccount}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
type="submit"
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("addContact")}
|
||||
>
|
||||
Add contact
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<OrgIncidentContactsTable />
|
||||
<AddOrgIncidentContactModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
},
|
||||
{ action: OrgGeneralPermissionActions.Read, subject: OrgPermissionSubjects.IncidentAccount }
|
||||
);
|
||||
|
||||
+16
-7
@@ -3,6 +3,7 @@ import { faContactBook, faMagnifyingGlass, faTrash } from "@fortawesome/free-sol
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
DeleteActionModal,
|
||||
EmptyState,
|
||||
@@ -17,7 +18,7 @@ import {
|
||||
THead,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import { OrgGeneralPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useDeleteIncidentContact, useGetOrgIncidentContact } from "@app/hooks/api";
|
||||
|
||||
@@ -83,13 +84,21 @@ export const OrgIncidentContactsTable = () => {
|
||||
<Tr key={email}>
|
||||
<Td className="w-full">{email}</Td>
|
||||
<Td className="mr-4">
|
||||
<IconButton
|
||||
ariaLabel="delete"
|
||||
colorSchema="danger"
|
||||
onClick={() => handlePopUpOpen("removeContact", { email })}
|
||||
<OrgPermissionCan
|
||||
I={OrgGeneralPermissionActions.Delete}
|
||||
an={OrgPermissionSubjects.IncidentAccount}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
ariaLabel="delete"
|
||||
colorSchema="danger"
|
||||
onClick={() => handlePopUpOpen("removeContact", { email })}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user