From 97e4338335e3aeeda1879865d4d504d4c99573de Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Wed, 25 Oct 2023 12:17:31 +0530 Subject: [PATCH] feat(secret-rotation): implemented frontend ui for secret rotation --- .../public/images/secretRotation/mysql.png | Bin 0 -> 13007 bytes .../public/images/secretRotation/postgres.png | Bin 0 -> 69342 bytes .../public/images/secretRotation/sendgrid.png | Bin 0 -> 4708 bytes .../src/components/v2/Stepper/Stepper.tsx | 78 ++++ frontend/src/components/v2/Stepper/index.tsx | 2 + frontend/src/components/v2/index.tsx | 1 + .../context/ProjectPermissionContext/types.ts | 4 +- frontend/src/hooks/api/index.tsx | 1 + .../src/hooks/api/secretRotation/index.ts | 6 + .../src/hooks/api/secretRotation/mutation.tsx | 52 +++ .../src/hooks/api/secretRotation/queries.tsx | 110 +++++ .../src/hooks/api/secretRotation/types.ts | 133 ++++++ frontend/src/hooks/api/subscriptions/types.ts | 1 + frontend/src/hooks/api/types.ts | 6 + frontend/src/layouts/AppLayout/AppLayout.tsx | 12 + .../project/[id]/secret-rotation/index.tsx | 23 + .../SecretRotationPage/SecretRotationPage.tsx | 407 ++++++++++++++++++ .../SecretRotationPage.utils.ts | 30 ++ .../CreateRotationForm/CreateRotationForm.tsx | 149 +++++++ .../components/CreateRotationForm/index.tsx | 1 + .../steps/GeneralDetailsForm.tsx | 93 ++++ .../steps/RotationInputForm.tsx | 61 +++ .../steps/RotationOutputForm.tsx | 80 ++++ .../src/views/SecretRotationPage/index.tsx | 1 + 24 files changed, 1250 insertions(+), 1 deletion(-) create mode 100644 frontend/public/images/secretRotation/mysql.png create mode 100644 frontend/public/images/secretRotation/postgres.png create mode 100644 frontend/public/images/secretRotation/sendgrid.png create mode 100644 frontend/src/components/v2/Stepper/Stepper.tsx create mode 100644 frontend/src/components/v2/Stepper/index.tsx create mode 100644 frontend/src/hooks/api/secretRotation/index.ts create mode 100644 frontend/src/hooks/api/secretRotation/mutation.tsx create mode 100644 frontend/src/hooks/api/secretRotation/queries.tsx create mode 100644 frontend/src/hooks/api/secretRotation/types.ts create mode 100644 frontend/src/pages/project/[id]/secret-rotation/index.tsx create mode 100644 frontend/src/views/SecretRotationPage/SecretRotationPage.tsx create mode 100644 frontend/src/views/SecretRotationPage/SecretRotationPage.utils.ts create mode 100644 frontend/src/views/SecretRotationPage/components/CreateRotationForm/CreateRotationForm.tsx create mode 100644 frontend/src/views/SecretRotationPage/components/CreateRotationForm/index.tsx create mode 100644 frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/GeneralDetailsForm.tsx create mode 100644 frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationInputForm.tsx create mode 100644 frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationOutputForm.tsx create mode 100644 frontend/src/views/SecretRotationPage/index.tsx diff --git a/frontend/public/images/secretRotation/mysql.png b/frontend/public/images/secretRotation/mysql.png new file mode 100644 index 0000000000000000000000000000000000000000..d92befdbcc726ba83c1f8cb57b51f0e982f7e7b6 GIT binary patch literal 13007 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 Vfb-MthI7ynapO}s&9Rx~e*<*xtlj_s literal 0 HcmV?d00001 diff --git a/frontend/src/components/v2/Stepper/Stepper.tsx b/frontend/src/components/v2/Stepper/Stepper.tsx new file mode 100644 index 000000000..8eabddaf6 --- /dev/null +++ b/frontend/src/components/v2/Stepper/Stepper.tsx @@ -0,0 +1,78 @@ +import { Children, cloneElement, ReactElement, ReactNode } from "react"; +import { faCheck } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { twMerge } from "tailwind-merge"; + +export type StepperProps = { + activeStep: number; + children: ReactNode; + direction: "vertical" | "horizontal"; + className?: string; +}; + +export const Stepper = ({ activeStep, children, direction, className }: StepperProps) => { + return ( +
+ {Children.map(children as ReactNode, (child: ReactNode, index) => { + const isCompleted = activeStep > index; + const isActive = index === activeStep; + const isNotLast = index + 1 !== (children as Array).length; + return ( +
+
+
+ {isCompleted ? : index + 1} +
+ {cloneElement(child as ReactElement, { + direction, + activeStep, + isCompleted, + isActive + })} +
+ {isNotLast && ( +
+ )} +
+ ); + })} +
+ ); +}; + +export type StepProps = { + title: string; + description?: ReactNode; + // isActive?: boolean; + // isCompleted?: boolean; + // activeStep?: number; + // direction?: "vertical" | "horizontal"; +}; + +export const Step = ({ title, description }: StepProps) => { + return ( +
+
{title}
+ {description &&
{description}
} +
+ ); +}; diff --git a/frontend/src/components/v2/Stepper/index.tsx b/frontend/src/components/v2/Stepper/index.tsx new file mode 100644 index 000000000..b90544cdd --- /dev/null +++ b/frontend/src/components/v2/Stepper/index.tsx @@ -0,0 +1,2 @@ +export type { StepperProps,StepProps } from "./Stepper"; +export { Step,Stepper } from "./Stepper"; diff --git a/frontend/src/components/v2/index.tsx b/frontend/src/components/v2/index.tsx index 31e2b4e57..26af93f37 100644 --- a/frontend/src/components/v2/index.tsx +++ b/frontend/src/components/v2/index.tsx @@ -22,6 +22,7 @@ export * from "./SecretInput"; export * from "./Select"; export * from "./Skeleton"; export * from "./Spinner"; +export * from "./Stepper"; export * from "./Switch"; export * from "./Table"; export * from "./Tabs"; diff --git a/frontend/src/context/ProjectPermissionContext/types.ts b/frontend/src/context/ProjectPermissionContext/types.ts index ae4b49d63..5ae91e756 100644 --- a/frontend/src/context/ProjectPermissionContext/types.ts +++ b/frontend/src/context/ProjectPermissionContext/types.ts @@ -21,7 +21,8 @@ export enum ProjectPermissionSub { Workspace = "workspace", Secrets = "secrets", SecretRollback = "secret-rollback", - SecretApproval = "secret-approval" + SecretApproval = "secret-approval", + SecretRotation = "secret-rotation" } type SubjectFields = { @@ -45,6 +46,7 @@ export type ProjectPermissionSet = | [ProjectPermissionActions, ProjectPermissionSub.Settings] | [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens] | [ProjectPermissionActions, ProjectPermissionSub.SecretApproval] + | [ProjectPermissionActions, ProjectPermissionSub.SecretRotation] | [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace] | [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace] | [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback] diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx index 008f1e2fb..c707fe33f 100644 --- a/frontend/src/hooks/api/index.tsx +++ b/frontend/src/hooks/api/index.tsx @@ -12,6 +12,7 @@ export * from "./secretApproval"; export * from "./secretApprovalRequest"; export * from "./secretFolders"; export * from "./secretImports"; +export * from "./secretRotation"; export * from "./secrets"; export * from "./secretSnapshots"; export * from "./serviceAccounts"; diff --git a/frontend/src/hooks/api/secretRotation/index.ts b/frontend/src/hooks/api/secretRotation/index.ts new file mode 100644 index 000000000..906c8b498 --- /dev/null +++ b/frontend/src/hooks/api/secretRotation/index.ts @@ -0,0 +1,6 @@ +export { + useCreateSecretRotation, + useDeleteSecretRotation, + useRestartSecretRotation +} from "./mutation"; +export { useGetSecretRotationProviders, useGetSecretRotations } from "./queries"; diff --git a/frontend/src/hooks/api/secretRotation/mutation.tsx b/frontend/src/hooks/api/secretRotation/mutation.tsx new file mode 100644 index 000000000..cda4f8074 --- /dev/null +++ b/frontend/src/hooks/api/secretRotation/mutation.tsx @@ -0,0 +1,52 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { secretRotationKeys } from "./queries"; +import { + TCreateSecretRotationDTO, + TDeleteSecretRotationDTO, + TRestartSecretRotationDTO +} from "./types"; + +export const useCreateSecretRotation = () => { + const queryClient = useQueryClient(); + + return useMutation<{}, {}, TCreateSecretRotationDTO>({ + mutationFn: async (dto) => { + const { data } = await apiRequest.post("/api/v1/secret-rotations", dto); + return data; + }, + onSuccess: (_, { workspaceId }) => { + queryClient.invalidateQueries(secretRotationKeys.list({ workspaceId })); + } + }); +}; + +export const useDeleteSecretRotation = () => { + const queryClient = useQueryClient(); + + return useMutation<{}, {}, TDeleteSecretRotationDTO>({ + mutationFn: async (dto) => { + const { data } = await apiRequest.delete(`/api/v1/secret-rotations/${dto.id}`); + return data; + }, + onSuccess: (_, { workspaceId }) => { + queryClient.invalidateQueries(secretRotationKeys.list({ workspaceId })); + } + }); +}; + +export const useRestartSecretRotation = () => { + const queryClient = useQueryClient(); + + return useMutation<{}, {}, TRestartSecretRotationDTO>({ + mutationFn: async (dto) => { + const { data } = await apiRequest.post("/api/v1/secret-rotations/restart", { id: dto.id }); + return data; + }, + onSuccess: (_, { workspaceId }) => { + queryClient.invalidateQueries(secretRotationKeys.list({ workspaceId })); + } + }); +}; diff --git a/frontend/src/hooks/api/secretRotation/queries.tsx b/frontend/src/hooks/api/secretRotation/queries.tsx new file mode 100644 index 000000000..a3c8e270e --- /dev/null +++ b/frontend/src/hooks/api/secretRotation/queries.tsx @@ -0,0 +1,110 @@ +import { useCallback } from "react"; +import { useQuery, UseQueryOptions } from "@tanstack/react-query"; + +import { + decryptAssymmetric, + decryptSymmetric +} from "@app/components/utilities/cryptography/crypto"; +import { apiRequest } from "@app/config/request"; + +import { + TGetSecretRotationList, + TGetSecretRotationProviders, + TSecretRotation, + TSecretRotationProviderList +} from "./types"; + +export const secretRotationKeys = { + listProviders: ({ workspaceId }: TGetSecretRotationProviders) => [ + { workspaceId }, + "secret-rotation-providers" + ], + list: ({ workspaceId }: Omit) => + [{ workspaceId }, "secret-rotations"] as const +}; + +const fetchSecretRotationProviders = async ({ workspaceId }: TGetSecretRotationProviders) => { + const { data } = await apiRequest.get( + `/api/v1/secret-rotation-providers/${workspaceId}` + ); + return data; +}; + +export const useGetSecretRotationProviders = ({ + workspaceId, + options = {} +}: TGetSecretRotationProviders & { + options?: Omit< + UseQueryOptions< + TSecretRotationProviderList, + unknown, + TSecretRotationProviderList, + ReturnType + >, + "queryKey" | "queryFn" + >; +}) => + useQuery({ + ...options, + queryKey: secretRotationKeys.listProviders({ workspaceId }), + enabled: Boolean(workspaceId) && (options?.enabled ?? true), + queryFn: async () => fetchSecretRotationProviders({ workspaceId }) + }); + +const fetchSecretRotations = async ({ + workspaceId +}: Omit) => { + const { data } = await apiRequest.get<{ secretRotations: TSecretRotation[] }>( + "/api/v1/secret-rotations", + { params: { workspaceId } } + ); + return data.secretRotations; +}; + +export const useGetSecretRotations = ({ + workspaceId, + decryptFileKey, + options = {} +}: TGetSecretRotationList & { + options?: Omit< + UseQueryOptions< + TSecretRotation[], + unknown, + TSecretRotation<{ key: string }>[], + ReturnType + >, + "queryKey" | "queryFn" + >; +}) => + useQuery({ + ...options, + queryKey: secretRotationKeys.list({ workspaceId }), + enabled: Boolean(workspaceId) && (options?.enabled ?? true), + queryFn: async () => fetchSecretRotations({ workspaceId }), + select: useCallback( + (data: TSecretRotation[]) => { + const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; + const decryptKey = decryptAssymmetric({ + ciphertext: decryptFileKey.encryptedKey, + nonce: decryptFileKey.nonce, + publicKey: decryptFileKey.sender.publicKey, + privateKey: PRIVATE_KEY + }); + return data.map((el) => ({ + ...el, + outputs: el.outputs.map(({ key, secret }) => ({ + key, + secret: { + key: decryptSymmetric({ + ciphertext: secret.secretValueCiphertext, + iv: secret.secretValueIV, + tag: secret.secretValueTag, + key: decryptKey + }) + } + })) + })); + }, + [decryptFileKey] + ) + }); diff --git a/frontend/src/hooks/api/secretRotation/types.ts b/frontend/src/hooks/api/secretRotation/types.ts new file mode 100644 index 000000000..76d3b5174 --- /dev/null +++ b/frontend/src/hooks/api/secretRotation/types.ts @@ -0,0 +1,133 @@ +import { UserWsKeyPair } from "../keys/types"; +import { EncryptedSecret } from "../secrets/types"; + +export enum TProviderFunctionTypes { + HTTP = "http", + DB = "database" +} + +export enum TDbProviderClients { + // postgres, cockroack db, amazon red shift + Pg = "pg", + // mysql and maria db + Sql = "sql" +} + +export enum TAssignOp { + Direct = "direct", + JmesPath = "jmesopath" +} + +export type TJmesPathAssignOp = { + assign: TAssignOp.JmesPath; + path: string; +}; + +export type TDirectAssignOp = { + assign: TAssignOp.Direct; + value: string; +}; + +export type TAssignFunction = TJmesPathAssignOp | TDirectAssignOp; + +export type THttpProviderFunction = { + type: TProviderFunctionTypes.HTTP; + url: string; + method: string; + header?: Record; + query?: Record; + body?: Record; + setter?: Record; + pre?: Record; +}; + +export type TDbProviderFunction = { + type: TProviderFunctionTypes.DB; + client: TDbProviderClients; + username: string; + password: string; + host: string; + database: string; + port: string; + query: string; + setter?: Record; + pre?: Record; +}; + +export type TProviderFunction = THttpProviderFunction | TDbProviderFunction; + +export type TProviderTemplate = { + inputs: { + properties: Record; + type: "object"; + required: string[]; + }; + outputs: Record; + functions: { + set: TProviderFunction; + remove?: TProviderFunction; + test: TProviderFunction; + }; +}; + +export type TSecretRotation = { + _id: string; + interval: number; + provider: string; + customProvider: string; + workspace: string; + environment: string; + secretPath: string; + outputs: Array<{ + key: string; + secret: T; + }>; + status?: "success" | "failed"; + lastRotatedAt?: string; + statusMessage?: string; + algorithm: string; + keyEncoding: string; +}; + +export type TSecretRotationProvider = { + name: string; + image: string; + title: string; + description: string; + template: TProviderTemplate; +}; + +export type TSecretRotationProviderList = { + custom: TSecretRotationProvider[]; + providers: TSecretRotationProvider[]; +}; + +export type TGetSecretRotationProviders = { + workspaceId: string; +}; + +export type TGetSecretRotationList = { + workspaceId: string; + decryptFileKey: UserWsKeyPair; +}; + +export type TCreateSecretRotationDTO = { + workspaceId: string; + secretPath: string; + environment: string; + interval: number; + provider: string; + customProvider?: string; + inputs: Record; + outputs: Record; +}; + +export type TDeleteSecretRotationDTO = { + id: string; + workspaceId: string; +}; + +export type TRestartSecretRotationDTO = { + id: string; + workspaceId: string; +}; diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts index c61967eec..e38a7527f 100644 --- a/frontend/src/hooks/api/subscriptions/types.ts +++ b/frontend/src/hooks/api/subscriptions/types.ts @@ -12,6 +12,7 @@ export type SubscriptionPlan = { secretVersioning: boolean; slug: string; secretApproval: string; + secretRotation: string; tier: number; workspaceLimit: number; workspacesUsed: number; diff --git a/frontend/src/hooks/api/types.ts b/frontend/src/hooks/api/types.ts index f6131e5b2..422219adf 100644 --- a/frontend/src/hooks/api/types.ts +++ b/frontend/src/hooks/api/types.ts @@ -13,6 +13,12 @@ export type { export { ApprovalStatus, CommitType } from "./secretApprovalRequest/types"; export type { TSecretFolder } from "./secretFolders/types"; export type { TImportedSecrets, TSecretImports } from "./secretImports/types"; +export type { + TGetSecretRotationProviders, + TProviderTemplate, + TSecretRotationProvider, + TSecretRotationProviderList +} from "./secretRotation/types"; export * from "./secrets/types"; export type { CreateServiceTokenDTO, ServiceToken } from "./serviceTokens/types"; export type { SubscriptionPlan } from "./subscriptions/types"; diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx index 04302548e..74f289209 100644 --- a/frontend/src/layouts/AppLayout/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout/AppLayout.tsx @@ -497,6 +497,18 @@ export const AppLayout = ({ children }: LayoutProps) => { + + + + Secret rotation + + + { + const { t } = useTranslation(); + + return ( +
+ + {t("common.head-title", { title: t("settings.project.title") })} + + + + +
+ ); +}; + +export default SecretRotation; + +SecretRotation.requireAuth = true; diff --git a/frontend/src/views/SecretRotationPage/SecretRotationPage.tsx b/frontend/src/views/SecretRotationPage/SecretRotationPage.tsx new file mode 100644 index 000000000..b83e30bde --- /dev/null +++ b/frontend/src/views/SecretRotationPage/SecretRotationPage.tsx @@ -0,0 +1,407 @@ +import { useTranslation } from "react-i18next"; +import { + faArrowsSpin, + faExclamationTriangle, + faFolder, + faInfoCircle, + faRotate, + faTrash +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { formatDistance } from "date-fns"; + +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { ProjectPermissionCan } from "@app/components/permissions"; +import { + Button, + DeleteActionModal, + EmptyState, + IconButton, + Modal, + ModalContent, + Skeleton, + Spinner, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tooltip, + Tr, + UpgradePlanModal +} from "@app/components/v2"; +import { + ProjectPermissionActions, + ProjectPermissionSub, + useProjectPermission, + useSubscription, + useWorkspace +} from "@app/context"; +import { withProjectPermission } from "@app/hoc"; +import { usePopUp } from "@app/hooks"; +import { + useDeleteSecretRotation, + useGetSecretRotationProviders, + useGetSecretRotations, + useGetUserWsKey, + useGetWorkspaceBot, + useRestartSecretRotation, + useUpdateBotActiveStatus +} from "@app/hooks/api"; +import { TSecretRotationProvider } from "@app/hooks/api/types"; + +import { CreateRotationForm } from "./components/CreateRotationForm"; +import { generateBotKey } from "./SecretRotationPage.utils"; + +export const SecretRotationPage = withProjectPermission( + () => { + const { currentWorkspace } = useWorkspace(); + const { t } = useTranslation(); + const permission = useProjectPermission(); + const { createNotification } = useNotificationContext(); + const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([ + "createRotation", + "activeBot", + "deleteRotation", + "upgradePlan" + ] as const); + const workspaceId = currentWorkspace?._id || ""; + const canCreateRotation = permission.can( + ProjectPermissionActions.Create, + ProjectPermissionSub.SecretRotation + ); + const { subscription } = useSubscription(); + + const { data: userWsKey } = useGetUserWsKey(workspaceId); + + const { data: secretRotationProviders, isLoading: isRotationProviderLoading } = + useGetSecretRotationProviders({ workspaceId }); + const { data: secretRotations, isLoading: isRotationLoading } = useGetSecretRotations({ + workspaceId, + decryptFileKey: userWsKey! + }); + + const { + mutateAsync: deleteSecretRotation, + variables: deleteSecretRotationVars, + isLoading: isDeletingRotation + } = useDeleteSecretRotation(); + const { + mutateAsync: restartSecretRotation, + variables: restartSecretRotationVar, + isLoading: isRestartingRotation + } = useRestartSecretRotation(); + + const { data: bot } = useGetWorkspaceBot(workspaceId); + const { mutateAsync: updateBotActiveStatus } = useUpdateBotActiveStatus(); + + const isBotActive = Boolean(bot?.isActive); + + const handleDeleteRotation = async () => { + const { id } = popUp.deleteRotation.data as { id: string }; + try { + await deleteSecretRotation({ + id, + workspaceId + }); + handlePopUpClose("deleteRotation"); + createNotification({ + type: "success", + text: "Successfully removed rotation" + }); + } catch (error) { + console.log(error); + createNotification({ + type: "error", + text: "Failed to remove rotation" + }); + } + }; + + const handleRestartRotation = async (id: string) => { + try { + await restartSecretRotation({ + id, + workspaceId + }); + createNotification({ + type: "success", + text: "Secret rotation initiated" + }); + } catch (error) { + console.log(error); + createNotification({ + type: "error", + text: "Failed to restart rotation" + }); + } + }; + + const handleUserAcceptBotCondition = async () => { + const provider = popUp.activeBot?.data as TSecretRotationProvider; + try { + if (bot?._id) { + const botKey = generateBotKey(bot.publicKey, userWsKey!); + await updateBotActiveStatus({ + isActive: true, + botId: bot._id, + workspaceId, + botKey + }); + } + handlePopUpOpen("createRotation", provider); + handlePopUpClose("activeBot"); + } catch (error) { + console.log(error); + createNotification({ + type: "error", + text: "Failed to create bot" + }); + } + }; + + const handleCreateRotation = async (provider: TSecretRotationProvider) => { + if (subscription && !subscription?.secretRotation) { + handlePopUpOpen("upgradePlan"); + return; + } + if (!canCreateRotation) { + createNotification({ type: "error", text: "Access permission denied!!" }); + return; + } + if (isBotActive) { + handlePopUpOpen("createRotation", provider); + } else { + handlePopUpOpen("activeBot", provider); + } + }; + + return ( +
+
+

Secret Rotation

+

Auto rotate secrets for better security

+
+
+
Rotated Secrets
+
+ + + + + + + + + + + + + + {isRotationLoading && ( + + )} + {!isRotationLoading && secretRotations?.length === 0 && ( + + + + )} + {secretRotations?.map( + ({ + environment, + secretPath, + outputs, + provider, + _id, + lastRotatedAt, + status, + statusMessage + }) => { + const isDeleting = deleteSecretRotationVars?.id === _id && isDeletingRotation; + const isRestarting = + restartSecretRotationVar?.id === _id && isRestartingRotation; + return ( + + + + + + + + + ); + } + )} + +
Secret NameEnvironmentProviderStatusLast RotationAction
+ +
+ {outputs + .map(({ key }) => key) + .join(",") + .toUpperCase()} + +
+
{environment}
+
+ + {secretPath} +
+
+
{provider} +
+ {status} + {status === "failed" && ( + + + + )} +
+
+ {lastRotatedAt + ? formatDistance(new Date(lastRotatedAt), new Date()) + : "-"} + +
+ + {(isAllowed) => ( + handleRestartRotation(_id)} + > + {isRestarting ? ( + + ) : ( + + )} + + )} + + + {(isAllowed) => ( + handlePopUpOpen("deleteRotation", { id: _id })} + > + {isDeleting ? ( + + ) : ( + + )} + + )} + +
+
+
+
+
+
Infisical Rotation Providers
+
+ {isRotationProviderLoading && + Array.from({ length: 12 }).map((_, index) => ( + + ))} + {!isRotationProviderLoading && + secretRotationProviders?.providers.map((provider) => ( +
{ + if (evt.key === "Enter") handlePopUpOpen("createRotation", provider); + }} + onClick={() => handleCreateRotation(provider)} + > + rotation provider logo +
+ {provider.title} +
+
+ + + +
+
+ ))} +
+ handlePopUpToggle("createRotation", isOpen)} + provider={(popUp.createRotation.data as TSecretRotationProvider) || {}} + /> + handlePopUpToggle("activeBot", isOpen)} + > + + + +
+ } + > + {t("integrations.why-infisical-needs-access")} + + + handlePopUpToggle("deleteRotation", isOpen)} + deleteKey="confirm" + onDeleteApproved={handleDeleteRotation} + /> + handlePopUpToggle("upgradePlan", isOpen)} + text="You can add secret rotation if you switch to Infisical's Team plan." + /> +
+ ); + }, + { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.SecretRotation } +); diff --git a/frontend/src/views/SecretRotationPage/SecretRotationPage.utils.ts b/frontend/src/views/SecretRotationPage/SecretRotationPage.utils.ts new file mode 100644 index 000000000..6dd346f8e --- /dev/null +++ b/frontend/src/views/SecretRotationPage/SecretRotationPage.utils.ts @@ -0,0 +1,30 @@ +import { UserWsKeyPair } from "@app/hooks/api/types"; + +import { + decryptAssymmetric, + encryptAssymmetric +} from "../../components/utilities/cryptography/crypto"; + +// refactor these to common function in frontend +export const generateBotKey = (botPublicKey: string, latestKey: UserWsKeyPair) => { + const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY"); + + if (!PRIVATE_KEY) { + throw new Error("Private Key missing"); + } + + const WORKSPACE_KEY = decryptAssymmetric({ + ciphertext: latestKey.encryptedKey, + nonce: latestKey.nonce, + publicKey: latestKey.sender.publicKey, + privateKey: PRIVATE_KEY + }); + + const { ciphertext, nonce } = encryptAssymmetric({ + plaintext: WORKSPACE_KEY, + publicKey: botPublicKey, + privateKey: PRIVATE_KEY + }); + + return { encryptedKey: ciphertext, nonce }; +}; diff --git a/frontend/src/views/SecretRotationPage/components/CreateRotationForm/CreateRotationForm.tsx b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/CreateRotationForm.tsx new file mode 100644 index 000000000..c5493159e --- /dev/null +++ b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/CreateRotationForm.tsx @@ -0,0 +1,149 @@ +import { useRef, useState } from "react"; +import { AnimatePresence, motion } from "framer-motion"; + +import { Modal, ModalContent, Step, Stepper } from "@app/components/v2"; +import { useCreateSecretRotation } from "@app/hooks/api"; +import { TSecretRotationProvider } from "@app/hooks/api/types"; + +import { useNotificationContext } from "~/components/context/Notifications/NotificationProvider"; + +import { GeneralDetailsForm, TFormSchema as TGeneralFormSchema } from "./steps/GeneralDetailsForm"; +import { RotationInputForm } from "./steps/RotationInputForm"; +import { + RotationOutputForm, + TFormSchema as TRotationOutputSchema +} from "./steps/RotationOutputForm"; + +const WIZARD_STEPS = [ + { + title: "General" + }, + { + title: "Inputs" + }, + { + title: "Secret Mapping" + } +]; + +type Props = { + isOpen?: boolean; + onToggle: (isOpen: boolean) => void; + customProvider?: string; + workspaceId: string; + provider: TSecretRotationProvider; +}; + +export const CreateRotationForm = ({ + isOpen, + onToggle, + provider, + workspaceId, + customProvider +}: Props) => { + const [wizardStep, setWizardStep] = useState(0); + const wizardData = useRef<{ + general?: TGeneralFormSchema; + input?: Record; + output?: TRotationOutputSchema; + }>({}); + const { createNotification } = useNotificationContext(); + + const { mutateAsync: createSecretRotation } = useCreateSecretRotation(); + + const handleFormCancel = () => { + onToggle(false); + setWizardStep(0); + wizardData.current = {}; + }; + + const handleFormSubmit = async () => { + if (!wizardData.current.general || !wizardData.current.input || !wizardData.current.output) + return; + try { + await createSecretRotation({ + workspaceId, + provider: provider.name, + customProvider, + secretPath: wizardData.current.general.secretPath, + environment: wizardData.current.general.environment, + interval: wizardData.current.general.interval, + inputs: wizardData.current.input, + outputs: wizardData.current.output + }); + setWizardStep(0); + onToggle(false); + wizardData.current = {}; + } catch (error) { + console.log(error); + createNotification({ + type: "error", + text: "Failed to create secret rotation" + }); + } + }; + + return ( + { + onToggle(state); + setWizardStep(0); + wizardData.current = {}; + }} + > + + + {WIZARD_STEPS.map(({ title }, index) => ( + + ))} + + + {wizardStep === 0 && ( + + { + wizardData.current.general = data; + setWizardStep((state) => state + 1); + }} + /> + + )} + {wizardStep === 1 && ( + { + wizardData.current.input = data; + setWizardStep((state) => state + 1); + }} + inputSchema={provider.template?.inputs || {}} + /> + )} + {wizardStep === 2 && ( + { + wizardData.current.output = data; + await handleFormSubmit(); + }} + /> + )} + + + + ); +}; diff --git a/frontend/src/views/SecretRotationPage/components/CreateRotationForm/index.tsx b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/index.tsx new file mode 100644 index 000000000..8392c4cdb --- /dev/null +++ b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/index.tsx @@ -0,0 +1 @@ +export { CreateRotationForm } from "./CreateRotationForm"; diff --git a/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/GeneralDetailsForm.tsx b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/GeneralDetailsForm.tsx new file mode 100644 index 000000000..b83b19974 --- /dev/null +++ b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/GeneralDetailsForm.tsx @@ -0,0 +1,93 @@ +import { Controller, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2"; +import { useWorkspace } from "@app/context"; + +const formSchema = z.object({ + environment: z.string().trim(), + secretPath: z.string().trim().default("/"), + interval: z.number() +}); + +export type TFormSchema = z.infer; +type Props = { + onSubmit: (data: TFormSchema) => void; + onCancel: () => void; +}; + +export const GeneralDetailsForm = ({ onSubmit, onCancel }: Props) => { + const { currentWorkspace } = useWorkspace(); + const environments = currentWorkspace?.environments || []; + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm({ + resolver: zodResolver(formSchema) + }); + + return ( +
+ ( + + + + )} + /> + ( + + + + )} + /> + ( + + field.onChange(parseInt(evt.target.value, 10))} + /> + + )} + /> +
+ + +
+ + ); +}; diff --git a/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationInputForm.tsx b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationInputForm.tsx new file mode 100644 index 000000000..baacfc2a5 --- /dev/null +++ b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationInputForm.tsx @@ -0,0 +1,61 @@ +import { Controller, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { Button, FormControl, SecretInput } from "@app/components/v2"; + +type Props = { + onSubmit: (data: Record) => void; + onCancel: () => void; + inputSchema: { + properties: Record; + required: string[]; + }; +}; + +const formSchema = z.record(z.string().trim().optional()); + +export const RotationInputForm = ({ onSubmit, onCancel, inputSchema }: Props) => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm>({ + resolver: zodResolver(formSchema) + }); + + return ( +
+ {Object.keys(inputSchema.properties || {}).map((inputName) => ( + ( + + + + )} + /> + ))} +
+ + +
+ + ); +}; diff --git a/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationOutputForm.tsx b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationOutputForm.tsx new file mode 100644 index 000000000..ed60ba912 --- /dev/null +++ b/frontend/src/views/SecretRotationPage/components/CreateRotationForm/steps/RotationOutputForm.tsx @@ -0,0 +1,80 @@ +import { Controller, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { Button, FormControl, Select, SelectItem } from "@app/components/v2"; +import { useWorkspace } from "@app/context"; +import { useGetProjectSecrets, useGetUserWsKey } from "@app/hooks/api"; + +const formSchema = z.record(z.string()); + +export type TFormSchema = z.infer; +type Props = { + environment: string; + secretPath: string; + outputSchema: Record; + onSubmit: (data: TFormSchema) => void; + onCancel: () => void; +}; + +export const RotationOutputForm = ({ + onSubmit, + onCancel, + environment, + secretPath, + outputSchema = {} +}: Props) => { + const { currentWorkspace } = useWorkspace(); + const workspaceId = currentWorkspace?._id || ""; + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm({ + resolver: zodResolver(formSchema) + }); + + const { data: userWsKey } = useGetUserWsKey(workspaceId); + const { data: secrets } = useGetProjectSecrets({ + workspaceId, + environment, + secretPath, + decryptFileKey: userWsKey! + }); + + return ( +
+ {Object.keys(outputSchema).map((outputName) => ( + ( + + + + )} + /> + ))} +
+ + +
+ + ); +}; diff --git a/frontend/src/views/SecretRotationPage/index.tsx b/frontend/src/views/SecretRotationPage/index.tsx new file mode 100644 index 000000000..c2f41e76e --- /dev/null +++ b/frontend/src/views/SecretRotationPage/index.tsx @@ -0,0 +1 @@ +export { SecretRotationPage } from "./SecretRotationPage";