feat(rbac): implemented granular blocking of actions based on permissions on org level ui

This commit is contained in:
Akhil Mohan
2023-09-08 21:21:43 +05:30
parent 34fb7be1c4
commit aac3168c80
41 changed files with 1992 additions and 1652 deletions
+21 -14
View File
@@ -1,21 +1,28 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { OrgGeneralPermissionActions, OrgPermissionSubjects, TOrgPermission } from "@app/context";
import { withPermission } from "@app/hoc";
import { BillingSettingsPage } from "@app/views/Settings/BillingSettingsPage";
export default function SettingsBilling() {
const { t } = useTranslation();
const SettingsBilling = withPermission<{}, TOrgPermission>(
() => {
const { t } = useTranslation();
return (
<div className="h-full bg-bunker-800">
<Head>
<title>{t("common.head-title", { title: t("billing.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<BillingSettingsPage />
</div>
);
}
return (
<div className="h-full bg-bunker-800">
<Head>
<title>{t("common.head-title", { title: t("billing.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<BillingSettingsPage />
</div>
);
},
{ action: OrgGeneralPermissionActions.Delete, subject: OrgPermissionSubjects.Billing }
);
SettingsBilling.requireAuth = true;
Object.assign(SettingsBilling, { requireAuth: true });
export default SettingsBilling;
+333 -301
View File
@@ -31,6 +31,7 @@ import * as Tabs from "@radix-ui/react-tabs";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { OrgPermissionCan } from "@app/components/permissions";
import onboardingCheck from "@app/components/utilities/checks/OnboardingCheck";
import {
Button,
@@ -42,9 +43,22 @@ import {
Skeleton,
UpgradePlanModal
} from "@app/components/v2";
import { useSubscription, useUser, useWorkspace } from "@app/context";
import { fetchOrgUsers, useAddUserToWs, useCreateWorkspace, useRegisterUserAction,useUploadWsKey } from "@app/hooks/api";
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
import {
OrgPermissionSubjects,
OrgWorkspacePermissionActions,
useSubscription,
useUser,
useWorkspace
} from "@app/context";
import { withPermission } from "@app/hoc";
import {
fetchOrgUsers,
useAddUserToWs,
useCreateWorkspace,
useRegisterUserAction,
useUploadWsKey
} from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { encryptAssymmetric } from "../../../../components/utilities/cryptography/crypto";
@@ -301,9 +315,7 @@ const LearningItem = ({
tabIndex={0}
onClick={async () => {
if (userAction && userAction !== "first_time_secrets_pushed") {
await registerUserAction.mutateAsync(
userAction
);
await registerUserAction.mutateAsync(userAction);
}
}}
className={`group relative flex h-[5.5rem] w-full items-center justify-between overflow-hidden rounded-md border ${
@@ -446,19 +458,20 @@ type TAddProjectFormData = yup.InferType<typeof formSchema>;
// #TODO: Update all the workspaceIds
export default function Organization() {
const { t } = useTranslation();
const OrganizationPage = withPermission(
() => {
const { t } = useTranslation();
const router = useRouter();
const router = useRouter();
const { workspaces, isLoading: isWorkspaceLoading } = useWorkspace();
const orgWorkspaces =
workspaces?.filter(
(workspace) => workspace.organization === localStorage.getItem("orgData.id")
) || [];
const currentOrg = String(router.query.id);
const { createNotification } = useNotificationContext();
const addWsUser = useAddUserToWs();
const { workspaces, isLoading: isWorkspaceLoading } = useWorkspace();
const orgWorkspaces =
workspaces?.filter(
(workspace) => workspace.organization === localStorage.getItem("orgData.id")
) || [];
const currentOrg = String(router.query.id);
const { createNotification } = useNotificationContext();
const addWsUser = useAddUserToWs();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"addNewWs",
@@ -537,24 +550,24 @@ export default function Organization() {
? subscription.workspacesUsed < subscription.workspaceLimit
: true;
useEffect(() => {
onboardingCheck({
setHasUserClickedIntro,
setHasUserClickedSlack,
setHasUserPushedSecrets,
setUsersInOrg
});
}, []);
useEffect(() => {
onboardingCheck({
setHasUserClickedIntro,
setHasUserClickedSlack,
setHasUserPushedSecrets,
setUsersInOrg
});
}, []);
const isWorkspaceEmpty = !isWorkspaceLoading && orgWorkspaces?.length === 0;
const isWorkspaceEmpty = !isWorkspaceLoading && orgWorkspaces?.length === 0;
return (
<div className="mx-auto flex max-w-7xl flex-col justify-start bg-bunker-800 md:h-screen">
<Head>
<title>{t("common.head-title", { title: t("settings.members.title") })}</title>
<link rel="icon" href="/infisical.ico" />
</Head>
{!serverDetails?.redisConfigured && <div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
return (
<div className="mx-auto flex max-w-7xl flex-col justify-start bg-bunker-800 md:h-screen">
<Head>
<title>{t("common.head-title", { title: t("settings.members.title") })}</title>
<link rel="icon" href="/infisical.ico" />
</Head>
{!serverDetails?.redisConfigured && <div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
<p className="mr-4 mb-4 font-semibold text-white">Announcements</p>
<div className="w-full border border-blue-400/70 rounded-md bg-blue-900/70 p-2 text-base text-mineshaft-100 flex items-center">
<FontAwesomeIcon icon={faExclamationCircle} className="text-2xl mr-4 p-4 text-mineshaft-50"/>
@@ -566,290 +579,309 @@ export default function Organization() {
</Link>.
</div>
</div>}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
<p className="mr-4 font-semibold text-white">Projects</p>
<div className="mt-6 flex w-full flex-row">
<Input
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
placeholder="Search by project name..."
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
/>
<Button
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
if (isAddingProjectsAllowed) {
handlePopUpOpen("addNewWs");
} else {
handlePopUpOpen("upgradePlan");
}
}}
className="ml-2"
>
Add New Project
</Button>
</div>
<div className="mt-4 grid w-full grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
{isWorkspaceLoading &&
Array.apply(0, Array(3)).map((_x, i) => (
<div
key={`workspace-cards-loading-${i + 1}`}
className="min-w-72 flex h-40 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">
<Skeleton className="w-3/4 bg-mineshaft-600" />
</div>
<div className="mt-0 pb-6 text-sm text-mineshaft-300">
<Skeleton className="w-1/2 bg-mineshaft-600" />
</div>
<div className="flex justify-end">
<Skeleton className="w-1/2 bg-mineshaft-600" />
</div>
</div>
))}
{orgWorkspaces
.filter((ws) => ws?.name?.toLowerCase().includes(searchFilter.toLowerCase()))
.map((workspace) => (
<div
key={workspace._id}
className="min-w-72 flex h-40 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">{workspace.name}</div>
<div className="mt-0 pb-6 text-sm text-mineshaft-300">
{workspace.environments?.length || 0} environments
</div>
<button
type="button"
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
<p className="mr-4 font-semibold text-white">Projects</p>
<div className="mt-6 flex w-full flex-row">
<Input
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"
placeholder="Search by project name..."
value={searchFilter}
onChange={(e) => setSearchFilter(e.target.value)}
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
/>
<OrgPermissionCan
I={OrgWorkspacePermissionActions.Create}
an={OrgPermissionSubjects.Workspace}
>
{(isAllowed) => (
<Button
isDisabled={!isAllowed}
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
router.push(`/project/${workspace._id}/secrets/overview`);
localStorage.setItem("projectData.id", workspace._id);
if (isAddingProjectsAllowed) {
handlePopUpOpen("addNewWs");
} else {
handlePopUpOpen("upgradePlan");
}
}}
className="ml-2"
>
<div className="group ml-auto w-max cursor-pointer rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200">
Explore{" "}
Add New Project
</Button>
)}
</OrgPermissionCan>
</div>
<div className="mt-4 grid w-full grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
{isWorkspaceLoading &&
Array.apply(0, Array(3)).map((_x, i) => (
<div
key={`workspace-cards-loading-${i + 1}`}
className="min-w-72 flex h-40 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">
<Skeleton className="w-3/4 bg-mineshaft-600" />
</div>
<div className="mt-0 pb-6 text-sm text-mineshaft-300">
<Skeleton className="w-1/2 bg-mineshaft-600" />
</div>
<div className="flex justify-end">
<Skeleton className="w-1/2 bg-mineshaft-600" />
</div>
</div>
))}
{orgWorkspaces
.filter((ws) => ws?.name?.toLowerCase().includes(searchFilter.toLowerCase()))
.map((workspace) => (
<div
key={workspace._id}
className="min-w-72 flex h-40 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">{workspace.name}</div>
<div className="mt-0 pb-6 text-sm text-mineshaft-300">
{workspace.environments?.length || 0} environments
</div>
<button
type="button"
onClick={() => {
router.push(`/project/${workspace._id}/secrets/overview`);
localStorage.setItem("projectData.id", workspace._id);
}}
>
<div className="group ml-auto w-max cursor-pointer rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200">
Explore{" "}
<FontAwesomeIcon
icon={faArrowRight}
className="pl-1.5 pr-0.5 duration-200 group-hover:pl-2 group-hover:pr-0"
/>
</div>
</button>
</div>
))}
</div>
{isWorkspaceEmpty && (
<div className="w-full rounded-md border border-mineshaft-700 bg-mineshaft-800 px-4 py-6 text-base text-mineshaft-300">
<FontAwesomeIcon
icon={faFolderOpen}
className="mb-4 mt-2 w-full text-center text-5xl text-mineshaft-400"
/>
<div className="text-center font-light">
You are not part of any projects in this organization yet. When you are, they will
appear here.
</div>
<div className="mt-0.5 text-center font-light">
Create a new project, or ask other organization members to give you necessary
permissions.
</div>
</div>
)}
</div>
{new Date().getTime() - new Date(user?.createdAt).getTime() < 30 * 24 * 60 * 60 * 1000 && (
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
<p className="mr-4 mb-4 font-semibold text-white">Onboarding Guide</p>
<div className="mb-3 grid w-full grid-cols-1 gap-3 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
<LearningItemSquare
text="Watch Infisical demo"
subText="Set up Infisical in 3 min."
complete={hasUserClickedIntro}
icon={faHandPeace}
time="3 min"
userAction="intro_cta_clicked"
link="https://www.youtube.com/watch?v=PK23097-25I"
/>
{orgWorkspaces.length !== 0 && (
<>
<LearningItemSquare
text="Add your secrets"
subText="Drop a .env file or type your secrets."
complete={hasUserPushedSecrets}
icon={faPlus}
time="1 min"
userAction="first_time_secrets_pushed"
link={`/project/${orgWorkspaces[0]?._id}/secrets/overview`}
/>
<LearningItemSquare
text="Invite your teammates"
subText="Infisical is better used as a team."
complete={usersInOrg}
icon={faUserPlus}
time="2 min"
link={`/org/${router.query.id}/members?action=invite`}
/>
</>
)}
<div className="block xl:hidden 2xl:block">
<LearningItemSquare
text="Join Infisical Slack"
subText="Have any questions? Ask us!"
complete={hasUserClickedSlack}
icon={faSlack}
time="1 min"
userAction="slack_cta_clicked"
link="https://infisical.com/slack"
/>
</div>
</div>
{orgWorkspaces.length !== 0 && (
<div className="group relative mb-3 flex h-full w-full cursor-default flex-col items-center justify-between overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-800 pl-2 pr-2 pt-4 pb-2 text-mineshaft-100 shadow-xl duration-200">
<div className="mb-4 flex w-full flex-row items-center pr-4">
<div className="mr-4 flex w-full flex-row items-center">
<FontAwesomeIcon icon={faNetworkWired} className="mx-2 w-16 text-4xl" />
{false && (
<div className="absolute left-12 top-10 flex h-7 w-7 items-center justify-center rounded-full bg-bunker-500 p-2 group-hover:bg-mineshaft-700">
<FontAwesomeIcon
icon={faCheckCircle}
className="h-5 w-5 text-4xl text-green"
/>
</div>
)}
<div className="flex flex-col items-start pl-0.5">
<div className="mt-0.5 text-xl font-semibold">Inject secrets locally</div>
<div className="text-sm font-normal">
Replace .env files with a more secure and efficient alternative.
</div>
</div>
</div>
<div
className={`w-28 pr-4 text-right text-sm font-semibold ${
false && "text-green"
}`}
>
About 2 min
</div>
</div>
<TabsObject />
{false && <div className="absolute bottom-0 left-0 h-1 w-full bg-green" />}
</div>
)}
{orgWorkspaces.length !== 0 && (
<LearningItem
text="Integrate Infisical with your infrastructure"
subText="Connect Infisical to various 3rd party services and platforms."
complete={false}
icon={faPlug}
time="15 min"
link="https://infisical.com/docs/integrations/overview"
/>
)}
</div>
)}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-6 text-3xl">
<p className="mr-4 font-semibold text-white">Explore More</p>
<div
className="mt-4 grid w-full grid-flow-dense gap-4"
style={{ gridTemplateColumns: "repeat(auto-fill, minmax(256px, 4fr))" }}
>
{features.map((feature) => (
<div
key={feature._id}
className="flex h-44 w-96 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">{feature.name}</div>
<div className="mb-4 mt-2 text-[15px] font-light text-mineshaft-300">
{feature.description}
</div>
<div className="flex w-full items-center">
<div className="text-[15px] font-light text-mineshaft-300">
Setup time: 20 min
</div>
<a
target="_blank"
rel="noopener noreferrer"
className="group ml-auto w-max cursor-default rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200"
href="https://infisical.com/docs/documentation/getting-started/kubernetes"
>
Learn more{" "}
<FontAwesomeIcon
icon={faArrowRight}
className="pl-1.5 pr-0.5 duration-200 group-hover:pl-2 group-hover:pr-0"
/>
</div>
</button>
</a>
</div>
</div>
))}
</div>
{isWorkspaceEmpty && (
<div className="w-full rounded-md border border-mineshaft-700 bg-mineshaft-800 px-4 py-6 text-base text-mineshaft-300">
<FontAwesomeIcon
icon={faFolderOpen}
className="mb-4 mt-2 w-full text-center text-5xl text-mineshaft-400"
/>
<div className="text-center font-light">
You are not part of any projects in this organization yet. When you are, they will
appear here.
</div>
<div className="mt-0.5 text-center font-light">
Create a new project, or ask other organization members to give you necessary
permissions.
</div>
</div>
)}
</div>
{new Date().getTime() - new Date(user?.createdAt).getTime() < 30 * 24 * 60 * 60 * 1000 && (
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0 text-3xl">
<p className="mr-4 mb-4 font-semibold text-white">Onboarding Guide</p>
<div className="mb-3 grid w-full grid-cols-1 gap-3 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
<LearningItemSquare
text="Watch Infisical demo"
subText="Set up Infisical in 3 min."
complete={hasUserClickedIntro}
icon={faHandPeace}
time="3 min"
userAction="intro_cta_clicked"
link="https://www.youtube.com/watch?v=PK23097-25I"
/>
{orgWorkspaces.length !== 0 && (
<>
<LearningItemSquare
text="Add your secrets"
subText="Drop a .env file or type your secrets."
complete={hasUserPushedSecrets}
icon={faPlus}
time="1 min"
userAction="first_time_secrets_pushed"
link={`/project/${orgWorkspaces[0]?._id}/secrets/overview`}
/>
<LearningItemSquare
text="Invite your teammates"
subText="Infisical is better used as a team."
complete={usersInOrg}
icon={faUserPlus}
time="2 min"
link={`/org/${router.query.id}/members?action=invite`}
/>
</>
)}
<div className="block xl:hidden 2xl:block">
<LearningItemSquare
text="Join Infisical Slack"
subText="Have any questions? Ask us!"
complete={hasUserClickedSlack}
icon={faSlack}
time="1 min"
userAction="slack_cta_clicked"
link="https://infisical.com/slack"
/>
</div>
</div>
{orgWorkspaces.length !== 0 && (
<div className="group relative mb-3 flex h-full w-full cursor-default flex-col items-center justify-between overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-800 pl-2 pr-2 pt-4 pb-2 text-mineshaft-100 shadow-xl duration-200">
<div className="mb-4 flex w-full flex-row items-center pr-4">
<div className="mr-4 flex w-full flex-row items-center">
<FontAwesomeIcon icon={faNetworkWired} className="mx-2 w-16 text-4xl" />
{false && (
<div className="absolute left-12 top-10 flex h-7 w-7 items-center justify-center rounded-full bg-bunker-500 p-2 group-hover:bg-mineshaft-700">
<FontAwesomeIcon
icon={faCheckCircle}
className="h-5 w-5 text-4xl text-green"
/>
</div>
)}
<div className="flex flex-col items-start pl-0.5">
<div className="mt-0.5 text-xl font-semibold">Inject secrets locally</div>
<div className="text-sm font-normal">
Replace .env files with a more secure and efficient alternative.
</div>
</div>
</div>
<div
className={`w-28 pr-4 text-right text-sm font-semibold ${false && "text-green"}`}
>
About 2 min
</div>
</div>
<TabsObject />
{false && <div className="absolute bottom-0 left-0 h-1 w-full bg-green" />}
</div>
)}
{orgWorkspaces.length !== 0 && (
<LearningItem
text="Integrate Infisical with your infrastructure"
subText="Connect Infisical to various 3rd party services and platforms."
complete={false}
icon={faPlug}
time="15 min"
link="https://infisical.com/docs/integrations/overview"
/>
)}
</div>
)}
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-6 text-3xl">
<p className="mr-4 font-semibold text-white">Explore More</p>
<div
className="mt-4 grid w-full grid-flow-dense gap-4"
style={{ gridTemplateColumns: "repeat(auto-fill, minmax(256px, 4fr))" }}
<Modal
isOpen={popUp.addNewWs.isOpen}
onOpenChange={(isModalOpen) => {
handlePopUpToggle("addNewWs", isModalOpen);
reset();
}}
>
{features.map((feature) => (
<div
key={feature._id}
className="flex h-44 w-96 flex-col justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
>
<div className="mt-0 text-lg text-mineshaft-100">{feature.name}</div>
<div className="mb-4 mt-2 text-[15px] font-light text-mineshaft-300">
{feature.description}
</div>
<div className="flex w-full items-center">
<div className="text-[15px] font-light text-mineshaft-300">Setup time: 20 min</div>
<a
target="_blank"
rel="noopener noreferrer"
className="group ml-auto w-max cursor-default rounded-full border border-mineshaft-600 bg-mineshaft-900 py-2 px-4 text-sm text-mineshaft-300 hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200"
href="https://infisical.com/docs/documentation/getting-started/kubernetes"
>
Learn more{" "}
<FontAwesomeIcon
icon={faArrowRight}
className="pl-1.5 pr-0.5 duration-200 group-hover:pl-2 group-hover:pr-0"
/>
</a>
</div>
</div>
))}
</div>
</div>
<Modal
isOpen={popUp.addNewWs.isOpen}
onOpenChange={(isModalOpen) => {
handlePopUpToggle("addNewWs", isModalOpen);
reset();
}}
>
<ModalContent
title="Create a new project"
subTitle="This project will contain your secrets and configurations."
>
<form onSubmit={handleSubmit(onCreateProject)}>
<Controller
control={control}
name="name"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Project Name"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="Type your project name" />
</FormControl>
)}
/>
<div className="mt-4 pl-1">
<ModalContent
title="Create a new project"
subTitle="This project will contain your secrets and configurations."
>
<form onSubmit={handleSubmit(onCreateProject)}>
<Controller
control={control}
name="addMembers"
defaultValue
render={({ field: { onBlur, value, onChange } }) => (
<Checkbox
id="add-project-layout"
isChecked={value}
onCheckedChange={onChange}
onBlur={onBlur}
name="name"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Project Name"
isError={Boolean(error)}
errorText={error?.message}
>
Add all members of my organization to this project
</Checkbox>
<Input {...field} placeholder="Type your project name" />
</FormControl>
)}
/>
</div>
<div className="mt-7 flex items-center">
<Button
isDisabled={isSubmitting}
isLoading={isSubmitting}
key="layout-create-project-submit"
className="mr-4"
type="submit"
>
Create Project
</Button>
<Button
key="layout-cancel-create-project"
onClick={() => handlePopUpClose("addNewWs")}
variant="plain"
colorSchema="secondary"
>
Cancel
</Button>
</div>
</form>
</ModalContent>
</Modal>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You have exceeded the number of projects allowed on the free plan."
/>
{/* <DeleteUserDialog isOpen={isDeleteOpen} closeModal={closeDeleteModal} submitModal={deleteMembership} userIdToBeDeleted={userIdToBeDeleted}/> */}
</div>
);
}
<div className="mt-4 pl-1">
<Controller
control={control}
name="addMembers"
defaultValue
render={({ field: { onBlur, value, onChange } }) => (
<Checkbox
id="add-project-layout"
isChecked={value}
onCheckedChange={onChange}
onBlur={onBlur}
>
Add all members of my organization to this project
</Checkbox>
)}
/>
</div>
<div className="mt-7 flex items-center">
<Button
isDisabled={isSubmitting}
isLoading={isSubmitting}
key="layout-create-project-submit"
className="mr-4"
type="submit"
>
Create Project
</Button>
<Button
key="layout-cancel-create-project"
onClick={() => handlePopUpClose("addNewWs")}
variant="plain"
colorSchema="secondary"
>
Cancel
</Button>
</div>
</form>
</ModalContent>
</Modal>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You have exceeded the number of projects allowed on the free plan."
/>
{/* <DeleteUserDialog isOpen={isDeleteOpen} closeModal={closeDeleteModal} submitModal={deleteMembership} userIdToBeDeleted={userIdToBeDeleted}/> */}
</div>
);
},
{
action: OrgWorkspacePermissionActions.Read,
subject: OrgPermissionSubjects.Workspace
}
);
Organization.requireAuth = true;
Object.assign(OrganizationPage, { requireAuth: true });
export default OrganizationPage;
@@ -1,89 +1,133 @@
import { useEffect, useState } from "react";
import Head from "next/head";
import { useRouter } from "next/router"
import { useRouter } from "next/router";
import { OrgPermissionCan } from "@app/components/permissions";
import { Button } from "@app/components/v2";
import { OrgGeneralPermissionActions, OrgPermissionSubjects } from "@app/context";
import { withPermission } from "@app/hoc";
import { SecretScanningLogsTable } from "@app/views/SecretScanning/components";
import createNewIntegrationSession from "../../../api/secret-scanning/createSecretScanningSession";
import getInstallationStatus from "../../../api/secret-scanning/getInstallationStatus";
import linkGitAppInstallationWithOrganization from "../../../api/secret-scanning/linkGitAppInstallationWithOrganization";
export default function SecretScanning() {
const router = useRouter()
const queryParams = router.query
const [integrationEnabled, setIntegrationStatus] = useState(false)
const SecretScanning = withPermission(
() => {
const router = useRouter();
const queryParams = router.query;
const [integrationEnabled, setIntegrationStatus] = useState(false);
useEffect(()=>{
const linkInstallation = async () => {
if (typeof queryParams.state === "string" && typeof queryParams.installation_id === "string"){
try {
const isLinked = await linkGitAppInstallationWithOrganization(queryParams.installation_id as string, queryParams.state as string)
if (isLinked){
router.reload()
useEffect(() => {
const linkInstallation = async () => {
if (
typeof queryParams.state === "string" &&
typeof queryParams.installation_id === "string"
) {
try {
const isLinked = await linkGitAppInstallationWithOrganization(
queryParams.installation_id as string,
queryParams.state as string
);
if (isLinked) {
router.reload();
}
console.log("installation verification complete");
} catch (e) {
console.log("app installation is stale, start new session", e);
}
console.log("installation verification complete")
}catch (e){
console.log("app installation is stale, start new session", e)
}
}
}
};
const fetchInstallationStatus = async () => {
const status = await getInstallationStatus(String(localStorage.getItem("orgData.id")))
setIntegrationStatus(status)
}
const fetchInstallationStatus = async () => {
const status = await getInstallationStatus(String(localStorage.getItem("orgData.id")));
setIntegrationStatus(status);
};
fetchInstallationStatus()
linkInstallation()
},[queryParams.state, queryParams.installation_id])
fetchInstallationStatus();
linkInstallation();
}, [queryParams.state, queryParams.installation_id]);
const generateNewIntegrationSession = async () => {
const session = await createNewIntegrationSession(String(localStorage.getItem("orgData.id")))
router.push(`https://github.com/apps/infisical-radar/installations/new?state=${session.sessionId}`)
}
const generateNewIntegrationSession = async () => {
const session = await createNewIntegrationSession(String(localStorage.getItem("orgData.id")));
router.push(
`https://github.com/apps/infisical-radar/installations/new?state=${session.sessionId}`
);
};
return (
<div>
<Head>
<title>Secret scanning</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
<div className="max-w-7xl px-6 w-full">
<div className="mt-6 text-3xl font-semibold text-gray-200">Secret Scanning</div>
<div className="mb-6 text-lg text-mineshaft-300">Automatically monitor your GitHub activity and prevent secret leaks</div>
<div className="relative flex justify-between bg-mineshaft-800 border border-mineshaft-600 rounded-md p-6 mb-6">
<div className="flex flex-col items-start">
<div className="flex flex-row mb-1">Secret Scanning Status: {integrationEnabled ? <p className="text-green ml-1.5 font-semibold">Enabled</p> : <p className="text-red ml-1.5 font-semibold">Not enabled</p>}</div>
<div>{integrationEnabled ? <p className="text-mineshaft-300">Your GitHub organization is connected to Infisical, and is being continuously monitored for secret leaks.</p> : <p className="text-mineshaft-300">Connect your GitHub organization to Infisical.</p>}</div>
return (
<div>
<Head>
<title>Secret scanning</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<div className="flex justify-center bg-bunker-800 text-white w-full h-full">
<div className="max-w-7xl px-6 w-full">
<div className="mt-6 text-3xl font-semibold text-gray-200">Secret Scanning</div>
<div className="mb-6 text-lg text-mineshaft-300">
Automatically monitor your GitHub activity and prevent secret leaks
</div>
{integrationEnabled ? (
<div>
<div className="absolute right-[2.5rem] top-[2.5rem] animate-ping rounded-full h-6 w-6 bg-green flex items-center justify-center"/>
<div className="absolute right-[2.63rem] top-[2.63rem] animate-ping rounded-full h-5 w-5 bg-green flex items-center justify-center"/>
<div className="absolute right-[2.82rem] top-[2.82rem] animate-ping rounded-full h-3.5 w-3.5 bg-green flex items-center justify-center"/>
<div className="relative flex justify-between bg-mineshaft-800 border border-mineshaft-600 rounded-md p-6 mb-6">
<div className="flex flex-col items-start">
<div className="flex flex-row mb-1">
Secret Scanning Status:{" "}
{integrationEnabled ? (
<p className="text-green ml-1.5 font-semibold">Enabled</p>
) : (
<p className="text-red ml-1.5 font-semibold">Not enabled</p>
)}
</div>
<div>
{integrationEnabled ? (
<p className="text-mineshaft-300">
Your GitHub organization is connected to Infisical, and is being continuously
monitored for secret leaks.
</p>
) : (
<p className="text-mineshaft-300">
Connect your GitHub organization to Infisical.
</p>
)}
</div>
</div>
) : (
<div className="flex items-center h-[3.25rem]">
<Button
variant="solid"
colorSchema="primary"
onClick={generateNewIntegrationSession}
className="py-2 h-min"
>
Integrate with GitHub
</Button>
</div>
)}
{integrationEnabled ? (
<div>
<div className="absolute right-[2.5rem] top-[2.5rem] animate-ping rounded-full h-6 w-6 bg-green flex items-center justify-center" />
<div className="absolute right-[2.63rem] top-[2.63rem] animate-ping rounded-full h-5 w-5 bg-green flex items-center justify-center" />
<div className="absolute right-[2.82rem] top-[2.82rem] animate-ping rounded-full h-3.5 w-3.5 bg-green flex items-center justify-center" />
</div>
) : (
<div className="flex items-center h-[3.25rem]">
<OrgPermissionCan
I={OrgGeneralPermissionActions.Create}
a={OrgPermissionSubjects.SecretScanning}
>
{(isAllowed) => (
<Button
variant="solid"
colorSchema="primary"
onClick={generateNewIntegrationSession}
className="py-2 h-min"
isDisabled={!isAllowed}
>
Integrate with GitHub
</Button>
)}
</OrgPermissionCan>
</div>
)}
</div>
<SecretScanningLogsTable />
</div>
<SecretScanningLogsTable />
</div>
</div>
</div>
);
}
);
},
{ action: OrgGeneralPermissionActions.Read, subject: OrgPermissionSubjects.SecretScanning }
);
SecretScanning.requireAuth = true;
Object.assign(SecretScanning, { requireAuth: true });
export default SecretScanning;