mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
feat: aws providers can be added via role
This commit is contained in:
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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 && <ViaCredentialsForm searchParams={searchParams} />}
|
||||
{useRoleForm && <ViaRoleForm searchParams={searchParams} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -34,20 +34,9 @@ export const RadioGroupAWSViaCredentialsForm = ({
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-sm text-default-500">Using IAM Role</span>
|
||||
<CustomRadio
|
||||
description="Connect via CloudFormation"
|
||||
value="cloudformation"
|
||||
>
|
||||
<CustomRadio description="Connect assuming IAM Role" value="role">
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">Connect via CloudFormation</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio
|
||||
description="Connect via Terraform"
|
||||
value="terraform"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="ml-2">Connect via Terraform</span>
|
||||
<span className="ml-2">Connect assuming IAM Role</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<span className="text-sm text-default-500">
|
||||
|
||||
@@ -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<FormType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
secretName: "",
|
||||
providerId,
|
||||
providerType,
|
||||
...(providerType === "aws"
|
||||
@@ -188,20 +187,6 @@ export const ViaCredentialsForm = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<span className="text-sm text-default-500">Name (Optional)</span>
|
||||
<CustomInput
|
||||
control={form.control}
|
||||
name="secretName"
|
||||
type="text"
|
||||
label="Credential name"
|
||||
labelPlacement="inside"
|
||||
placeholder={"Enter the credential name"}
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
size="sm"
|
||||
isInvalid={!!form.formState.errors.secretName}
|
||||
/>
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
<CustomButton
|
||||
type="submit"
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { SaveIcon } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Control, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { addCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import {
|
||||
addCredentialsRoleFormSchema,
|
||||
ApiError,
|
||||
AWSCredentialsRole,
|
||||
} from "@/types";
|
||||
|
||||
import { AWSCredentialsRoleForm } from "./via-role/aws-role-form";
|
||||
|
||||
export const ViaRoleForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const providerType = searchParams.type;
|
||||
const providerId = searchParams.id;
|
||||
|
||||
const formSchema = addCredentialsRoleFormSchema(providerType);
|
||||
type FormSchemaType = z.infer<typeof formSchema>;
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
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 (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmitClient)}
|
||||
className="flex flex-col space-y-4"
|
||||
>
|
||||
<input type="hidden" name="providerId" value={providerId} />
|
||||
<input type="hidden" name="providerType" value={providerType} />
|
||||
|
||||
{providerType === "aws" && (
|
||||
<AWSCredentialsRoleForm
|
||||
control={form.control as unknown as Control<AWSCredentialsRole>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel={"Save"}
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
startContent={!isLoading && <SaveIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Save</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -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<AWSCredentialsRole>;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-4 text-left">
|
||||
<div className="text-2xl font-bold leading-9 text-default-foreground">
|
||||
Connect assuming IAM Role
|
||||
</div>
|
||||
<div className="py-2 text-default-500">
|
||||
Please provide the information for your AWS credentials.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="role_arn"
|
||||
type="text"
|
||||
label="Role ARN"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the Role ARN"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.role_arn}
|
||||
/>
|
||||
<span className="text-sm text-default-500">Optional fields</span>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_access_key_id"
|
||||
type="password"
|
||||
label="AWS Access Key ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Access Key ID"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.aws_access_key_id}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_secret_access_key"
|
||||
type="password"
|
||||
label="AWS Secret Access Key"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Secret Access Key"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.aws_secret_access_key}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_session_token"
|
||||
type="password"
|
||||
label="AWS Session Token"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Session Token"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.aws_session_token}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="external_id"
|
||||
type="text"
|
||||
label="External ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the External ID"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.external_id}
|
||||
/>
|
||||
|
||||
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="role_session_name"
|
||||
type="text"
|
||||
label="Role Session Name"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the Role Session Name"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.role_session_name}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="session_duration"
|
||||
type="number"
|
||||
label="Session Duration (seconds)"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the session duration (default: 3600)"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.session_duration}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./aws-role-form";
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user