mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
fix: improve error handling in UpdateViaCredentialsForm with early re… (#7988)
This commit is contained in:
@@ -30,6 +30,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
|
||||
- Add `Provider UID` filter to scans page. [(#7820)](https://github.com/prowler-cloud/prowler/pull/7820)
|
||||
- Aligned Next.js version to `v14.2.29` across Prowler and Cloud environments for consistency and improved maintainability. [(#7962)](https://github.com/prowler-cloud/prowler/pull/7962)
|
||||
- Refactor credentials forms with reusable components and error handling. [(#7988)](https://github.com/prowler-cloud/prowler/pull/7988)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -7,9 +7,17 @@ import {
|
||||
apiBaseUrl,
|
||||
getAuthHeaders,
|
||||
getErrorMessage,
|
||||
getFormValue,
|
||||
parseStringify,
|
||||
wait,
|
||||
} from "@/lib";
|
||||
import {
|
||||
buildSecretConfig,
|
||||
buildUpdateSecretConfig,
|
||||
handleApiError,
|
||||
handleApiResponse,
|
||||
} from "@/lib/provider-credentials/build-crendentials";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { ProvidersApiResponse, ProviderType } from "@/types/providers";
|
||||
|
||||
export const getProviders = async ({
|
||||
@@ -74,10 +82,8 @@ export const getProvider = async (formData: FormData) => {
|
||||
|
||||
export const updateProvider = async (formData: FormData) => {
|
||||
const headers = await getAuthHeaders({ contentType: true });
|
||||
|
||||
const providerId = formData.get("providerId");
|
||||
const providerAlias = formData.get("alias");
|
||||
|
||||
const providerId = formData.get(ProviderCredentialFields.PROVIDER_ID);
|
||||
const providerAlias = formData.get(ProviderCredentialFields.PROVIDER_ALIAS);
|
||||
const url = new URL(`${apiBaseUrl}/providers/${providerId}`);
|
||||
|
||||
try {
|
||||
@@ -88,22 +94,14 @@ export const updateProvider = async (formData: FormData) => {
|
||||
data: {
|
||||
type: "providers",
|
||||
id: providerId,
|
||||
attributes: {
|
||||
alias: providerAlias,
|
||||
},
|
||||
attributes: { alias: providerAlias },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
revalidatePath("/providers");
|
||||
return parseStringify(data);
|
||||
return handleApiResponse(response, "/providers");
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
return {
|
||||
error: getErrorMessage(error),
|
||||
};
|
||||
return handleApiError(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -150,120 +148,37 @@ export const addCredentialsProvider = async (formData: FormData) => {
|
||||
const headers = await getAuthHeaders({ contentType: true });
|
||||
const url = new URL(`${apiBaseUrl}/providers/secrets`);
|
||||
|
||||
const secretName = formData.get("secretName");
|
||||
const providerId = formData.get("providerId");
|
||||
const providerType = formData.get("providerType") as ProviderType;
|
||||
|
||||
const isRole = formData.get("role_arn") !== null;
|
||||
const isServiceAccount = formData.get("service_account_key") !== null;
|
||||
|
||||
let secret = {};
|
||||
let secretType = "static"; // Default to static credentials
|
||||
|
||||
if (providerType === "aws") {
|
||||
if (isRole) {
|
||||
// Role-based configuration for AWS
|
||||
secretType = "role";
|
||||
secret = {
|
||||
role_arn: formData.get("role_arn"),
|
||||
external_id: formData.get("external_id"),
|
||||
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,
|
||||
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 === "m365") {
|
||||
// Static credentials configuration for M365
|
||||
secret = {
|
||||
client_id: formData.get("client_id"),
|
||||
client_secret: formData.get("client_secret"),
|
||||
tenant_id: formData.get("tenant_id"),
|
||||
user: formData.get("user"),
|
||||
password: formData.get("password"),
|
||||
};
|
||||
} else if (providerType === "gcp") {
|
||||
if (isServiceAccount) {
|
||||
// Service account configuration for GCP
|
||||
secretType = "service_account";
|
||||
const serviceAccountKeyRaw = formData.get(
|
||||
"service_account_key",
|
||||
) as string;
|
||||
|
||||
try {
|
||||
const serviceAccountKey = JSON.parse(serviceAccountKeyRaw);
|
||||
secret = {
|
||||
service_account_key: serviceAccountKey,
|
||||
};
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("error", error);
|
||||
}
|
||||
} else {
|
||||
// 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"),
|
||||
};
|
||||
}
|
||||
const bodyData = {
|
||||
data: {
|
||||
type: "provider-secrets",
|
||||
attributes: {
|
||||
secret_type: secretType,
|
||||
secret,
|
||||
name: secretName,
|
||||
},
|
||||
relationships: {
|
||||
provider: {
|
||||
data: {
|
||||
id: providerId,
|
||||
type: "providers",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const providerId = getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.PROVIDER_ID,
|
||||
);
|
||||
const providerType = getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.PROVIDER_TYPE,
|
||||
) as ProviderType;
|
||||
|
||||
try {
|
||||
const { secretType, secret } = buildSecretConfig(formData, providerType);
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(bodyData),
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "provider-secrets",
|
||||
attributes: { secret_type: secretType, secret },
|
||||
relationships: {
|
||||
provider: {
|
||||
data: { id: providerId, type: "providers" },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
revalidatePath("/providers");
|
||||
return parseStringify(data);
|
||||
|
||||
return handleApiResponse(response, "/providers");
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
return {
|
||||
error: getErrorMessage(error),
|
||||
};
|
||||
return handleApiError(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -273,139 +188,48 @@ export const updateCredentialsProvider = async (
|
||||
) => {
|
||||
const headers = await getAuthHeaders({ contentType: true });
|
||||
const url = new URL(`${apiBaseUrl}/providers/secrets/${credentialsId}`);
|
||||
|
||||
const secretName = formData.get("secretName");
|
||||
const providerType = formData.get("providerType") as ProviderType;
|
||||
|
||||
const isRole = formData.get("role_arn") !== null;
|
||||
const isServiceAccount = formData.get("service_account_key") !== null;
|
||||
|
||||
let secret = {};
|
||||
|
||||
if (providerType === "aws") {
|
||||
if (isRole) {
|
||||
// Role-based configuration for AWS
|
||||
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 === "m365") {
|
||||
// Static credentials configuration for M365
|
||||
secret = {
|
||||
client_id: formData.get("client_id"),
|
||||
client_secret: formData.get("client_secret"),
|
||||
tenant_id: formData.get("tenant_id"),
|
||||
user: formData.get("user"),
|
||||
password: formData.get("password"),
|
||||
};
|
||||
} else if (providerType === "gcp") {
|
||||
if (isServiceAccount) {
|
||||
// Service account configuration for GCP
|
||||
const serviceAccountKeyRaw = formData.get(
|
||||
"service_account_key",
|
||||
) as string;
|
||||
|
||||
try {
|
||||
// Parse the service account key as JSON
|
||||
const serviceAccountKey = JSON.parse(serviceAccountKeyRaw);
|
||||
secret = {
|
||||
service_account_key: serviceAccountKey,
|
||||
};
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("error", error);
|
||||
}
|
||||
} else {
|
||||
// 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"),
|
||||
};
|
||||
}
|
||||
|
||||
const bodyData = {
|
||||
data: {
|
||||
type: "provider-secrets",
|
||||
id: credentialsId,
|
||||
attributes: {
|
||||
name: secretName,
|
||||
secret,
|
||||
},
|
||||
},
|
||||
};
|
||||
const providerType = getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.PROVIDER_TYPE,
|
||||
) as ProviderType;
|
||||
|
||||
try {
|
||||
const secret = buildUpdateSecretConfig(formData, providerType);
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "PATCH",
|
||||
headers,
|
||||
body: JSON.stringify(bodyData),
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
type: "provider-secrets",
|
||||
id: credentialsId,
|
||||
attributes: { secret },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to update credentials: ${response.statusText}`);
|
||||
const data = await response.json();
|
||||
return parseStringify(data); // Return API errors for UI handling
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
revalidatePath("/providers");
|
||||
return parseStringify(data);
|
||||
return handleApiResponse(response, "/providers");
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
return {
|
||||
error: getErrorMessage(error),
|
||||
};
|
||||
return handleApiError(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const checkConnectionProvider = async (formData: FormData) => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
|
||||
const providerId = formData.get("providerId");
|
||||
|
||||
const providerId = formData.get(ProviderCredentialFields.PROVIDER_ID);
|
||||
const url = new URL(`${apiBaseUrl}/providers/${providerId}/connection`);
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "POST",
|
||||
headers,
|
||||
});
|
||||
const data = await response.json();
|
||||
const response = await fetch(url.toString(), { method: "POST", headers });
|
||||
await wait(2000);
|
||||
revalidatePath("/providers");
|
||||
return parseStringify(data);
|
||||
return handleApiResponse(response, "/providers");
|
||||
} catch (error) {
|
||||
return {
|
||||
error: getErrorMessage(error),
|
||||
};
|
||||
return handleApiError(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -451,7 +275,7 @@ export const deleteCredentials = async (secretId: string) => {
|
||||
|
||||
export const deleteProvider = async (formData: FormData) => {
|
||||
const headers = await getAuthHeaders({ contentType: false });
|
||||
const providerId = formData.get("id");
|
||||
const providerId = formData.get(ProviderCredentialFields.PROVIDER_ID);
|
||||
|
||||
if (!providerId) {
|
||||
return { error: "Provider ID is required" };
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
ViaCredentialsForm,
|
||||
ViaRoleForm,
|
||||
AddViaCredentialsForm,
|
||||
AddViaRoleForm,
|
||||
} from "@/components/providers/workflow/forms";
|
||||
import { SelectViaAWS } from "@/components/providers/workflow/forms/select-credentials-type/aws";
|
||||
import {
|
||||
AddViaServiceAccountForm,
|
||||
SelectViaGCP,
|
||||
ViaServiceAccountForm,
|
||||
} from "@/components/providers/workflow/forms/select-credentials-type/gcp";
|
||||
import { ProviderType } from "@/types/providers";
|
||||
|
||||
@@ -29,16 +29,16 @@ export default function AddCredentialsPage({ searchParams }: Props) {
|
||||
{((searchParams.type === "aws" && searchParams.via === "credentials") ||
|
||||
(searchParams.type === "gcp" && searchParams.via === "credentials") ||
|
||||
(searchParams.type !== "aws" && searchParams.type !== "gcp")) && (
|
||||
<ViaCredentialsForm searchParams={searchParams} />
|
||||
<AddViaCredentialsForm searchParams={searchParams} />
|
||||
)}
|
||||
|
||||
{searchParams.type === "aws" && searchParams.via === "role" && (
|
||||
<ViaRoleForm searchParams={searchParams} />
|
||||
<AddViaRoleForm searchParams={searchParams} />
|
||||
)}
|
||||
|
||||
{searchParams.type === "gcp" &&
|
||||
searchParams.via === "service-account" && (
|
||||
<ViaServiceAccountForm searchParams={searchParams} />
|
||||
<AddViaServiceAccountForm searchParams={searchParams} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -10,9 +10,10 @@ import { DeleteIcon } from "@/components/icons";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
|
||||
const formSchema = z.object({
|
||||
providerId: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
});
|
||||
|
||||
export const DeleteForm = ({
|
||||
@@ -53,7 +54,11 @@ export const DeleteForm = ({
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form action={onSubmitClient}>
|
||||
<input type="hidden" name="id" value={providerId} />
|
||||
<input
|
||||
type="hidden"
|
||||
name={ProviderCredentialFields.PROVIDER_ID}
|
||||
value={providerId}
|
||||
/>
|
||||
<div className="flex w-full justify-center sm:space-x-6">
|
||||
<CustomButton
|
||||
type="button"
|
||||
|
||||
@@ -10,6 +10,7 @@ import { SaveIcon } from "@/components/icons";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton, CustomInput } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { editProviderFormSchema } from "@/types";
|
||||
|
||||
export const EditForm = ({
|
||||
@@ -26,8 +27,8 @@ export const EditForm = ({
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId: providerId,
|
||||
alias: providerAlias,
|
||||
[ProviderCredentialFields.PROVIDER_ID]: providerId,
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: providerAlias,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -74,14 +75,16 @@ export const EditForm = ({
|
||||
<div>
|
||||
<CustomInput
|
||||
control={form.control}
|
||||
name="alias"
|
||||
name={ProviderCredentialFields.PROVIDER_ALIAS}
|
||||
type="text"
|
||||
label="Alias"
|
||||
labelPlacement="outside"
|
||||
placeholder={providerAlias}
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!form.formState.errors.alias}
|
||||
isInvalid={
|
||||
!!form.formState.errors[ProviderCredentialFields.PROVIDER_ALIAS]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<input type="hidden" name="providerId" value={providerId} />
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import { addCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { ProviderType } from "@/types";
|
||||
|
||||
import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const AddViaCredentialsForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
}) => {
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
|
||||
const handleAddCredentials = async (formData: FormData) => {
|
||||
return await addCredentialsProvider(formData);
|
||||
};
|
||||
|
||||
const successNavigationUrl = `/providers/test-connection?type=${providerType}&id=${providerId}`;
|
||||
|
||||
return (
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
onSubmit={handleAddCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import { addCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { ProviderType } from "@/types";
|
||||
|
||||
import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const AddViaRoleForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
}) => {
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
|
||||
const handleAddCredentials = async (formData: FormData) => {
|
||||
return await addCredentialsProvider(formData);
|
||||
};
|
||||
|
||||
const successNavigationUrl = `/providers/test-connection?type=${providerType}&id=${providerId}`;
|
||||
|
||||
return (
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
onSubmit={handleAddCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
"use client";
|
||||
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { useCredentialsForm } from "@/hooks/use-credentials-form";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import {
|
||||
AWSCredentials,
|
||||
AWSCredentialsRole,
|
||||
AzureCredentials,
|
||||
GCPDefaultCredentials,
|
||||
GCPServiceAccountKey,
|
||||
KubernetesCredentials,
|
||||
M365Credentials,
|
||||
ProviderType,
|
||||
} from "@/types";
|
||||
|
||||
import { ProviderTitleDocs } from "../provider-title-docs";
|
||||
import { AWSStaticCredentialsForm } from "./select-credentials-type/aws/credentials-type";
|
||||
import { AWSRoleCredentialsForm } from "./select-credentials-type/aws/credentials-type/aws-role-credentials-form";
|
||||
import { GCPDefaultCredentialsForm } from "./select-credentials-type/gcp/credentials-type";
|
||||
import { GCPServiceAccountKeyForm } from "./select-credentials-type/gcp/credentials-type/gcp-service-account-key-form";
|
||||
import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
|
||||
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
|
||||
import { M365CredentialsForm } from "./via-credentials/m365-credentials-form";
|
||||
|
||||
type BaseCredentialsFormProps = {
|
||||
providerType: ProviderType;
|
||||
providerId: string;
|
||||
onSubmit: (formData: FormData) => Promise<any>;
|
||||
successNavigationUrl: string;
|
||||
submitButtonText?: string;
|
||||
showBackButton?: boolean;
|
||||
};
|
||||
|
||||
export const BaseCredentialsForm = ({
|
||||
providerType,
|
||||
providerId,
|
||||
onSubmit,
|
||||
successNavigationUrl,
|
||||
submitButtonText = "Next",
|
||||
showBackButton = true,
|
||||
}: BaseCredentialsFormProps) => {
|
||||
const {
|
||||
form,
|
||||
isLoading,
|
||||
handleSubmit,
|
||||
handleBackStep,
|
||||
searchParamsObj,
|
||||
externalId,
|
||||
} = useCredentialsForm({
|
||||
providerType,
|
||||
providerId,
|
||||
onSubmit,
|
||||
successNavigationUrl,
|
||||
});
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
className="flex flex-col space-y-4"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name={ProviderCredentialFields.PROVIDER_ID}
|
||||
value={providerId}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name={ProviderCredentialFields.PROVIDER_TYPE}
|
||||
value={providerType}
|
||||
/>
|
||||
|
||||
<ProviderTitleDocs providerType={providerType} />
|
||||
|
||||
<Divider />
|
||||
|
||||
{providerType === "aws" && searchParamsObj.get("via") === "role" && (
|
||||
<AWSRoleCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentialsRole>}
|
||||
setValue={form.setValue as any}
|
||||
externalId={externalId}
|
||||
/>
|
||||
)}
|
||||
{providerType === "aws" && searchParamsObj.get("via") !== "role" && (
|
||||
<AWSStaticCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "azure" && (
|
||||
<AzureCredentialsForm
|
||||
control={form.control as unknown as Control<AzureCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "m365" && (
|
||||
<M365CredentialsForm
|
||||
control={form.control as unknown as Control<M365Credentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "gcp" &&
|
||||
searchParamsObj.get("via") === "service-account" && (
|
||||
<GCPServiceAccountKeyForm
|
||||
control={form.control as unknown as Control<GCPServiceAccountKey>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "gcp" &&
|
||||
searchParamsObj.get("via") !== "service-account" && (
|
||||
<GCPDefaultCredentialsForm
|
||||
control={
|
||||
form.control as unknown as Control<GCPDefaultCredentials>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{providerType === "kubernetes" && (
|
||||
<KubernetesCredentialsForm
|
||||
control={form.control as unknown as Control<KubernetesCredentials>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{showBackButton &&
|
||||
(searchParamsObj.get("via") === "credentials" ||
|
||||
searchParamsObj.get("via") === "role" ||
|
||||
searchParamsObj.get("via") === "service-account") && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Save"
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>{submitButtonText}</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from "./add-via-credentials-form";
|
||||
export * from "./add-via-role-form";
|
||||
export * from "./connect-account-form";
|
||||
export * from "./test-connection-form";
|
||||
export * from "./update-via-credentials-form";
|
||||
export * from "./update-via-role-form";
|
||||
export * from "./via-credentials-form";
|
||||
export * from "./via-role-form";
|
||||
|
||||
+42
-17
@@ -3,6 +3,7 @@ import { Control, UseFormSetValue, useWatch } from "react-hook-form";
|
||||
|
||||
import { CredentialsRoleHelper } from "@/components/providers/workflow";
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { AWSCredentialsRole } from "@/types";
|
||||
|
||||
export const AWSRoleCredentialsForm = ({
|
||||
@@ -16,7 +17,7 @@ export const AWSRoleCredentialsForm = ({
|
||||
}) => {
|
||||
const credentialsType = useWatch({
|
||||
control,
|
||||
name: "credentials_type" as const,
|
||||
name: ProviderCredentialFields.CREDENTIALS_TYPE,
|
||||
defaultValue: "aws-sdk-default",
|
||||
});
|
||||
|
||||
@@ -34,7 +35,7 @@ export const AWSRoleCredentialsForm = ({
|
||||
<span className="text-xs font-bold text-default-500">Authentication</span>
|
||||
|
||||
<Select
|
||||
name="credentials_type"
|
||||
name={ProviderCredentialFields.CREDENTIALS_TYPE}
|
||||
label="Authentication Method"
|
||||
placeholder="Select credentials type"
|
||||
defaultSelectedKeys={["aws-sdk-default"]}
|
||||
@@ -42,7 +43,7 @@ export const AWSRoleCredentialsForm = ({
|
||||
variant="bordered"
|
||||
onSelectionChange={(keys) =>
|
||||
setValue(
|
||||
"credentials_type",
|
||||
ProviderCredentialFields.CREDENTIALS_TYPE,
|
||||
Array.from(keys)[0] as "aws-sdk-default" | "access-secret-key",
|
||||
)
|
||||
}
|
||||
@@ -55,36 +56,48 @@ export const AWSRoleCredentialsForm = ({
|
||||
<>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_access_key_id"
|
||||
name={ProviderCredentialFields.AWS_ACCESS_KEY_ID}
|
||||
type="password"
|
||||
label="AWS Access Key ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Access Key ID"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.aws_access_key_id}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.AWS_ACCESS_KEY_ID
|
||||
]
|
||||
}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_secret_access_key"
|
||||
name={ProviderCredentialFields.AWS_SECRET_ACCESS_KEY}
|
||||
type="password"
|
||||
label="AWS Secret Access Key"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Secret Access Key"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.aws_secret_access_key}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY
|
||||
]
|
||||
}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_session_token"
|
||||
name={ProviderCredentialFields.AWS_SESSION_TOKEN}
|
||||
type="password"
|
||||
label="AWS Session Token (optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Session Token"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.aws_session_token}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.AWS_SESSION_TOKEN
|
||||
]
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -96,18 +109,20 @@ export const AWSRoleCredentialsForm = ({
|
||||
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="role_arn"
|
||||
name={ProviderCredentialFields.ROLE_ARN}
|
||||
type="text"
|
||||
label="Role ARN"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the Role ARN"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.role_arn}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.ROLE_ARN]
|
||||
}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="external_id"
|
||||
name={ProviderCredentialFields.EXTERNAL_ID}
|
||||
type="text"
|
||||
label="External ID"
|
||||
labelPlacement="inside"
|
||||
@@ -116,32 +131,42 @@ export const AWSRoleCredentialsForm = ({
|
||||
defaultValue={externalId}
|
||||
isDisabled
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.external_id}
|
||||
isInvalid={
|
||||
!!control._formState.errors[ProviderCredentialFields.EXTERNAL_ID]
|
||||
}
|
||||
/>
|
||||
|
||||
<span className="text-xs text-default-500">Optional fields</span>
|
||||
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="role_session_name"
|
||||
name={ProviderCredentialFields.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}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.ROLE_SESSION_NAME
|
||||
]
|
||||
}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="session_duration"
|
||||
name={ProviderCredentialFields.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}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.SESSION_DURATION
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
+19
-6
@@ -1,6 +1,7 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { AWSCredentials } from "@/types";
|
||||
|
||||
export const AWSStaticCredentialsForm = ({
|
||||
@@ -20,36 +21,48 @@ export const AWSStaticCredentialsForm = ({
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_access_key_id"
|
||||
name={ProviderCredentialFields.AWS_ACCESS_KEY_ID}
|
||||
type="password"
|
||||
label="AWS Access Key ID"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Access Key ID"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.aws_access_key_id}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.AWS_ACCESS_KEY_ID
|
||||
]
|
||||
}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_secret_access_key"
|
||||
name={ProviderCredentialFields.AWS_SECRET_ACCESS_KEY}
|
||||
type="password"
|
||||
label="AWS Secret Access Key"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the AWS Secret Access Key"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.aws_secret_access_key}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY
|
||||
]
|
||||
}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name="aws_session_token"
|
||||
name={ProviderCredentialFields.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}
|
||||
isInvalid={
|
||||
!!control._formState.errors[
|
||||
ProviderCredentialFields.AWS_SESSION_TOKEN
|
||||
]
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import { addCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { ProviderType } from "@/types";
|
||||
|
||||
import { BaseCredentialsForm } from "../../base-credentials-form";
|
||||
|
||||
export const AddViaServiceAccountForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: ProviderType; id: string };
|
||||
}) => {
|
||||
const providerType = searchParams.type;
|
||||
const providerId = searchParams.id;
|
||||
|
||||
const handleAddCredentials = async (formData: FormData) => {
|
||||
return await addCredentialsProvider(formData);
|
||||
};
|
||||
|
||||
const successNavigationUrl = `/providers/test-connection?type=${providerType}&id=${providerId}`;
|
||||
|
||||
return (
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
onSubmit={handleAddCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from "./add-via-service-account-form";
|
||||
export * from "./radio-group-gcp-via-credentials-type-form";
|
||||
export * from "./select-via-gcp";
|
||||
export * from "./via-service-account-form";
|
||||
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Control, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { addCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { ProviderTitleDocs } from "@/components/providers/workflow";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import {
|
||||
addCredentialsServiceAccountFormSchema,
|
||||
ApiError,
|
||||
GCPServiceAccountKey,
|
||||
ProviderType,
|
||||
} from "@/types";
|
||||
|
||||
import { GCPServiceAccountKeyForm } from "./credentials-type/gcp-service-account-key-form";
|
||||
|
||||
export const ViaServiceAccountForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: ProviderType; id: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const searchParamsObj = useSearchParams();
|
||||
|
||||
// Handler for back button
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
|
||||
const providerType = searchParams.type;
|
||||
const providerId = searchParams.id;
|
||||
|
||||
const formSchema = addCredentialsServiceAccountFormSchema(providerType);
|
||||
type FormSchemaType = z.infer<typeof formSchema>;
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId,
|
||||
providerType,
|
||||
...(providerType === "gcp"
|
||||
? {
|
||||
service_account_key: "",
|
||||
secretName: "",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: FormSchemaType) => {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== "") {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
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/service_account_key":
|
||||
form.setError("service_account_key" 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}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error during submission:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Submission failed",
|
||||
description: "An error occurred while processing your request.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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} />
|
||||
|
||||
<ProviderTitleDocs providerType={providerType} />
|
||||
|
||||
<Divider />
|
||||
|
||||
{providerType === "gcp" && (
|
||||
<GCPServiceAccountKeyForm
|
||||
control={form.control as unknown as Control<GCPServiceAccountKey>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{searchParamsObj.get("via") === "service-account" && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel={"Save"}
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Next</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -1,266 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Control, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { updateCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { ProviderType } from "@/types";
|
||||
import {
|
||||
addCredentialsFormSchema,
|
||||
ApiError,
|
||||
AWSCredentials,
|
||||
AzureCredentials,
|
||||
GCPDefaultCredentials,
|
||||
KubernetesCredentials,
|
||||
M365Credentials,
|
||||
} from "@/types";
|
||||
|
||||
import { ProviderTitleDocs } from "../provider-title-docs";
|
||||
import { AWSStaticCredentialsForm } from "./select-credentials-type/aws/credentials-type";
|
||||
import { GCPDefaultCredentialsForm } from "./select-credentials-type/gcp/credentials-type";
|
||||
import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
|
||||
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
|
||||
import { M365CredentialsForm } from "./via-credentials/m365-credentials-form";
|
||||
|
||||
type CredentialsFormSchema = z.infer<
|
||||
ReturnType<typeof addCredentialsFormSchema>
|
||||
>;
|
||||
|
||||
// Add this type intersection to include all fields
|
||||
type FormType = CredentialsFormSchema &
|
||||
AWSCredentials &
|
||||
AzureCredentials &
|
||||
M365Credentials &
|
||||
GCPDefaultCredentials &
|
||||
KubernetesCredentials;
|
||||
import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const UpdateViaCredentialsForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string; secretId?: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const searchParamsObj = useSearchParams();
|
||||
|
||||
// Handler for back button
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
const providerSecretId = searchParams.secretId || "";
|
||||
const formSchema = addCredentialsFormSchema(providerType);
|
||||
|
||||
const form = useForm<FormType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId,
|
||||
providerType,
|
||||
...(providerType === "aws"
|
||||
? {
|
||||
aws_access_key_id: "",
|
||||
aws_secret_access_key: "",
|
||||
aws_session_token: "",
|
||||
}
|
||||
: providerType === "azure"
|
||||
? {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
tenant_id: "",
|
||||
}
|
||||
: providerType === "m365"
|
||||
? {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
tenant_id: "",
|
||||
user: "",
|
||||
password: "",
|
||||
}
|
||||
: providerType === "gcp"
|
||||
? {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
refresh_token: "",
|
||||
}
|
||||
: providerType === "kubernetes"
|
||||
? {
|
||||
kubeconfig_content: "",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: FormType) => {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(
|
||||
([key, value]) => value !== undefined && formData.append(key, value),
|
||||
);
|
||||
|
||||
const data = await updateCredentialsProvider(providerSecretId, 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/aws_access_key_id":
|
||||
form.setError("aws_access_key_id", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/aws_secret_access_key":
|
||||
form.setError("aws_secret_access_key", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/aws_session_token":
|
||||
form.setError("aws_session_token", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/client_id":
|
||||
form.setError("client_id", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/client_secret":
|
||||
form.setError("client_secret", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/user":
|
||||
form.setError("user", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/password":
|
||||
form.setError("password", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/tenant_id":
|
||||
form.setError("tenant_id", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/kubeconfig_content":
|
||||
form.setError("kubeconfig_content", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/name":
|
||||
form.setError("secretName", {
|
||||
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}&updated=true`,
|
||||
);
|
||||
}
|
||||
const handleUpdateCredentials = async (formData: FormData) => {
|
||||
return await updateCredentialsProvider(providerSecretId, formData);
|
||||
};
|
||||
|
||||
const successNavigationUrl = `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`;
|
||||
|
||||
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} />
|
||||
|
||||
<ProviderTitleDocs providerType={providerType} />
|
||||
|
||||
<Divider />
|
||||
|
||||
{providerType === "aws" && (
|
||||
<AWSStaticCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "azure" && (
|
||||
<AzureCredentialsForm
|
||||
control={form.control as unknown as Control<AzureCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "m365" && (
|
||||
<M365CredentialsForm
|
||||
control={form.control as unknown as Control<M365Credentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "gcp" && (
|
||||
<GCPDefaultCredentialsForm
|
||||
control={form.control as unknown as Control<GCPDefaultCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "kubernetes" && (
|
||||
<KubernetesCredentialsForm
|
||||
control={form.control as unknown as Control<KubernetesCredentials>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{searchParamsObj.get("via") === "credentials" && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel={"Save"}
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Next</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
onSubmit={handleUpdateCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,195 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { Control, useForm, UseFormSetValue } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { updateCredentialsProvider } 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 { ProviderType } from "@/types";
|
||||
|
||||
import { AWSRoleCredentialsForm } from "./select-credentials-type/aws/credentials-type";
|
||||
import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const UpdateViaRoleForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string; secretId?: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { data: session } = useSession();
|
||||
|
||||
const searchParamsObj = useSearchParams();
|
||||
|
||||
// Extract values from searchParams
|
||||
const providerType = searchParams.type;
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
const providerSecretId = searchParams.secretId || "";
|
||||
const externalId = session?.tenantId;
|
||||
|
||||
const formSchema = addCredentialsRoleFormSchema(providerType);
|
||||
type FormSchemaType = z.infer<typeof formSchema> & {
|
||||
credentials_type: "aws-sdk-default" | "access-secret-key";
|
||||
const handleUpdateCredentials = async (formData: FormData) => {
|
||||
return await updateCredentialsProvider(providerSecretId, formData);
|
||||
};
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId,
|
||||
providerType,
|
||||
credentials_type: "aws-sdk-default",
|
||||
...(providerType === "aws" && {
|
||||
role_arn: "",
|
||||
external_id: externalId,
|
||||
aws_access_key_id: "",
|
||||
aws_secret_access_key: "",
|
||||
aws_session_token: "",
|
||||
role_session_name: "",
|
||||
session_duration: "3600",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
// Handle form submission
|
||||
const onSubmitClient = async (values: FormSchemaType) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
if (key === "credentials_type") return;
|
||||
|
||||
if (
|
||||
values.credentials_type === "access-secret-key" &&
|
||||
[
|
||||
"aws_access_key_id",
|
||||
"aws_secret_access_key",
|
||||
"aws_session_token",
|
||||
].includes(key)
|
||||
) {
|
||||
if (value !== undefined && value !== "") {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (value !== undefined && value !== "") {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
const data = await updateCredentialsProvider(providerSecretId, formData);
|
||||
|
||||
// Handle errors
|
||||
if (data?.errors?.length) {
|
||||
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;
|
||||
case "/data/attributes/secret/external_id":
|
||||
form.setError("external_id" as keyof FormSchemaType, {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Oops! Something went wrong",
|
||||
description: errorMessage,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Redirect on success
|
||||
router.push(
|
||||
`/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error during submission:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Submission failed",
|
||||
description: "An error occurred while processing your request.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Handle back navigation
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
const successNavigationUrl = `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`;
|
||||
|
||||
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} />
|
||||
|
||||
{/* Conditional AWS Form */}
|
||||
{providerType === "aws" && (
|
||||
<AWSRoleCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentialsRole>}
|
||||
setValue={
|
||||
form.setValue as unknown as UseFormSetValue<AWSCredentialsRole>
|
||||
}
|
||||
externalId={externalId || ""}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{searchParamsObj.get("via") === "role" && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Save"
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Next</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
onSubmit={handleUpdateCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,169 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Control, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { updateCredentialsProvider } from "@/actions/providers/providers";
|
||||
import { ProviderTitleDocs } from "@/components/providers/workflow";
|
||||
import { useToast } from "@/components/ui";
|
||||
import { CustomButton } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import {
|
||||
addCredentialsServiceAccountFormSchema,
|
||||
ApiError,
|
||||
GCPServiceAccountKey,
|
||||
ProviderType,
|
||||
} from "@/types";
|
||||
import { ProviderType } from "@/types";
|
||||
|
||||
import { GCPServiceAccountKeyForm } from "./select-credentials-type/gcp/credentials-type";
|
||||
import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const UpdateViaServiceAccountForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string; secretId?: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const searchParamsObj = useSearchParams();
|
||||
|
||||
// Handler for back button
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
const providerSecretId = searchParams.secretId || "";
|
||||
|
||||
const formSchema = addCredentialsServiceAccountFormSchema(providerType);
|
||||
type FormSchemaType = z.infer<typeof formSchema>;
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId,
|
||||
providerType,
|
||||
...(providerType === "gcp"
|
||||
? {
|
||||
service_account_key: "",
|
||||
secretName: "",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: FormSchemaType) => {
|
||||
if (!providerSecretId) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Missing Secret ID",
|
||||
description: "Cannot update credentials without a valid secret ID.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== "") {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const data = await updateCredentialsProvider(providerSecretId, 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/service_account_key":
|
||||
form.setError("service_account_key" 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}&updated=true`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error during submission:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Submission failed",
|
||||
description: "An error occurred while processing your request.",
|
||||
});
|
||||
}
|
||||
const handleUpdateCredentials = async (formData: FormData) => {
|
||||
return await updateCredentialsProvider(providerSecretId, formData);
|
||||
};
|
||||
|
||||
const successNavigationUrl = `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`;
|
||||
|
||||
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} />
|
||||
|
||||
<ProviderTitleDocs providerType={providerType} />
|
||||
|
||||
<Divider />
|
||||
|
||||
{providerType === "gcp" && (
|
||||
<GCPServiceAccountKeyForm
|
||||
control={form.control as unknown as Control<GCPServiceAccountKey>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{searchParamsObj.get("via") === "service-account" && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel={"Save"}
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Next</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
onSubmit={handleUpdateCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Divider } from "@nextui-org/divider";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { useRouter, useSearchParams } 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 {
|
||||
addCredentialsFormSchema,
|
||||
ApiError,
|
||||
AWSCredentials,
|
||||
AzureCredentials,
|
||||
GCPDefaultCredentials,
|
||||
KubernetesCredentials,
|
||||
M365Credentials,
|
||||
ProviderType,
|
||||
} from "@/types";
|
||||
|
||||
import { ProviderTitleDocs } from "../provider-title-docs";
|
||||
import { AWSStaticCredentialsForm } from "./select-credentials-type/aws/credentials-type";
|
||||
import { GCPDefaultCredentialsForm } from "./select-credentials-type/gcp/credentials-type";
|
||||
import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
|
||||
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
|
||||
import { M365CredentialsForm } from "./via-credentials/m365-credentials-form";
|
||||
|
||||
type CredentialsFormSchema = z.infer<
|
||||
ReturnType<typeof addCredentialsFormSchema>
|
||||
>;
|
||||
|
||||
// Add this type intersection to include all fields
|
||||
type FormType = CredentialsFormSchema &
|
||||
AWSCredentials &
|
||||
AzureCredentials &
|
||||
GCPDefaultCredentials &
|
||||
KubernetesCredentials &
|
||||
M365Credentials;
|
||||
|
||||
export const ViaCredentialsForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const searchParamsObj = useSearchParams();
|
||||
|
||||
// Handler for back button
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
const formSchema = addCredentialsFormSchema(providerType);
|
||||
|
||||
const form = useForm<FormType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId,
|
||||
providerType,
|
||||
...(providerType === "aws"
|
||||
? {
|
||||
aws_access_key_id: "",
|
||||
aws_secret_access_key: "",
|
||||
aws_session_token: "",
|
||||
}
|
||||
: providerType === "azure"
|
||||
? {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
tenant_id: "",
|
||||
}
|
||||
: providerType === "m365"
|
||||
? {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
tenant_id: "",
|
||||
user: "",
|
||||
password: "",
|
||||
}
|
||||
: providerType === "gcp"
|
||||
? {
|
||||
client_id: "",
|
||||
client_secret: "",
|
||||
refresh_token: "",
|
||||
}
|
||||
: providerType === "kubernetes"
|
||||
? {
|
||||
kubeconfig_content: "",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: FormType) => {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(
|
||||
([key, value]) => value !== undefined && formData.append(key, 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/aws_access_key_id":
|
||||
form.setError("aws_access_key_id", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/aws_secret_access_key":
|
||||
form.setError("aws_secret_access_key", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/aws_session_token":
|
||||
form.setError("aws_session_token", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/client_id":
|
||||
form.setError("client_id", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/client_secret":
|
||||
form.setError("client_secret", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/user":
|
||||
form.setError("user", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/password":
|
||||
form.setError("password", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/tenant_id":
|
||||
form.setError("tenant_id", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/secret/kubeconfig_content":
|
||||
form.setError("kubeconfig_content", {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
break;
|
||||
case "/data/attributes/name":
|
||||
form.setError("secretName", {
|
||||
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} />
|
||||
|
||||
<ProviderTitleDocs providerType={providerType} />
|
||||
|
||||
<Divider />
|
||||
|
||||
{providerType === "aws" && (
|
||||
<AWSStaticCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "azure" && (
|
||||
<AzureCredentialsForm
|
||||
control={form.control as unknown as Control<AzureCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "m365" && (
|
||||
<M365CredentialsForm
|
||||
control={form.control as unknown as Control<M365Credentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "gcp" && (
|
||||
<GCPDefaultCredentialsForm
|
||||
control={form.control as unknown as Control<GCPDefaultCredentials>}
|
||||
/>
|
||||
)}
|
||||
{providerType === "kubernetes" && (
|
||||
<KubernetesCredentialsForm
|
||||
control={form.control as unknown as Control<KubernetesCredentials>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{searchParamsObj.get("via") === "credentials" && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel={"Save"}
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Next</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -1,193 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { Control, useForm, UseFormSetValue } 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 { AWSRoleCredentialsForm } from "./select-credentials-type/aws/credentials-type";
|
||||
|
||||
export const ViaRoleForm = ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { data: session } = useSession();
|
||||
const searchParamsObj = useSearchParams();
|
||||
const externalId = session?.tenantId;
|
||||
|
||||
// Handler for back button
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
|
||||
const providerType = searchParams.type;
|
||||
const providerId = searchParams.id;
|
||||
|
||||
const formSchema = addCredentialsRoleFormSchema(providerType);
|
||||
type FormSchemaType = z.infer<typeof formSchema> & {
|
||||
credentials_type: "aws-sdk-default" | "access-secret-key";
|
||||
};
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
providerId,
|
||||
providerType,
|
||||
credentials_type: "aws-sdk-default",
|
||||
...(providerType === "aws"
|
||||
? {
|
||||
role_arn: "",
|
||||
external_id: externalId,
|
||||
aws_access_key_id: "",
|
||||
aws_secret_access_key: "",
|
||||
aws_session_token: "",
|
||||
role_session_name: "",
|
||||
session_duration: "3600",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmitClient = async (values: FormSchemaType) => {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
// Do not include credentials_type
|
||||
if (key === "credentials_type") return;
|
||||
|
||||
// If credentials_type is "access-secret-key", include the relevant fields
|
||||
if (
|
||||
values.credentials_type === "access-secret-key" &&
|
||||
[
|
||||
"aws_access_key_id",
|
||||
"aws_secret_access_key",
|
||||
"aws_session_token",
|
||||
].includes(key)
|
||||
) {
|
||||
if (value !== undefined && value !== "") {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add any other valid field
|
||||
if (value !== undefined && value !== "") {
|
||||
formData.append(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
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;
|
||||
case "/data/attributes/secret/external_id":
|
||||
form.setError("external_id" 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}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error during submission:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Submission failed",
|
||||
description: "An error occurred while processing your request.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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" && (
|
||||
<AWSRoleCredentialsForm
|
||||
control={form.control as unknown as Control<AWSCredentialsRole>}
|
||||
setValue={
|
||||
form.setValue as unknown as UseFormSetValue<AWSCredentialsRole>
|
||||
}
|
||||
externalId={externalId || ""}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:space-x-6">
|
||||
{searchParamsObj.get("via") === "role" && (
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Back"
|
||||
className="w-1/2 bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
radius="lg"
|
||||
onPress={handleBackStep}
|
||||
startContent={!isLoading && <ChevronLeftIcon size={24} />}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
<span>Back</span>
|
||||
</CustomButton>
|
||||
)}
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel={"Save"}
|
||||
className="w-1/2"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
endContent={!isLoading && <ChevronRightIcon size={24} />}
|
||||
>
|
||||
{isLoading ? <>Loading</> : <span>Next</span>}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
export * from "./use-credentials-form";
|
||||
export * from "./use-form-server-errors";
|
||||
export * from "./use-local-storage";
|
||||
export * from "./use-sidebar";
|
||||
export * from "./use-store";
|
||||
export * from "./useLocalStorage";
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { useFormServerErrors } from "@/hooks/use-form-server-errors";
|
||||
import { filterEmptyValues } from "@/lib";
|
||||
import { PROVIDER_CREDENTIALS_ERROR_MAPPING } from "@/lib/error-mappings";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import {
|
||||
addCredentialsFormSchema,
|
||||
addCredentialsRoleFormSchema,
|
||||
addCredentialsServiceAccountFormSchema,
|
||||
ProviderType,
|
||||
} from "@/types";
|
||||
|
||||
type CredentialsFormData = {
|
||||
providerId: string;
|
||||
providerType: ProviderType;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
type UseCredentialsFormProps = {
|
||||
providerType: ProviderType;
|
||||
providerId: string;
|
||||
onSubmit: (formData: FormData) => Promise<any>;
|
||||
successNavigationUrl: string;
|
||||
};
|
||||
|
||||
export const useCredentialsForm = ({
|
||||
providerType,
|
||||
providerId,
|
||||
onSubmit,
|
||||
successNavigationUrl,
|
||||
}: UseCredentialsFormProps) => {
|
||||
const router = useRouter();
|
||||
const searchParamsObj = useSearchParams();
|
||||
const { data: session } = useSession();
|
||||
const via = searchParamsObj.get("via");
|
||||
|
||||
// Select the appropriate schema based on provider type and via parameter
|
||||
const getFormSchema = () => {
|
||||
if (providerType === "aws" && via === "role") {
|
||||
return addCredentialsRoleFormSchema(providerType);
|
||||
}
|
||||
if (providerType === "gcp" && via === "service-account") {
|
||||
return addCredentialsServiceAccountFormSchema(providerType);
|
||||
}
|
||||
return addCredentialsFormSchema(providerType);
|
||||
};
|
||||
|
||||
const formSchema = getFormSchema();
|
||||
|
||||
// Get default values based on provider type and via parameter
|
||||
const getDefaultValues = (): CredentialsFormData => {
|
||||
const baseDefaults = {
|
||||
[ProviderCredentialFields.PROVIDER_ID]: providerId,
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: providerType,
|
||||
};
|
||||
|
||||
// AWS Role credentials
|
||||
if (providerType === "aws" && via === "role") {
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.CREDENTIALS_TYPE]: "aws-sdk-default",
|
||||
[ProviderCredentialFields.ROLE_ARN]: "",
|
||||
[ProviderCredentialFields.EXTERNAL_ID]: session?.tenantId || "",
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: "",
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: "",
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: "",
|
||||
[ProviderCredentialFields.ROLE_SESSION_NAME]: "",
|
||||
[ProviderCredentialFields.SESSION_DURATION]: "3600",
|
||||
};
|
||||
}
|
||||
|
||||
// GCP Service Account
|
||||
if (providerType === "gcp" && via === "service-account") {
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.SERVICE_ACCOUNT_KEY]: "",
|
||||
};
|
||||
}
|
||||
|
||||
switch (providerType) {
|
||||
case "aws":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: "",
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: "",
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: "",
|
||||
};
|
||||
case "azure":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.CLIENT_ID]: "",
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: "",
|
||||
[ProviderCredentialFields.TENANT_ID]: "",
|
||||
};
|
||||
case "m365":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.CLIENT_ID]: "",
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: "",
|
||||
[ProviderCredentialFields.TENANT_ID]: "",
|
||||
[ProviderCredentialFields.USER]: "",
|
||||
[ProviderCredentialFields.PASSWORD]: "",
|
||||
};
|
||||
case "gcp":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.CLIENT_ID]: "",
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: "",
|
||||
[ProviderCredentialFields.REFRESH_TOKEN]: "",
|
||||
};
|
||||
case "kubernetes":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.KUBECONFIG_CONTENT]: "",
|
||||
};
|
||||
default:
|
||||
return baseDefaults;
|
||||
}
|
||||
};
|
||||
|
||||
const form = useForm<CredentialsFormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: getDefaultValues(),
|
||||
});
|
||||
|
||||
const { handleServerResponse } = useFormServerErrors(
|
||||
form,
|
||||
PROVIDER_CREDENTIALS_ERROR_MAPPING,
|
||||
);
|
||||
|
||||
// Handler for back button
|
||||
const handleBackStep = () => {
|
||||
const currentParams = new URLSearchParams(window.location.search);
|
||||
currentParams.delete("via");
|
||||
router.push(`?${currentParams.toString()}`);
|
||||
};
|
||||
|
||||
// Form submit handler
|
||||
const handleSubmit = async (values: CredentialsFormData) => {
|
||||
const formData = new FormData();
|
||||
|
||||
// Filter out empty values first, then append all remaining values
|
||||
const filteredValues = filterEmptyValues(values);
|
||||
Object.entries(filteredValues).forEach(([key, value]) => {
|
||||
formData.append(key, value);
|
||||
});
|
||||
|
||||
const data = await onSubmit(formData);
|
||||
|
||||
const isSuccess = handleServerResponse(data);
|
||||
if (isSuccess) {
|
||||
router.push(successNavigationUrl);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
form,
|
||||
isLoading: form.formState.isSubmitting,
|
||||
handleSubmit,
|
||||
handleBackStep,
|
||||
searchParamsObj,
|
||||
externalId: session?.tenantId || "",
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
|
||||
import { useToast } from "@/components/ui";
|
||||
import { ApiError } from "@/types";
|
||||
|
||||
/**
|
||||
* Generic hook for handling server errors in forms
|
||||
* Can be used across different types of forms, not just credential forms
|
||||
*/
|
||||
export const useFormServerErrors = <T extends Record<string, any>>(
|
||||
form: UseFormReturn<T>,
|
||||
customErrorMapping?: Record<string, string>,
|
||||
) => {
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleServerErrors = (
|
||||
errors: ApiError[],
|
||||
errorMapping?: Record<string, string>,
|
||||
) => {
|
||||
errors.forEach((error: ApiError) => {
|
||||
const errorMessage = error.detail;
|
||||
const fieldName = errorMapping?.[error.source.pointer];
|
||||
|
||||
if (fieldName && fieldName in form.formState.defaultValues!) {
|
||||
form.setError(fieldName as any, {
|
||||
type: "server",
|
||||
message: errorMessage,
|
||||
});
|
||||
} else {
|
||||
// Handle unknown error pointers with toast
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Oops! Something went wrong",
|
||||
description: errorMessage,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleServerResponse = (
|
||||
data: any,
|
||||
errorMapping?: Record<string, string>,
|
||||
) => {
|
||||
// Check for both error (singular) and errors (plural) from server responses
|
||||
if (data?.error) {
|
||||
// Handle single error from server
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Oops! Something went wrong",
|
||||
description: data.error,
|
||||
});
|
||||
return false; // Indicates error occurred
|
||||
} else if (data?.errors && data.errors.length > 0) {
|
||||
handleServerErrors(data.errors, errorMapping || customErrorMapping);
|
||||
return false; // Indicates error occurred
|
||||
}
|
||||
return true; // Indicates success
|
||||
};
|
||||
|
||||
return { handleServerResponse, handleServerErrors };
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
ErrorPointers,
|
||||
ProviderCredentialFields,
|
||||
} from "./provider-credentials/provider-credential-fields";
|
||||
|
||||
/**
|
||||
* Error pointer to field name mappings for different types of forms
|
||||
* These can be imported and used with the useFormServerErrors hook
|
||||
*/
|
||||
|
||||
// Mapping for provider credentials forms
|
||||
export const PROVIDER_CREDENTIALS_ERROR_MAPPING: Record<string, string> = {
|
||||
[ErrorPointers.AWS_ACCESS_KEY_ID]: ProviderCredentialFields.AWS_ACCESS_KEY_ID,
|
||||
[ErrorPointers.AWS_SECRET_ACCESS_KEY]:
|
||||
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY,
|
||||
[ErrorPointers.AWS_SESSION_TOKEN]: ProviderCredentialFields.AWS_SESSION_TOKEN,
|
||||
[ErrorPointers.CLIENT_ID]: ProviderCredentialFields.CLIENT_ID,
|
||||
[ErrorPointers.CLIENT_SECRET]: ProviderCredentialFields.CLIENT_SECRET,
|
||||
[ErrorPointers.USER]: ProviderCredentialFields.USER,
|
||||
[ErrorPointers.PASSWORD]: ProviderCredentialFields.PASSWORD,
|
||||
[ErrorPointers.TENANT_ID]: ProviderCredentialFields.TENANT_ID,
|
||||
[ErrorPointers.KUBECONFIG_CONTENT]:
|
||||
ProviderCredentialFields.KUBECONFIG_CONTENT,
|
||||
[ErrorPointers.REFRESH_TOKEN]: ProviderCredentialFields.REFRESH_TOKEN,
|
||||
[ErrorPointers.ROLE_ARN]: ProviderCredentialFields.ROLE_ARN,
|
||||
[ErrorPointers.EXTERNAL_ID]: ProviderCredentialFields.EXTERNAL_ID,
|
||||
[ErrorPointers.SESSION_DURATION]: ProviderCredentialFields.SESSION_DURATION,
|
||||
[ErrorPointers.ROLE_SESSION_NAME]: ProviderCredentialFields.ROLE_SESSION_NAME,
|
||||
[ErrorPointers.SERVICE_ACCOUNT_KEY]:
|
||||
ProviderCredentialFields.SERVICE_ACCOUNT_KEY,
|
||||
};
|
||||
@@ -7,6 +7,44 @@ import { AuthSocialProvider, MetaDataProps, PermissionInfo } from "@/types";
|
||||
export const baseUrl = process.env.AUTH_URL || "http://localhost:3000";
|
||||
export const apiBaseUrl = process.env.API_BASE_URL;
|
||||
|
||||
/**
|
||||
* Extracts a form value from a FormData object
|
||||
* @param formData - The FormData object to extract from
|
||||
* @param field - The name of the field to extract
|
||||
* @returns The value of the field
|
||||
*/
|
||||
export const getFormValue = (formData: FormData, field: string) =>
|
||||
formData.get(field);
|
||||
|
||||
/**
|
||||
* Filters out empty values from an object
|
||||
* @param obj - Object to filter
|
||||
* @returns New object with empty values removed
|
||||
* Avoids sending empty values to the API
|
||||
*/
|
||||
export function filterEmptyValues(
|
||||
obj: Record<string, any>,
|
||||
): Record<string, any> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([_, value]) => {
|
||||
// Keep number 0 and boolean false as they are valid values
|
||||
if (value === 0 || value === false) return true;
|
||||
|
||||
// Filter out null, undefined, empty strings, and empty arrays
|
||||
if (value === null || value === undefined) return false;
|
||||
if (typeof value === "string" && value.trim() === "") return false;
|
||||
if (Array.isArray(value) && value.length === 0) return false;
|
||||
|
||||
return true;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication headers for API requests
|
||||
* @param options - Optional configuration options
|
||||
* @returns Authentication headers with Accept and Authorization
|
||||
*/
|
||||
export const getAuthHeaders = async (options?: { contentType?: boolean }) => {
|
||||
const session = await auth();
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./error-mappings";
|
||||
export * from "./external-urls";
|
||||
export * from "./helper";
|
||||
export * from "./helper-filters";
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
import {
|
||||
filterEmptyValues,
|
||||
getErrorMessage,
|
||||
getFormValue,
|
||||
parseStringify,
|
||||
} from "@/lib";
|
||||
import { ProviderType } from "@/types";
|
||||
|
||||
import { ProviderCredentialFields } from "./provider-credential-fields";
|
||||
|
||||
// Helper functions for each provider type
|
||||
export const buildAWSSecret = (formData: FormData, isRole: boolean) => {
|
||||
if (isRole) {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.ROLE_ARN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.ROLE_ARN,
|
||||
),
|
||||
[ProviderCredentialFields.EXTERNAL_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.EXTERNAL_ID,
|
||||
),
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.AWS_ACCESS_KEY_ID,
|
||||
),
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY,
|
||||
),
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.AWS_SESSION_TOKEN,
|
||||
),
|
||||
session_duration:
|
||||
parseInt(
|
||||
getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.SESSION_DURATION,
|
||||
) as string,
|
||||
10,
|
||||
) || 3600,
|
||||
[ProviderCredentialFields.ROLE_SESSION_NAME]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.ROLE_SESSION_NAME,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
}
|
||||
|
||||
const secret = {
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.AWS_ACCESS_KEY_ID,
|
||||
),
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.AWS_SECRET_ACCESS_KEY,
|
||||
),
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.AWS_SESSION_TOKEN,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildAzureSecret = (formData: FormData) => {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.CLIENT_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.CLIENT_ID,
|
||||
),
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.CLIENT_SECRET,
|
||||
),
|
||||
[ProviderCredentialFields.TENANT_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.TENANT_ID,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildM365Secret = (formData: FormData) => {
|
||||
const secret = {
|
||||
...buildAzureSecret(formData),
|
||||
[ProviderCredentialFields.USER]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.USER,
|
||||
),
|
||||
[ProviderCredentialFields.PASSWORD]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.PASSWORD,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildGCPSecret = (
|
||||
formData: FormData,
|
||||
isServiceAccount: boolean,
|
||||
) => {
|
||||
if (isServiceAccount) {
|
||||
const serviceAccountKeyRaw = getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.SERVICE_ACCOUNT_KEY,
|
||||
) as string;
|
||||
|
||||
try {
|
||||
return {
|
||||
service_account_key: JSON.parse(serviceAccountKeyRaw),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Invalid service account key JSON:", error);
|
||||
throw new Error("Invalid service account key format");
|
||||
}
|
||||
}
|
||||
|
||||
const secret = {
|
||||
[ProviderCredentialFields.CLIENT_ID]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.CLIENT_ID,
|
||||
),
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.CLIENT_SECRET,
|
||||
),
|
||||
[ProviderCredentialFields.REFRESH_TOKEN]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.REFRESH_TOKEN,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildKubernetesSecret = (formData: FormData) => {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.KUBECONFIG_CONTENT]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.KUBECONFIG_CONTENT,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
// Main function to build secret configuration
|
||||
export const buildSecretConfig = (
|
||||
formData: FormData,
|
||||
providerType: ProviderType,
|
||||
) => {
|
||||
const isRole = formData.get(ProviderCredentialFields.ROLE_ARN) !== null;
|
||||
const isServiceAccount =
|
||||
formData.get(ProviderCredentialFields.SERVICE_ACCOUNT_KEY) !== null;
|
||||
|
||||
const secretBuilders = {
|
||||
aws: () => ({
|
||||
secretType: isRole ? "role" : "static",
|
||||
secret: buildAWSSecret(formData, isRole),
|
||||
}),
|
||||
azure: () => ({
|
||||
secretType: "static",
|
||||
secret: buildAzureSecret(formData),
|
||||
}),
|
||||
m365: () => ({
|
||||
secretType: "static",
|
||||
secret: buildM365Secret(formData),
|
||||
}),
|
||||
gcp: () => ({
|
||||
secretType: isServiceAccount ? "service_account" : "static",
|
||||
secret: buildGCPSecret(formData, isServiceAccount),
|
||||
}),
|
||||
kubernetes: () => ({
|
||||
secretType: "static",
|
||||
secret: buildKubernetesSecret(formData),
|
||||
}),
|
||||
};
|
||||
|
||||
const builder = secretBuilders[providerType];
|
||||
if (!builder) {
|
||||
throw new Error(`Unsupported provider type: ${providerType}`);
|
||||
}
|
||||
|
||||
return builder();
|
||||
};
|
||||
|
||||
// Helper function to build secret for update (reuses existing logic)
|
||||
export const buildUpdateSecretConfig = (
|
||||
formData: FormData,
|
||||
providerType: ProviderType,
|
||||
) => {
|
||||
// Reuse the same secret building logic as add, but only return the secret
|
||||
const { secret } = buildSecretConfig(formData, providerType);
|
||||
|
||||
// Handle special case for M365 password field inconsistency
|
||||
if (providerType === "m365") {
|
||||
return {
|
||||
...secret,
|
||||
password: formData.get(ProviderCredentialFields.PASSWORD),
|
||||
};
|
||||
}
|
||||
|
||||
return secret;
|
||||
};
|
||||
|
||||
// Helper function to handle API responses consistently
|
||||
export const handleApiResponse = async (
|
||||
response: Response,
|
||||
pathToRevalidate?: string,
|
||||
) => {
|
||||
const data = await response.json();
|
||||
|
||||
if (pathToRevalidate) {
|
||||
revalidatePath(pathToRevalidate);
|
||||
}
|
||||
|
||||
return parseStringify(data);
|
||||
};
|
||||
|
||||
// Helper function to handle API errors consistently
|
||||
export const handleApiError = (error: unknown) => {
|
||||
console.error(error);
|
||||
return {
|
||||
error: getErrorMessage(error),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Centralized credential field names to avoid hardcoded strings
|
||||
* and provide type safety across the application
|
||||
*/
|
||||
|
||||
// Provider credential field names
|
||||
export const ProviderCredentialFields = {
|
||||
CREDENTIALS_TYPE: "credentials_type",
|
||||
CREDENTIALS_TYPE_AWS: "aws-sdk-default",
|
||||
CREDENTIALS_TYPE_ACCESS_SECRET_KEY: "access-secret-key",
|
||||
// Base fields for all providers
|
||||
PROVIDER_ID: "providerId",
|
||||
PROVIDER_TYPE: "providerType",
|
||||
PROVIDER_ALIAS: "providerAlias",
|
||||
|
||||
// AWS fields
|
||||
AWS_ACCESS_KEY_ID: "aws_access_key_id",
|
||||
AWS_SECRET_ACCESS_KEY: "aws_secret_access_key",
|
||||
AWS_SESSION_TOKEN: "aws_session_token",
|
||||
ROLE_ARN: "role_arn",
|
||||
EXTERNAL_ID: "external_id",
|
||||
SESSION_DURATION: "session_duration",
|
||||
ROLE_SESSION_NAME: "role_session_name",
|
||||
|
||||
// Azure/M365 fields
|
||||
CLIENT_ID: "client_id",
|
||||
CLIENT_SECRET: "client_secret",
|
||||
TENANT_ID: "tenant_id",
|
||||
USER: "user",
|
||||
PASSWORD: "password",
|
||||
|
||||
// GCP fields
|
||||
REFRESH_TOKEN: "refresh_token",
|
||||
SERVICE_ACCOUNT_KEY: "service_account_key",
|
||||
|
||||
// Kubernetes fields
|
||||
KUBECONFIG_CONTENT: "kubeconfig_content",
|
||||
} as const;
|
||||
|
||||
// Type for credential field values
|
||||
export type ProviderCredentialField =
|
||||
(typeof ProviderCredentialFields)[keyof typeof ProviderCredentialFields];
|
||||
|
||||
// API error pointer paths
|
||||
export const ErrorPointers = {
|
||||
// Secret fields
|
||||
AWS_ACCESS_KEY_ID: "/data/attributes/secret/aws_access_key_id",
|
||||
AWS_SECRET_ACCESS_KEY: "/data/attributes/secret/aws_secret_access_key",
|
||||
AWS_SESSION_TOKEN: "/data/attributes/secret/aws_session_token",
|
||||
CLIENT_ID: "/data/attributes/secret/client_id",
|
||||
CLIENT_SECRET: "/data/attributes/secret/client_secret",
|
||||
USER: "/data/attributes/secret/user",
|
||||
PASSWORD: "/data/attributes/secret/password",
|
||||
TENANT_ID: "/data/attributes/secret/tenant_id",
|
||||
KUBECONFIG_CONTENT: "/data/attributes/secret/kubeconfig_content",
|
||||
REFRESH_TOKEN: "/data/attributes/secret/refresh_token",
|
||||
ROLE_ARN: "/data/attributes/secret/role_arn",
|
||||
EXTERNAL_ID: "/data/attributes/secret/external_id",
|
||||
SESSION_DURATION: "/data/attributes/secret/session_duration",
|
||||
ROLE_SESSION_NAME: "/data/attributes/secret/role_session_name",
|
||||
SERVICE_ACCOUNT_KEY: "/data/attributes/secret/service_account_key",
|
||||
} as const;
|
||||
|
||||
export type ErrorPointer = (typeof ErrorPointers)[keyof typeof ErrorPointers];
|
||||
+29
-31
@@ -1,6 +1,8 @@
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
|
||||
export type IconSvgProps = SVGProps<SVGSVGElement> & {
|
||||
size?: number;
|
||||
};
|
||||
@@ -179,39 +181,38 @@ export interface TaskDetails {
|
||||
};
|
||||
}
|
||||
export type AWSCredentials = {
|
||||
aws_access_key_id: string;
|
||||
aws_secret_access_key: string;
|
||||
aws_session_token: string;
|
||||
secretName: string;
|
||||
providerId: string;
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: string;
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: string;
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: 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;
|
||||
credentials_type?: "aws-sdk-default" | "access-secret-key";
|
||||
[ProviderCredentialFields.ROLE_ARN]: string;
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]?: string;
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]?: string;
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]?: string;
|
||||
[ProviderCredentialFields.EXTERNAL_ID]?: string;
|
||||
[ProviderCredentialFields.ROLE_SESSION_NAME]?: string;
|
||||
[ProviderCredentialFields.SESSION_DURATION]?: number;
|
||||
[ProviderCredentialFields.CREDENTIALS_TYPE]?:
|
||||
| "aws-sdk-default"
|
||||
| "access-secret-key";
|
||||
};
|
||||
|
||||
export type AzureCredentials = {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
tenant_id: string;
|
||||
secretName: string;
|
||||
providerId: string;
|
||||
[ProviderCredentialFields.CLIENT_ID]: string;
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: string;
|
||||
[ProviderCredentialFields.TENANT_ID]: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type M365Credentials = {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
tenant_id: string;
|
||||
user: string;
|
||||
password: string;
|
||||
secretName: string;
|
||||
[ProviderCredentialFields.CLIENT_ID]: string;
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: string;
|
||||
[ProviderCredentialFields.TENANT_ID]: string;
|
||||
[ProviderCredentialFields.USER]: string;
|
||||
[ProviderCredentialFields.PASSWORD]: string;
|
||||
providerId: string;
|
||||
};
|
||||
|
||||
@@ -219,20 +220,17 @@ export type GCPDefaultCredentials = {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
refresh_token: string;
|
||||
secretName: string;
|
||||
providerId: string;
|
||||
};
|
||||
|
||||
export type GCPServiceAccountKey = {
|
||||
service_account_key: string;
|
||||
secretName: string;
|
||||
providerId: string;
|
||||
[ProviderCredentialFields.SERVICE_ACCOUNT_KEY]: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type KubernetesCredentials = {
|
||||
kubeconfig_content: string;
|
||||
secretName: string;
|
||||
providerId: string;
|
||||
[ProviderCredentialFields.KUBECONFIG_CONTENT]: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type CredentialsFormSchema =
|
||||
|
||||
+74
-48
@@ -1,5 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
|
||||
import { ProviderType } from "./providers";
|
||||
|
||||
export const addRoleFormSchema = z.object({
|
||||
@@ -42,7 +44,7 @@ export const editScanFormSchema = (currentName: string) =>
|
||||
|
||||
export const onDemandScanFormSchema = () =>
|
||||
z.object({
|
||||
providerId: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
scanName: z.string().optional(),
|
||||
scannerArgs: z
|
||||
.object({
|
||||
@@ -73,30 +75,30 @@ export const addProviderFormSchema = z
|
||||
z.discriminatedUnion("providerType", [
|
||||
z.object({
|
||||
providerType: z.literal("aws"),
|
||||
providerAlias: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("azure"),
|
||||
providerAlias: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
awsCredentialsType: z.string().optional(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("m365"),
|
||||
providerAlias: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
awsCredentialsType: z.string().optional(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("gcp"),
|
||||
providerAlias: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
awsCredentialsType: z.string().optional(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("kubernetes"),
|
||||
providerAlias: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
awsCredentialsType: z.string().optional(),
|
||||
}),
|
||||
@@ -105,46 +107,65 @@ export const addProviderFormSchema = z
|
||||
|
||||
export const addCredentialsFormSchema = (providerType: string) =>
|
||||
z.object({
|
||||
secretName: z.string().optional(),
|
||||
providerId: z.string(),
|
||||
providerType: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: z.string(),
|
||||
...(providerType === "aws"
|
||||
? {
|
||||
aws_access_key_id: z
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: z
|
||||
.string()
|
||||
.nonempty("AWS Access Key ID is required"),
|
||||
aws_secret_access_key: z
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: z
|
||||
.string()
|
||||
.nonempty("AWS Secret Access Key is required"),
|
||||
aws_session_token: z.string().optional(),
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: z.string().optional(),
|
||||
}
|
||||
: providerType === "azure"
|
||||
? {
|
||||
client_id: z.string().nonempty("Client ID is required"),
|
||||
client_secret: z.string().nonempty("Client Secret is required"),
|
||||
tenant_id: z.string().nonempty("Tenant ID is required"),
|
||||
[ProviderCredentialFields.CLIENT_ID]: z
|
||||
.string()
|
||||
.nonempty("Client ID is required"),
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: z
|
||||
.string()
|
||||
.nonempty("Client Secret is required"),
|
||||
[ProviderCredentialFields.TENANT_ID]: z
|
||||
.string()
|
||||
.nonempty("Tenant ID is required"),
|
||||
}
|
||||
: providerType === "gcp"
|
||||
? {
|
||||
client_id: z.string().nonempty("Client ID is required"),
|
||||
client_secret: z.string().nonempty("Client Secret is required"),
|
||||
refresh_token: z.string().nonempty("Refresh Token is required"),
|
||||
[ProviderCredentialFields.CLIENT_ID]: z
|
||||
.string()
|
||||
.nonempty("Client ID is required"),
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: z
|
||||
.string()
|
||||
.nonempty("Client Secret is required"),
|
||||
[ProviderCredentialFields.REFRESH_TOKEN]: z
|
||||
.string()
|
||||
.nonempty("Refresh Token is required"),
|
||||
}
|
||||
: providerType === "kubernetes"
|
||||
? {
|
||||
kubeconfig_content: z
|
||||
[ProviderCredentialFields.KUBECONFIG_CONTENT]: z
|
||||
.string()
|
||||
.nonempty("Kubeconfig Content is required"),
|
||||
}
|
||||
: providerType === "m365"
|
||||
? {
|
||||
client_id: z.string().nonempty("Client ID is required"),
|
||||
client_secret: z
|
||||
[ProviderCredentialFields.CLIENT_ID]: z
|
||||
.string()
|
||||
.nonempty("Client ID is required"),
|
||||
[ProviderCredentialFields.CLIENT_SECRET]: z
|
||||
.string()
|
||||
.nonempty("Client Secret is required"),
|
||||
tenant_id: z.string().nonempty("Tenant ID is required"),
|
||||
user: z.string().nonempty("User is required"),
|
||||
password: z.string().nonempty("Password is required"),
|
||||
[ProviderCredentialFields.TENANT_ID]: z
|
||||
.string()
|
||||
.nonempty("Tenant ID is required"),
|
||||
[ProviderCredentialFields.USER]: z
|
||||
.string()
|
||||
.nonempty("User is required"),
|
||||
[ProviderCredentialFields.PASSWORD]: z
|
||||
.string()
|
||||
.nonempty("Password is required"),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
@@ -153,24 +174,30 @@ export const addCredentialsRoleFormSchema = (providerType: string) =>
|
||||
providerType === "aws"
|
||||
? z
|
||||
.object({
|
||||
providerId: z.string(),
|
||||
providerType: z.string(),
|
||||
role_arn: z.string().nonempty("AWS Role ARN is required"),
|
||||
external_id: 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.string().optional(),
|
||||
role_session_name: z.string().optional(),
|
||||
credentials_type: z.string().optional(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: z.string(),
|
||||
[ProviderCredentialFields.ROLE_ARN]: z
|
||||
.string()
|
||||
.nonempty("AWS Role ARN is required"),
|
||||
[ProviderCredentialFields.EXTERNAL_ID]: z.string().optional(),
|
||||
[ProviderCredentialFields.AWS_ACCESS_KEY_ID]: z.string().optional(),
|
||||
[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]: z
|
||||
.string()
|
||||
.optional(),
|
||||
[ProviderCredentialFields.AWS_SESSION_TOKEN]: z.string().optional(),
|
||||
[ProviderCredentialFields.SESSION_DURATION]: z.string().optional(),
|
||||
[ProviderCredentialFields.ROLE_SESSION_NAME]: z.string().optional(),
|
||||
[ProviderCredentialFields.CREDENTIALS_TYPE]: z.string().optional(),
|
||||
})
|
||||
.refine(
|
||||
(data) =>
|
||||
data.credentials_type !== "access-secret-key" ||
|
||||
(data.aws_access_key_id && data.aws_secret_access_key),
|
||||
data[ProviderCredentialFields.CREDENTIALS_TYPE] !==
|
||||
"access-secret-key" ||
|
||||
(data[ProviderCredentialFields.AWS_ACCESS_KEY_ID] &&
|
||||
data[ProviderCredentialFields.AWS_SECRET_ACCESS_KEY]),
|
||||
{
|
||||
message: "AWS Access Key ID and Secret Access Key are required.",
|
||||
path: ["aws_access_key_id"],
|
||||
path: [ProviderCredentialFields.AWS_ACCESS_KEY_ID],
|
||||
},
|
||||
)
|
||||
: z.object({
|
||||
@@ -183,9 +210,9 @@ export const addCredentialsServiceAccountFormSchema = (
|
||||
) =>
|
||||
providerType === "gcp"
|
||||
? z.object({
|
||||
providerId: z.string(),
|
||||
providerType: z.string(),
|
||||
service_account_key: z.string().refine(
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: z.string(),
|
||||
[ProviderCredentialFields.SERVICE_ACCOUNT_KEY]: z.string().refine(
|
||||
(val) => {
|
||||
try {
|
||||
const parsed = JSON.parse(val);
|
||||
@@ -202,22 +229,21 @@ export const addCredentialsServiceAccountFormSchema = (
|
||||
message: "Invalid JSON format. Please provide a valid JSON object.",
|
||||
},
|
||||
),
|
||||
secretName: z.string().optional(),
|
||||
})
|
||||
: z.object({
|
||||
providerId: z.string(),
|
||||
providerType: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: z.string(),
|
||||
});
|
||||
|
||||
export const testConnectionFormSchema = z.object({
|
||||
providerId: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
runOnce: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const launchScanFormSchema = () =>
|
||||
z.object({
|
||||
providerId: z.string(),
|
||||
providerType: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: z.string(),
|
||||
scannerArgs: z
|
||||
.object({
|
||||
checksToExecute: z.array(z.string()).optional(),
|
||||
@@ -227,7 +253,7 @@ export const launchScanFormSchema = () =>
|
||||
|
||||
export const editProviderFormSchema = (currentAlias: string) =>
|
||||
z.object({
|
||||
alias: z
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z
|
||||
.string()
|
||||
.refine((val) => val === "" || val.length >= 3, {
|
||||
message: "The alias must be empty or have at least 3 characters.",
|
||||
@@ -236,7 +262,7 @@ export const editProviderFormSchema = (currentAlias: string) =>
|
||||
message: "The new alias must be different from the current one.",
|
||||
})
|
||||
.optional(),
|
||||
providerId: z.string(),
|
||||
[ProviderCredentialFields.PROVIDER_ID]: z.string(),
|
||||
});
|
||||
|
||||
export const editInviteFormSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user