From f1a951b2e496db251e611e4d1066d583c0f9fa5a Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 30 Oct 2024 15:01:48 +0100 Subject: [PATCH] chore: add form for add-credentials-providers --- actions/providers/providers.ts | 3 +- .../add-credentials/page.tsx | 12 +- .../connect-account/page.tsx | 6 +- .../workflow/forms/add-credentials-form.tsx | 142 ++++++++++++++++++ .../workflow/forms/connect-account-form.tsx | 6 + components/providers/workflow/forms/index.ts | 1 + components/providers/workflow/workflow.tsx | 2 +- lib/helper.ts | 2 + types/formSchemas.ts | 23 +++ 9 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 components/providers/workflow/forms/add-credentials-form.tsx diff --git a/actions/providers/providers.ts b/actions/providers/providers.ts index 4578786534..bbf1fbc844 100644 --- a/actions/providers/providers.ts +++ b/actions/providers/providers.ts @@ -4,7 +4,7 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { auth } from "@/auth.config"; -import { getErrorMessage, parseStringify } from "@/lib"; +import { getErrorMessage, parseStringify, wait } from "@/lib"; export const getProviders = async ({ page = 1, @@ -190,6 +190,7 @@ export const deleteProvider = async (formData: FormData) => { }, }); const data = await response.json(); + await wait(1000); revalidatePath("/providers"); return parseStringify(data); } catch (error) { diff --git a/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx b/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx index 1b9724578b..5ccd9a1f25 100644 --- a/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx +++ b/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx @@ -1,15 +1,7 @@ import React from "react"; -import { NavigationHeader } from "@/components/ui"; +import { AddCredentialsForm } from "@/components/providers/workflow/forms"; export default function AddCredentialsPage() { - return ( - <> - - - ); + return ; } diff --git a/app/(prowler)/providers/(set-up-provider)/connect-account/page.tsx b/app/(prowler)/providers/(set-up-provider)/connect-account/page.tsx index 1b4f1bf0f0..67094101f5 100644 --- a/app/(prowler)/providers/(set-up-provider)/connect-account/page.tsx +++ b/app/(prowler)/providers/(set-up-provider)/connect-account/page.tsx @@ -5,9 +5,5 @@ import React from "react"; import { ConnectAccountForm } from "@/components/providers/workflow/forms"; export default function ConnectAccountPage() { - return ( - <> - - - ); + return ; } diff --git a/components/providers/workflow/forms/add-credentials-form.tsx b/components/providers/workflow/forms/add-credentials-form.tsx new file mode 100644 index 0000000000..567a505310 --- /dev/null +++ b/components/providers/workflow/forms/add-credentials-form.tsx @@ -0,0 +1,142 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { SaveIcon } from "lucide-react"; +import { useRouter } from "next/navigation"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; + +import { useToast } from "@/components/ui"; +import { CustomButton, CustomInput } from "@/components/ui/custom"; +import { Form } from "@/components/ui/form"; + +import { addProvider } from "../../../../actions/providers/providers"; +import { addCredentialsFormSchema, ApiError } from "../../../../types"; + +export const AddCredentialsForm = ({ + providerType, +}: { + providerType: string; +}) => { + const formSchema = addCredentialsFormSchema(providerType); + const { toast } = useToast(); + + const form = useForm>({ + resolver: zodResolver(formSchema), + }); + + const isLoading = form.formState.isSubmitting; + + const router = useRouter(); + + const onSubmitClient = async (values: z.infer) => { + const formData = new FormData(); + + Object.entries(values).forEach( + ([key, value]) => value !== undefined && formData.append(key, value), + ); + + const data = await addProvider(formData); + console.log(data); + + if (data?.errors && data.errors.length > 0) { + data.errors.forEach((error: ApiError) => { + const errorMessage = error.detail; + switch (error.source.pointer) { + case "/data/attributes/provider": + form.setError("providerType", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/uid": + case "/data/attributes/__all__": + form.setError("providerId", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/alias": + form.setError("providerAlias", { + type: "server", + message: errorMessage, + }); + break; + default: + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } + }); + } else { + router.push("/providers/test-connection"); + } + }; + + return ( +
+ + {providerType === "aws" && ( + <> + {/* AWS Access Key ID */} + + {/* AWS Secret Access Key */} + + {/* AWS Session Token */} + + + )} + +
+ } + > + {isLoading ? <>Loading : Save} + +
+ + + ); +}; diff --git a/components/providers/workflow/forms/connect-account-form.tsx b/components/providers/workflow/forms/connect-account-form.tsx index 5b3a5ce7a9..5af9b01c12 100644 --- a/components/providers/workflow/forms/connect-account-form.tsx +++ b/components/providers/workflow/forms/connect-account-form.tsx @@ -2,6 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { ChevronLeftIcon, ChevronRightIcon, SaveIcon } from "lucide-react"; +import { useRouter } from "next/navigation"; import { useState } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; @@ -35,6 +36,8 @@ export const ConnectAccountForm = () => { const providerType = form.watch("providerType"); const isLoading = form.formState.isSubmitting; + const router = useRouter(); + const onSubmitClient = async (values: FormValues) => { const formData = new FormData(); @@ -43,6 +46,7 @@ export const ConnectAccountForm = () => { ); const data = await addProvider(formData); + console.log(data); if (data?.errors && data.errors.length > 0) { data.errors.forEach((error: ApiError) => { @@ -76,6 +80,8 @@ export const ConnectAccountForm = () => { } }); setPrevStep(1); + } else { + router.push("/providers/add-credentials"); } }; diff --git a/components/providers/workflow/forms/index.ts b/components/providers/workflow/forms/index.ts index a5c3ae2773..16cd78f184 100644 --- a/components/providers/workflow/forms/index.ts +++ b/components/providers/workflow/forms/index.ts @@ -1,2 +1,3 @@ +export * from "./add-credentials-form"; export * from "./connect-account-form"; export * from "./radio-group-aws-via-credentials-form"; diff --git a/components/providers/workflow/workflow.tsx b/components/providers/workflow/workflow.tsx index 28765f8c4b..323d97753d 100644 --- a/components/providers/workflow/workflow.tsx +++ b/components/providers/workflow/workflow.tsx @@ -25,7 +25,7 @@ const steps = [ href: "/providers/test-connection", }, { - title: "Lunch scan", + title: "Launch scan", description: "Choose when you want to launch your scan.", href: "/providers", }, diff --git a/lib/helper.ts b/lib/helper.ts index 7eeee17a6f..3ce912e078 100644 --- a/lib/helper.ts +++ b/lib/helper.ts @@ -1,5 +1,7 @@ import { MetaDataProps } from "@/types"; +export const wait = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)); // Helper function to create dictionaries by type export const createDict = ( type: string, diff --git a/types/formSchemas.ts b/types/formSchemas.ts index f541ebf593..64d6b94718 100644 --- a/types/formSchemas.ts +++ b/types/formSchemas.ts @@ -38,6 +38,29 @@ export const addProviderFormSchema = z.object({ awsCredentialsType: z.string().optional(), }); +export const addCredentialsFormSchema = (providerType: string) => + z.object( + providerType === "aws" + ? { + aws_access_key_id: z.string(), + aws_secret_access_key: z.string(), + aws_session_token: z.string(), + } + : providerType === "azure" + ? { + client_id: z.string(), + client_secret: z.string(), + tenant_id: z.string(), + } + : providerType === "gcp" + ? { + client_id: z.string(), + client_secret: z.string(), + refresh_token: z.string(), + } + : {}, + ); + export const editProviderFormSchema = (currentAlias: string) => z.object({ alias: z