mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-10 13:34:16 +00:00
336 lines
8.9 KiB
TypeScript
336 lines
8.9 KiB
TypeScript
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
|
|
import { apiBaseUrl, getAuthHeaders } from "@/lib/helper";
|
|
import { handleApiResponse } from "@/lib/server-actions-helper";
|
|
import { isCloud } from "@/lib/shared/env";
|
|
import type { ApiResponse } from "@/types/components";
|
|
import {
|
|
samlConfigFormSchema,
|
|
samlConfigUpdateFormSchema,
|
|
} from "@/types/formSchemas";
|
|
import {
|
|
SAML_CONFIGURATION_RESOURCE_TYPE,
|
|
type SamlConfigurationActionState,
|
|
type SamlConfigurationCreateRequestAttributes,
|
|
type SamlConfigurationErrors,
|
|
type SamlConfigurationUpdateRequestAttributes,
|
|
} from "@/types/saml";
|
|
|
|
const getSamlFormValues = (formData: FormData) => ({
|
|
email_domain: formData.get("email_domain"),
|
|
additional_email_domains: formData.getAll("additional_email_domains"),
|
|
metadata_xml: formData.get("metadata_xml"),
|
|
});
|
|
|
|
const parseSamlCreateFormData = (formData: FormData) =>
|
|
samlConfigFormSchema.safeParse(getSamlFormValues(formData));
|
|
|
|
const parseSamlUpdateFormData = (formData: FormData) =>
|
|
samlConfigUpdateFormSchema.safeParse(getSamlFormValues(formData));
|
|
|
|
const mapApiErrorsToFields = (
|
|
result: ApiResponse,
|
|
fallback: string,
|
|
): SamlConfigurationErrors => {
|
|
const errors: SamlConfigurationErrors = {};
|
|
|
|
result.errors?.forEach((error) => {
|
|
const pointer = error.source?.pointer;
|
|
if (pointer?.includes("additional_email_domains")) {
|
|
errors.additional_email_domains = error.detail;
|
|
} else if (pointer?.includes("email_domain")) {
|
|
errors.email_domain = error.detail;
|
|
} else if (pointer?.includes("metadata_xml")) {
|
|
errors.metadata_xml = error.detail;
|
|
} else {
|
|
errors.general = error.detail;
|
|
}
|
|
});
|
|
|
|
if (Object.keys(errors).length === 0) {
|
|
errors.general = result.error || fallback;
|
|
}
|
|
|
|
return errors;
|
|
};
|
|
|
|
const buildSamlRequestAttributes = (
|
|
emailDomain: string,
|
|
additionalEmailDomains: string[],
|
|
): SamlConfigurationUpdateRequestAttributes => ({
|
|
email_domain: emailDomain,
|
|
...(isCloud() && {
|
|
additional_email_domains: additionalEmailDomains,
|
|
}),
|
|
});
|
|
|
|
const buildSamlCreateRequestAttributes = (
|
|
emailDomain: string,
|
|
additionalEmailDomains: string[],
|
|
metadataXml: string,
|
|
): SamlConfigurationCreateRequestAttributes => ({
|
|
...buildSamlRequestAttributes(emailDomain, additionalEmailDomains),
|
|
metadata_xml: metadataXml,
|
|
});
|
|
|
|
const buildSamlUpdateRequestAttributes = (
|
|
emailDomain: string,
|
|
additionalEmailDomains: string[],
|
|
metadataXml?: string,
|
|
): SamlConfigurationUpdateRequestAttributes => ({
|
|
...buildSamlRequestAttributes(emailDomain, additionalEmailDomains),
|
|
...(metadataXml !== undefined && { metadata_xml: metadataXml }),
|
|
});
|
|
|
|
export const createSamlConfig = async (
|
|
_prevState: SamlConfigurationActionState,
|
|
formData: FormData,
|
|
): Promise<SamlConfigurationActionState> => {
|
|
const headers = await getAuthHeaders({ contentType: true });
|
|
const validatedData = parseSamlCreateFormData(formData);
|
|
|
|
if (!validatedData.success) {
|
|
const formFieldErrors = validatedData.error.flatten().fieldErrors;
|
|
|
|
return {
|
|
errors: {
|
|
email_domain: formFieldErrors?.email_domain?.[0],
|
|
additional_email_domains:
|
|
formFieldErrors?.additional_email_domains?.[0],
|
|
metadata_xml: formFieldErrors?.metadata_xml?.[0],
|
|
},
|
|
};
|
|
}
|
|
|
|
const { email_domain, additional_email_domains, metadata_xml } =
|
|
validatedData.data;
|
|
|
|
try {
|
|
const url = new URL(`${apiBaseUrl}/saml-config`);
|
|
const response = await fetch(url.toString(), {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({
|
|
data: {
|
|
type: SAML_CONFIGURATION_RESOURCE_TYPE,
|
|
attributes: buildSamlCreateRequestAttributes(
|
|
email_domain,
|
|
additional_email_domains,
|
|
metadata_xml,
|
|
),
|
|
},
|
|
}),
|
|
});
|
|
|
|
const result = (await handleApiResponse(
|
|
response,
|
|
"/profile",
|
|
false,
|
|
)) as ApiResponse;
|
|
if (result.error) {
|
|
return {
|
|
errors: mapApiErrorsToFields(
|
|
result,
|
|
"Error creating SAML configuration. Please try again.",
|
|
),
|
|
};
|
|
}
|
|
|
|
return { success: "SAML configuration created successfully!" };
|
|
} catch (error) {
|
|
console.error("Error creating SAML config:", error);
|
|
return {
|
|
errors: {
|
|
general:
|
|
error instanceof Error
|
|
? error.message
|
|
: "Error creating SAML configuration. Please try again.",
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
export const updateSamlConfig = async (
|
|
_prevState: SamlConfigurationActionState,
|
|
formData: FormData,
|
|
): Promise<SamlConfigurationActionState> => {
|
|
const id = String(formData.get("id") || "");
|
|
if (!id) {
|
|
return {
|
|
errors: {
|
|
general: "SAML configuration ID is required for update.",
|
|
},
|
|
};
|
|
}
|
|
|
|
const headers = await getAuthHeaders({ contentType: true });
|
|
const validatedData = parseSamlUpdateFormData(formData);
|
|
|
|
if (!validatedData.success) {
|
|
const formFieldErrors = validatedData.error.flatten().fieldErrors;
|
|
|
|
return {
|
|
errors: {
|
|
email_domain: formFieldErrors?.email_domain?.[0],
|
|
additional_email_domains:
|
|
formFieldErrors?.additional_email_domains?.[0],
|
|
metadata_xml: formFieldErrors?.metadata_xml?.[0],
|
|
},
|
|
};
|
|
}
|
|
|
|
const { email_domain, additional_email_domains, metadata_xml } =
|
|
validatedData.data;
|
|
|
|
try {
|
|
const url = new URL(`${apiBaseUrl}/saml-config/${id}`);
|
|
const response = await fetch(url.toString(), {
|
|
method: "PATCH",
|
|
headers,
|
|
body: JSON.stringify({
|
|
data: {
|
|
type: SAML_CONFIGURATION_RESOURCE_TYPE,
|
|
id,
|
|
attributes: buildSamlUpdateRequestAttributes(
|
|
email_domain,
|
|
additional_email_domains,
|
|
metadata_xml,
|
|
),
|
|
},
|
|
}),
|
|
});
|
|
|
|
const result = (await handleApiResponse(
|
|
response,
|
|
"/profile",
|
|
false,
|
|
)) as ApiResponse;
|
|
if (result.error) {
|
|
return {
|
|
errors: mapApiErrorsToFields(
|
|
result,
|
|
"Error updating SAML configuration. Please try again.",
|
|
),
|
|
};
|
|
}
|
|
return { success: "SAML configuration updated successfully!" };
|
|
} catch (error) {
|
|
console.error("Error updating SAML config:", error);
|
|
return {
|
|
errors: {
|
|
general:
|
|
error instanceof Error
|
|
? error.message
|
|
: "Error updating SAML configuration. Please try again.",
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
export const getSamlConfig = async () => {
|
|
const headers = await getAuthHeaders({ contentType: false });
|
|
const url = new URL(`${apiBaseUrl}/saml-config`);
|
|
|
|
try {
|
|
const response = await fetch(url.toString(), {
|
|
method: "GET",
|
|
headers,
|
|
});
|
|
|
|
return handleApiResponse(response);
|
|
} catch (error) {
|
|
console.error("Error fetching SAML config:", error);
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
export const deleteSamlConfig = async (id: string) => {
|
|
const headers = await getAuthHeaders({ contentType: true });
|
|
|
|
try {
|
|
const url = new URL(`${apiBaseUrl}/saml-config/${id}`);
|
|
const response = await fetch(url.toString(), {
|
|
method: "DELETE",
|
|
headers,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({}));
|
|
throw new Error(
|
|
errorData.errors?.[0]?.detail ||
|
|
`Failed to delete SAML config: ${response.statusText}`,
|
|
);
|
|
}
|
|
|
|
revalidatePath("/profile");
|
|
return { success: "SAML configuration deleted successfully!" };
|
|
} catch (error) {
|
|
console.error("Error deleting SAML config:", error);
|
|
return {
|
|
errors: {
|
|
general:
|
|
error instanceof Error
|
|
? error.message
|
|
: "Error deleting SAML configuration. Please try again.",
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
export const initiateSamlAuth = async (email: string, callbackUrl = "/") => {
|
|
try {
|
|
const attributes = {
|
|
email_domain: email,
|
|
...(callbackUrl !== "/" && { callback_url: callbackUrl }),
|
|
};
|
|
|
|
const response = await fetch(`${apiBaseUrl}/auth/saml/initiate/`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/vnd.api+json",
|
|
},
|
|
body: JSON.stringify({
|
|
data: {
|
|
type: "saml-initiate",
|
|
attributes,
|
|
},
|
|
}),
|
|
redirect: "manual",
|
|
});
|
|
|
|
if (response.status === 302) {
|
|
const location = response.headers.get("Location");
|
|
|
|
if (location) {
|
|
return {
|
|
success: true,
|
|
redirectUrl: location,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (response.status === 403) {
|
|
return {
|
|
success: false,
|
|
error: "Domain is not authorized for SAML authentication.",
|
|
};
|
|
}
|
|
|
|
// Add error other error case:
|
|
const errorData = await response.json().catch(() => ({}));
|
|
return {
|
|
success: false,
|
|
error:
|
|
errorData.errors?.[0]?.detail ||
|
|
"An error occurred during SAML authentication.",
|
|
};
|
|
} catch (_error) {
|
|
return {
|
|
success: false,
|
|
error: "Failed to connect to authentication service.",
|
|
};
|
|
}
|
|
};
|