From 4a3ff78636f9032a498eb4b9e6673cef30d1ca76 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Tue, 5 Nov 2024 16:19:07 +0100 Subject: [PATCH] feat: aws providers can be added via role --- actions/providers/providers.ts | 35 ++++- .../add-credentials/page.tsx | 9 +- components/providers/workflow/forms/index.ts | 1 + .../radio-group-aws-via-credentials-form.tsx | 15 +-- .../workflow/forms/via-credentials-form.tsx | 17 +-- .../workflow/forms/via-role-form.tsx | 125 ++++++++++++++++++ .../workflow/forms/via-role/aws-role-form.tsx | 105 +++++++++++++++ .../workflow/forms/via-role/index.ts | 1 + types/components.ts | 10 ++ types/formSchemas.ts | 18 +++ 10 files changed, 300 insertions(+), 36 deletions(-) create mode 100644 components/providers/workflow/forms/via-role-form.tsx create mode 100644 components/providers/workflow/forms/via-role/aws-role-form.tsx create mode 100644 components/providers/workflow/forms/via-role/index.ts diff --git a/actions/providers/providers.ts b/actions/providers/providers.ts index a1704c4fea..3a651783df 100644 --- a/actions/providers/providers.ts +++ b/actions/providers/providers.ts @@ -158,27 +158,50 @@ export const addCredentialsProvider = async (formData: FormData) => { const providerId = formData.get("providerId"); const providerType = formData.get("providerType"); + const isRole = formData.get("role_arn") !== null; + let secret = {}; + let secretType = "static"; // Default to static credentials if (providerType === "aws") { - secret = { - aws_access_key_id: formData.get("aws_access_key_id"), - aws_secret_access_key: formData.get("aws_secret_access_key"), - aws_session_token: formData.get("aws_session_token") || undefined, - }; + if (isRole) { + // Role-based configuration for AWS + secretType = "role"; + secret = { + role_arn: formData.get("role_arn"), + aws_access_key_id: formData.get("aws_access_key_id") || undefined, + aws_secret_access_key: + formData.get("aws_secret_access_key") || undefined, + aws_session_token: formData.get("aws_session_token") || undefined, + session_duration: + parseInt(formData.get("session_duration") as string, 10) || 3600, + external_id: formData.get("external_id") || undefined, + role_session_name: formData.get("role_session_name") || undefined, + }; + } else { + // Static credentials configuration for AWS + secret = { + aws_access_key_id: formData.get("aws_access_key_id"), + aws_secret_access_key: formData.get("aws_secret_access_key"), + aws_session_token: formData.get("aws_session_token") || undefined, + }; + } } else if (providerType === "azure") { + // Static credentials configuration for Azure secret = { client_id: formData.get("client_id"), client_secret: formData.get("client_secret"), tenant_id: formData.get("tenant_id"), }; } else if (providerType === "gcp") { + // Static credentials configuration for GCP secret = { client_id: formData.get("client_id"), client_secret: formData.get("client_secret"), refresh_token: formData.get("refresh_token"), }; } else if (providerType === "kubernetes") { + // Static credentials configuration for Kubernetes secret = { kubeconfig_content: formData.get("kubeconfig_content"), }; @@ -188,7 +211,7 @@ export const addCredentialsProvider = async (formData: FormData) => { data: { type: "ProviderSecret", attributes: { - secret_type: "static", + secret_type: secretType, secret, name: secretName, }, 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 b5fda55e11..6a525990fa 100644 --- a/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx +++ b/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx @@ -1,7 +1,10 @@ import { redirect } from "next/navigation"; import React from "react"; -import { ViaCredentialsForm } from "@/components/providers/workflow/forms"; +import { + ViaCredentialsForm, + ViaRoleForm, +} from "@/components/providers/workflow/forms"; interface Props { searchParams: { type: string; id: string; via?: string }; @@ -20,9 +23,13 @@ export default function AddCredentialsPage({ searchParams }: Props) { (searchParams.type === "aws" && searchParams.via === "credentials") || (searchParams.type !== "aws" && !searchParams.via); + const useRoleForm = + searchParams.type === "aws" && searchParams.via === "role"; + return ( <> {useCredentialsForm && } + {useRoleForm && } ); } diff --git a/components/providers/workflow/forms/index.ts b/components/providers/workflow/forms/index.ts index cf0417ed09..be1bb3277d 100644 --- a/components/providers/workflow/forms/index.ts +++ b/components/providers/workflow/forms/index.ts @@ -3,3 +3,4 @@ export * from "./launch-scan-form"; export * from "./radio-group-aws-via-credentials-form"; export * from "./test-connection-form"; export * from "./via-credentials-form"; +export * from "./via-role-form"; diff --git a/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx b/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx index a0a04f551d..7a870c79ff 100644 --- a/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx +++ b/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx @@ -34,20 +34,9 @@ export const RadioGroupAWSViaCredentialsForm = ({ >
Using IAM Role - +
- Connect via CloudFormation -
-
- -
- Connect via Terraform + Connect assuming IAM Role
diff --git a/components/providers/workflow/forms/via-credentials-form.tsx b/components/providers/workflow/forms/via-credentials-form.tsx index 56a9161ea0..176ed40a00 100644 --- a/components/providers/workflow/forms/via-credentials-form.tsx +++ b/components/providers/workflow/forms/via-credentials-form.tsx @@ -8,7 +8,7 @@ import * as z from "zod"; import { addCredentialsProvider } from "@/actions/providers/providers"; import { useToast } from "@/components/ui"; -import { CustomButton, CustomInput } from "@/components/ui/custom"; +import { CustomButton } from "@/components/ui/custom"; import { Form } from "@/components/ui/form"; import { addCredentialsFormSchema, @@ -50,7 +50,6 @@ export const ViaCredentialsForm = ({ const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { - secretName: "", providerId, providerType, ...(providerType === "aws" @@ -188,20 +187,6 @@ export const ViaCredentialsForm = ({ /> )} - Name (Optional) - -
{ + const router = useRouter(); + const { toast } = useToast(); + + const providerType = searchParams.type; + const providerId = searchParams.id; + + const formSchema = addCredentialsRoleFormSchema(providerType); + type FormSchemaType = z.infer; + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + providerId, + providerType, + ...(providerType === "aws" + ? { + role_arn: "", + aws_access_key_id: "", + aws_secret_access_key: "", + aws_session_token: "", + session_duration: 3600, + external_id: "", + role_session_name: "", + } + : {}), + }, + }); + + const isLoading = form.formState.isSubmitting; + + const onSubmitClient = async (values: FormSchemaType) => { + console.log("via ROLE form", values); + const formData = new FormData(); + + Object.entries(values).forEach( + ([key, value]) => + value !== undefined && formData.append(key, String(value)), + ); + + const data = await addCredentialsProvider(formData); + + if (data?.errors && data.errors.length > 0) { + data.errors.forEach((error: ApiError) => { + const errorMessage = error.detail; + switch (error.source.pointer) { + case "/data/attributes/secret/role_arn": + form.setError("role_arn" as keyof FormSchemaType, { + type: "server", + message: errorMessage, + }); + break; + + default: + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } + }); + } else { + router.push( + `/providers/test-connection?type=${providerType}&id=${providerId}`, + ); + } + }; + + return ( +
+ + + + + {providerType === "aws" && ( + } + /> + )} + +
+ } + > + {isLoading ? <>Loading : Save} + +
+ + + ); +}; diff --git a/components/providers/workflow/forms/via-role/aws-role-form.tsx b/components/providers/workflow/forms/via-role/aws-role-form.tsx new file mode 100644 index 0000000000..eb7f5d3a43 --- /dev/null +++ b/components/providers/workflow/forms/via-role/aws-role-form.tsx @@ -0,0 +1,105 @@ +import { Control } from "react-hook-form"; + +import { CustomInput } from "@/components/ui/custom"; +import { AWSCredentialsRole } from "@/types"; + +export const AWSCredentialsRoleForm = ({ + control, +}: { + control: Control; +}) => { + return ( + <> +
+
+ Connect assuming IAM Role +
+
+ Please provide the information for your AWS credentials. +
+
+ + + Optional fields + + + + + +
+ + +
+ + ); +}; diff --git a/components/providers/workflow/forms/via-role/index.ts b/components/providers/workflow/forms/via-role/index.ts new file mode 100644 index 0000000000..763c90c0c0 --- /dev/null +++ b/components/providers/workflow/forms/via-role/index.ts @@ -0,0 +1 @@ +export * from "./aws-role-form"; diff --git a/types/components.ts b/types/components.ts index 9c84ce65ab..b6c4b59da2 100644 --- a/types/components.ts +++ b/types/components.ts @@ -34,6 +34,16 @@ export type AWSCredentials = { providerId: string; }; +export type AWSCredentialsRole = { + role_arn: string; + aws_access_key_id?: string; + aws_secret_access_key?: string; + aws_session_token?: string; + external_id?: string; + role_session_name?: string; + session_duration?: number; +}; + export type AzureCredentials = { client_id: string; client_secret: string; diff --git a/types/formSchemas.ts b/types/formSchemas.ts index bd7c0b6585..a90ffc8c02 100644 --- a/types/formSchemas.ts +++ b/types/formSchemas.ts @@ -104,6 +104,24 @@ export const addCredentialsFormSchema = (providerType: string) => : {}), }); +export const addCredentialsRoleFormSchema = (providerType: string) => + providerType === "aws" + ? z.object({ + providerId: z.string(), + providerType: z.string(), + role_arn: z.string().optional(), + aws_access_key_id: z.string().optional(), + aws_secret_access_key: z.string().optional(), + aws_session_token: z.string().optional(), + session_duration: z.number().optional(), + external_id: z.string().optional(), + role_session_name: z.string().optional(), + }) + : z.object({ + providerId: z.string(), + providerType: z.string(), + }); + export const testConnectionFormSchema = z.object({ providerId: z.string(), });