mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Address greptile comments
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||||
|
import RE2 from "re2";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { CertificatesSchema } from "@app/db/schemas";
|
import { CertificatesSchema } from "@app/db/schemas";
|
||||||
@@ -632,8 +633,11 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber)
|
serialNumber: z.string().trim().describe(CERTIFICATES.GET.serialNumber)
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
password: z.string().describe("Password for the keystore"),
|
password: z
|
||||||
alias: z.string().describe("Alias for the certificate in the keystore")
|
.string()
|
||||||
|
.min(6, "Password must be at least 6 characters long")
|
||||||
|
.describe("Password for the keystore (minimum 6 characters)"),
|
||||||
|
alias: z.string().min(1, "Alias is required").describe("Alias for the certificate in the keystore")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.any().describe("PKCS12 keystore as binary data")
|
200: z.any().describe("PKCS12 keystore as binary data")
|
||||||
@@ -665,7 +669,10 @@ export const registerCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
|
|
||||||
addNoCacheHeaders(reply);
|
addNoCacheHeaders(reply);
|
||||||
reply.header("Content-Type", "application/octet-stream");
|
reply.header("Content-Type", "application/octet-stream");
|
||||||
reply.header("Content-Disposition", `attachment; filename="certificate-${req.params.serialNumber}.p12"`);
|
reply.header(
|
||||||
|
"Content-Disposition",
|
||||||
|
`attachment; filename="certificate-${req.params.serialNumber.replace(new RE2("[^\\w.-]", "g"), "_")}.p12"`
|
||||||
|
);
|
||||||
|
|
||||||
return pkcs12Data;
|
return pkcs12Data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -675,6 +675,12 @@ export const certificateServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "Password is required for PKCS12 keystore generation" });
|
throw new BadRequestError({ message: "Password is required for PKCS12 keystore generation" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (password.length < 6) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Password must be at least 6 characters long for PKCS12 keystore security"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!alias || alias.trim() === "") {
|
if (!alias || alias.trim() === "") {
|
||||||
throw new BadRequestError({ message: "Alias is required for PKCS12 keystore generation" });
|
throw new BadRequestError({ message: "Alias is required for PKCS12 keystore generation" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,28 +139,38 @@ export const useUpdateRenewalConfig = () => {
|
|||||||
export const useDownloadCertPkcs12 = () => {
|
export const useDownloadCertPkcs12 = () => {
|
||||||
return useMutation<void, object, TDownloadPkcs12DTO>({
|
return useMutation<void, object, TDownloadPkcs12DTO>({
|
||||||
mutationFn: async ({ serialNumber, projectSlug, password, alias }) => {
|
mutationFn: async ({ serialNumber, projectSlug, password, alias }) => {
|
||||||
const response = await apiRequest.post(
|
try {
|
||||||
`/api/v1/pki/certificates/${serialNumber}/pkcs12`,
|
const response = await apiRequest.post(
|
||||||
{
|
`/api/v1/pki/certificates/${serialNumber}/pkcs12`,
|
||||||
password,
|
{
|
||||||
alias
|
password,
|
||||||
},
|
alias
|
||||||
{
|
},
|
||||||
params: { projectSlug },
|
{
|
||||||
responseType: "arraybuffer"
|
params: { projectSlug },
|
||||||
}
|
responseType: "arraybuffer"
|
||||||
);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Create blob and trigger download
|
// Create blob and trigger download
|
||||||
const blob = new Blob([response.data], { type: "application/octet-stream" });
|
const blob = new Blob([response.data], { type: "application/octet-stream" });
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.download = `certificate-${serialNumber}.p12`;
|
link.download = `certificate-${serialNumber}.p12`;
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.response?.data instanceof ArrayBuffer) {
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const errorText = decoder.decode(error.response.data);
|
||||||
|
const errorData = JSON.parse(errorText);
|
||||||
|
throw new Error(errorData.message);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,13 +19,17 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["certificateExport"]>,
|
popUpName: keyof UsePopUpState<["certificateExport"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
onFormatSelected: (format: "pem" | "jks", serialNumber: string, options?: ExportOptions) => void;
|
onFormatSelected: (
|
||||||
|
format: "pem" | "pkcs12",
|
||||||
|
serialNumber: string,
|
||||||
|
options?: ExportOptions
|
||||||
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CertificateExportFormat = "pem" | "jks";
|
export type CertificateExportFormat = "pem" | "pkcs12";
|
||||||
|
|
||||||
export type ExportOptions = {
|
export type ExportOptions = {
|
||||||
jks?: {
|
pkcs12?: {
|
||||||
password: string;
|
password: string;
|
||||||
alias: string;
|
alias: string;
|
||||||
};
|
};
|
||||||
@@ -33,7 +37,7 @@ export type ExportOptions = {
|
|||||||
|
|
||||||
export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelected }: Props) => {
|
export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelected }: Props) => {
|
||||||
const [selectedFormat, setSelectedFormat] = useState<CertificateExportFormat>("pem");
|
const [selectedFormat, setSelectedFormat] = useState<CertificateExportFormat>("pem");
|
||||||
const [jksOptions, setJksOptions] = useState({
|
const [pkcs12Options, setPkcs12Options] = useState({
|
||||||
password: "",
|
password: "",
|
||||||
alias: ""
|
alias: ""
|
||||||
});
|
});
|
||||||
@@ -45,7 +49,7 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (popUp?.certificateExport?.isOpen) {
|
if (popUp?.certificateExport?.isOpen) {
|
||||||
setSelectedFormat("pem");
|
setSelectedFormat("pem");
|
||||||
setJksOptions({
|
setPkcs12Options({
|
||||||
password: "",
|
password: "",
|
||||||
alias: ""
|
alias: ""
|
||||||
});
|
});
|
||||||
@@ -53,8 +57,8 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
|
|||||||
}, [popUp?.certificateExport?.isOpen]);
|
}, [popUp?.certificateExport?.isOpen]);
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
if (selectedFormat === "jks") {
|
if (selectedFormat === "pkcs12") {
|
||||||
return jksOptions.password.trim() !== "" && jksOptions.alias.trim() !== "";
|
return pkcs12Options.password.length >= 6 && pkcs12Options.alias.trim() !== "";
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -63,8 +67,8 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
|
|||||||
if (serialNumber && isFormValid()) {
|
if (serialNumber && isFormValid()) {
|
||||||
const options: ExportOptions = {};
|
const options: ExportOptions = {};
|
||||||
|
|
||||||
if (selectedFormat === "jks") {
|
if (selectedFormat === "pkcs12") {
|
||||||
options.jks = jksOptions;
|
options.pkcs12 = pkcs12Options;
|
||||||
}
|
}
|
||||||
|
|
||||||
onFormatSelected(selectedFormat, serialNumber, options);
|
onFormatSelected(selectedFormat, serialNumber, options);
|
||||||
@@ -97,20 +101,28 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
|
|||||||
onValueChange={(value) => setSelectedFormat(value as CertificateExportFormat)}
|
onValueChange={(value) => setSelectedFormat(value as CertificateExportFormat)}
|
||||||
>
|
>
|
||||||
<SelectItem value="pem">PEM Format</SelectItem>
|
<SelectItem value="pem">PEM Format</SelectItem>
|
||||||
<SelectItem value="jks">PKCS12 Format</SelectItem>
|
<SelectItem value="pkcs12">PKCS12 Format</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
{selectedFormat === "jks" && (
|
{selectedFormat === "pkcs12" && (
|
||||||
<>
|
<>
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Keystore Password"
|
label="Keystore Password"
|
||||||
helperText="Password to protect the PKCS12 keystore"
|
helperText={
|
||||||
|
pkcs12Options.password.length > 0 && pkcs12Options.password.length < 6
|
||||||
|
? undefined
|
||||||
|
: "Password to protect the PKCS12 keystore (minimum 6 characters)"
|
||||||
|
}
|
||||||
|
isError={pkcs12Options.password.length > 0 && pkcs12Options.password.length < 6}
|
||||||
|
errorText="Password must be at least 6 characters long"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Enter keystore password"
|
placeholder="Enter keystore password"
|
||||||
value={jksOptions.password}
|
value={pkcs12Options.password}
|
||||||
onChange={(e) => setJksOptions((prev) => ({ ...prev, password: e.target.value }))}
|
onChange={(e) =>
|
||||||
|
setPkcs12Options((prev) => ({ ...prev, password: e.target.value }))
|
||||||
|
}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -121,8 +133,8 @@ export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelec
|
|||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Enter certificate alias"
|
placeholder="Enter certificate alias"
|
||||||
value={jksOptions.alias}
|
value={pkcs12Options.alias}
|
||||||
onChange={(e) => setJksOptions((prev) => ({ ...prev, alias: e.target.value }))}
|
onChange={(e) => setPkcs12Options((prev) => ({ ...prev, alias: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -53,16 +53,16 @@ export const CertificatesSection = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCertificateExport = async (
|
const handleCertificateExport = async (
|
||||||
format: "pem" | "jks",
|
format: "pem" | "pkcs12",
|
||||||
serialNumber: string,
|
serialNumber: string,
|
||||||
options?: ExportOptions
|
options?: ExportOptions
|
||||||
) => {
|
) => {
|
||||||
if (format === "pem") {
|
if (format === "pem") {
|
||||||
handlePopUpOpen("certificateCert", { serialNumber });
|
handlePopUpOpen("certificateCert", { serialNumber });
|
||||||
} else if (format === "jks") {
|
} else if (format === "pkcs12") {
|
||||||
if (!currentProject?.slug) return;
|
if (!currentProject?.slug) return;
|
||||||
|
|
||||||
if (!options?.jks?.password || !options?.jks?.alias) {
|
if (!options?.pkcs12?.password || !options?.pkcs12?.alias) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Password and alias are required for PKCS12 export",
|
text: "Password and alias are required for PKCS12 export",
|
||||||
type: "error"
|
type: "error"
|
||||||
@@ -74,17 +74,17 @@ export const CertificatesSection = () => {
|
|||||||
await downloadCertPkcs12({
|
await downloadCertPkcs12({
|
||||||
serialNumber,
|
serialNumber,
|
||||||
projectSlug: currentProject.slug,
|
projectSlug: currentProject.slug,
|
||||||
password: options.jks.password,
|
password: options.pkcs12.password,
|
||||||
alias: options.jks.alias
|
alias: options.pkcs12.alias
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "PKCS12 certificate downloaded successfully",
|
text: "PKCS12 certificate downloaded successfully",
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
} catch {
|
} catch (error: any) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Failed to download PKCS12 certificate",
|
text: error?.message || "Failed to download PKCS12 certificate",
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user