mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-21 07:27:58 +00:00
PKI revamp EST improvements and fixes
This commit is contained in:
+25
-19
@@ -47,7 +47,7 @@ const createSchema = z
|
||||
estConfig: z
|
||||
.object({
|
||||
disableBootstrapCaValidation: z.boolean().optional(),
|
||||
passphrase: z.string().min(1, "EST passphrase is required"),
|
||||
passphraseInput: z.string().min(1, "EST passphrase is required"),
|
||||
caChain: z.string().min(1, "EST CA chain is required").optional()
|
||||
})
|
||||
.refine(
|
||||
@@ -58,7 +58,8 @@ const createSchema = z
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "EST CA chain is required"
|
||||
message: "EST CA chain is required when bootstrap CA validation is enabled",
|
||||
path: ["caChain"]
|
||||
}
|
||||
)
|
||||
.optional(),
|
||||
@@ -106,7 +107,7 @@ const editSchema = z
|
||||
estConfig: z
|
||||
.object({
|
||||
disableBootstrapCaValidation: z.boolean().optional(),
|
||||
passphrase: z.string().optional(),
|
||||
passphraseInput: z.string().optional(),
|
||||
caChain: z.string().optional()
|
||||
})
|
||||
.optional(),
|
||||
@@ -168,15 +169,22 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
enrollmentType: profile.enrollmentType,
|
||||
certificateAuthorityId: profile.caId,
|
||||
certificateTemplateId: profile.certificateTemplateId,
|
||||
estConfig: {
|
||||
disableBootstrapCaValidation: profile.estConfig?.disableBootstrapCaValidation || false,
|
||||
passphrase: "",
|
||||
caChain: undefined
|
||||
},
|
||||
apiConfig: {
|
||||
autoRenew: profile.apiConfig?.autoRenew || false,
|
||||
autoRenewDays: profile.apiConfig?.autoRenewDays || 30
|
||||
}
|
||||
estConfig:
|
||||
profile.enrollmentType === "est"
|
||||
? {
|
||||
disableBootstrapCaValidation:
|
||||
profile.estConfig?.disableBootstrapCaValidation || false,
|
||||
passphraseInput: "",
|
||||
caChain: profile.estConfig?.encryptedCaChain || ""
|
||||
}
|
||||
: undefined,
|
||||
apiConfig:
|
||||
profile.enrollmentType === "api"
|
||||
? {
|
||||
autoRenew: profile.apiConfig?.autoRenew || false,
|
||||
autoRenewDays: profile.apiConfig?.autoRenewDays || 30
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
: {
|
||||
slug: "",
|
||||
@@ -229,8 +237,8 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
|
||||
if (data.enrollmentType === "est" && data.estConfig) {
|
||||
createData.estConfig = {
|
||||
passphrase: data.estConfig.passphrase,
|
||||
caChain: data.estConfig.caChain || "",
|
||||
passphraseInput: data.estConfig.passphraseInput,
|
||||
caChain: data.estConfig.caChain || undefined,
|
||||
disableBootstrapCaValidation: data.estConfig.disableBootstrapCaValidation
|
||||
};
|
||||
} else if (data.enrollmentType === "api" && data.apiConfig) {
|
||||
@@ -346,8 +354,7 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
if (watchedEnrollmentType === "est") {
|
||||
setValue("estConfig", {
|
||||
disableBootstrapCaValidation: false,
|
||||
passphrase: "",
|
||||
caChain: ""
|
||||
passphraseInput: ""
|
||||
});
|
||||
setValue("apiConfig", undefined);
|
||||
} else {
|
||||
@@ -391,8 +398,7 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
setValue("apiConfig", undefined);
|
||||
setValue("estConfig", {
|
||||
disableBootstrapCaValidation: false,
|
||||
passphrase: "",
|
||||
caChain: ""
|
||||
passphraseInput: ""
|
||||
});
|
||||
} else {
|
||||
setValue("estConfig", undefined);
|
||||
@@ -444,7 +450,7 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="estConfig.passphrase"
|
||||
name="estConfig.passphraseInput"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="EST Passphrase"
|
||||
|
||||
+1
-1
@@ -201,7 +201,7 @@ export const ProfileRow = ({ profile, onEditProfile, onDeleteProfile }: Props) =
|
||||
Edit Profile
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canIssueCertificate && (
|
||||
{canIssueCertificate && profile.enrollmentType === "api" && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
+7
-6
@@ -9,8 +9,8 @@ import {
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import { useDeleteCertificateTemplateV2New } from "@app/hooks/api/certificateTemplates/mutations";
|
||||
import { TCertificateTemplateV2New } from "@app/hooks/api/certificateTemplates/types";
|
||||
import { useDeleteCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/mutations";
|
||||
import { TCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/types";
|
||||
|
||||
import { CreateTemplateModal } from "./CreateTemplateModal";
|
||||
import { TemplateList } from "./TemplateList";
|
||||
@@ -21,9 +21,10 @@ export const CertificateTemplatesV2Tab = () => {
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<TCertificateTemplateV2New | null>(null);
|
||||
const [selectedTemplate, setSelectedTemplate] =
|
||||
useState<TCertificateTemplateV2WithPolicies | null>(null);
|
||||
|
||||
const deleteTemplateV2 = useDeleteCertificateTemplateV2New();
|
||||
const deleteTemplateV2 = useDeleteCertificateTemplateV2WithPolicies();
|
||||
|
||||
const canCreateTemplate = permission.can(
|
||||
ProjectPermissionPkiTemplateActions.Create,
|
||||
@@ -34,12 +35,12 @@ export const CertificateTemplatesV2Tab = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
|
||||
const handleEditTemplate = (template: TCertificateTemplateV2New) => {
|
||||
const handleEditTemplate = (template: TCertificateTemplateV2WithPolicies) => {
|
||||
setSelectedTemplate(template);
|
||||
setIsEditModalOpen(true);
|
||||
};
|
||||
|
||||
const handleDeleteTemplate = (template: TCertificateTemplateV2New) => {
|
||||
const handleDeleteTemplate = (template: TCertificateTemplateV2WithPolicies) => {
|
||||
setSelectedTemplate(template);
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
|
||||
+59
-22
@@ -24,12 +24,12 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useProject } from "@app/context";
|
||||
import {
|
||||
useCreateCertificateTemplateV2New,
|
||||
useUpdateCertificateTemplateV2New
|
||||
useCreateCertificateTemplateV2WithPolicies,
|
||||
useUpdateCertificateTemplateV2WithPolicies
|
||||
} from "@app/hooks/api/certificateTemplates/mutations";
|
||||
import {
|
||||
TCertificateTemplateV2New,
|
||||
TCertificateTemplateV2Policy
|
||||
TCertificateTemplateV2Policy,
|
||||
TCertificateTemplateV2WithPolicies
|
||||
} from "@app/hooks/api/certificateTemplates/types";
|
||||
|
||||
import {
|
||||
@@ -57,7 +57,7 @@ type ValidityTransform = TCertificateTemplateV2Policy["validity"];
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
template?: TCertificateTemplateV2New;
|
||||
template?: TCertificateTemplateV2WithPolicies;
|
||||
mode?: "create" | "edit";
|
||||
}
|
||||
|
||||
@@ -106,12 +106,12 @@ const KEY_ALGORITHMS = [
|
||||
|
||||
export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create" }: Props) => {
|
||||
const { currentProject } = useProject();
|
||||
const createTemplate = useCreateCertificateTemplateV2New();
|
||||
const updateTemplate = useUpdateCertificateTemplateV2New();
|
||||
const createTemplate = useCreateCertificateTemplateV2WithPolicies();
|
||||
const updateTemplate = useUpdateCertificateTemplateV2WithPolicies();
|
||||
|
||||
const isEdit = mode === "edit" && template;
|
||||
|
||||
const convertApiToUiFormat = (templateData: TCertificateTemplateV2New): FormData => {
|
||||
const convertApiToUiFormat = (templateData: TCertificateTemplateV2WithPolicies): FormData => {
|
||||
const attributes: FormData["attributes"] = [];
|
||||
if (templateData.subject && Array.isArray(templateData.subject)) {
|
||||
templateData.subject.forEach((subj) => {
|
||||
@@ -244,12 +244,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
maxDuration: { value: 365, unit: CertDurationUnit.DAYS }
|
||||
},
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [],
|
||||
defaultAlgorithm: ""
|
||||
allowedAlgorithms: []
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [],
|
||||
defaultKeyType: ""
|
||||
allowedKeyTypes: []
|
||||
}
|
||||
});
|
||||
|
||||
@@ -275,8 +273,38 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
optionalUsages: []
|
||||
};
|
||||
|
||||
const transformToNewApiFormat = (data: FormData) => {
|
||||
const subject =
|
||||
const consolidateByType = <
|
||||
T extends { type: string; allowed?: string[]; required?: string[]; denied?: string[] }
|
||||
>(
|
||||
items: T[]
|
||||
): T[] => {
|
||||
const consolidated = new Map<string, T>();
|
||||
|
||||
items.forEach((item) => {
|
||||
const existing = consolidated.get(item.type);
|
||||
if (existing) {
|
||||
const mergedItem = {
|
||||
...item,
|
||||
allowed: [...new Set([...(existing.allowed || []), ...(item.allowed || [])])],
|
||||
required: [...new Set([...(existing.required || []), ...(item.required || [])])],
|
||||
denied: [...new Set([...(existing.denied || []), ...(item.denied || [])])]
|
||||
} as T;
|
||||
|
||||
if (mergedItem.allowed?.length === 0) delete mergedItem.allowed;
|
||||
if (mergedItem.required?.length === 0) delete mergedItem.required;
|
||||
if (mergedItem.denied?.length === 0) delete mergedItem.denied;
|
||||
|
||||
consolidated.set(item.type, mergedItem);
|
||||
} else {
|
||||
consolidated.set(item.type, item);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(consolidated.values());
|
||||
};
|
||||
|
||||
const transformToApiFormat = (data: FormData) => {
|
||||
const subjectRaw =
|
||||
data.attributes?.map((attr) => {
|
||||
const result: AttributeTransform = { type: attr.type };
|
||||
|
||||
@@ -289,7 +317,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
return result;
|
||||
}) || [];
|
||||
|
||||
const sans =
|
||||
const sansRaw =
|
||||
data.subjectAlternativeNames?.map((san) => {
|
||||
const result: SanTransform = { type: san.type };
|
||||
|
||||
@@ -304,7 +332,13 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
return result;
|
||||
}) || [];
|
||||
|
||||
const keyUsages: KeyUsagesTransform = {};
|
||||
const subject = consolidateByType(subjectRaw);
|
||||
const sans = consolidateByType(sansRaw);
|
||||
|
||||
const keyUsages: KeyUsagesTransform = {
|
||||
required: [],
|
||||
allowed: []
|
||||
};
|
||||
if (data.keyUsages?.requiredUsages && data.keyUsages.requiredUsages.length > 0) {
|
||||
keyUsages.required = data.keyUsages.requiredUsages;
|
||||
}
|
||||
@@ -312,7 +346,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
keyUsages.allowed = data.keyUsages.optionalUsages;
|
||||
}
|
||||
|
||||
const extendedKeyUsages: ExtendedKeyUsagesTransform = {};
|
||||
const extendedKeyUsages: ExtendedKeyUsagesTransform = {
|
||||
required: [],
|
||||
allowed: []
|
||||
};
|
||||
if (
|
||||
data.extendedKeyUsages?.requiredUsages &&
|
||||
data.extendedKeyUsages.requiredUsages.length > 0
|
||||
@@ -364,10 +401,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
description: data.description,
|
||||
subject,
|
||||
sans,
|
||||
keyUsages: Object.keys(keyUsages).length > 0 ? keyUsages : undefined,
|
||||
extendedKeyUsages: Object.keys(extendedKeyUsages).length > 0 ? extendedKeyUsages : undefined,
|
||||
algorithms: Object.keys(algorithms).length > 0 ? algorithms : undefined,
|
||||
validity: Object.keys(validity).length > 0 ? validity : undefined
|
||||
keyUsages,
|
||||
extendedKeyUsages,
|
||||
algorithms,
|
||||
validity
|
||||
};
|
||||
};
|
||||
|
||||
@@ -391,7 +428,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
return;
|
||||
}
|
||||
|
||||
const transformedData = transformToNewApiFormat(data);
|
||||
const transformedData = transformToApiFormat(data);
|
||||
|
||||
if (isEdit) {
|
||||
const updateData = {
|
||||
|
||||
+3
-3
@@ -23,11 +23,11 @@ import {
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import { useListCertificateTemplatesV2 } from "@app/hooks/api/certificateTemplates/queries";
|
||||
import { TCertificateTemplateV2New } from "@app/hooks/api/certificateTemplates/types";
|
||||
import { TCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/types";
|
||||
|
||||
interface Props {
|
||||
onEditTemplate: (template: TCertificateTemplateV2New) => void;
|
||||
onDeleteTemplate: (template: TCertificateTemplateV2New) => void;
|
||||
onEditTemplate: (template: TCertificateTemplateV2WithPolicies) => void;
|
||||
onDeleteTemplate: (template: TCertificateTemplateV2WithPolicies) => void;
|
||||
}
|
||||
|
||||
export const TemplateList = ({ onEditTemplate, onDeleteTemplate }: Props) => {
|
||||
|
||||
+8
-6
@@ -40,13 +40,15 @@ export const uiValiditySchema = z.object({
|
||||
});
|
||||
|
||||
export const uiSignatureAlgorithmSchema = z.object({
|
||||
allowedAlgorithms: z.array(z.string()).optional(),
|
||||
defaultAlgorithm: z.string().optional()
|
||||
allowedAlgorithms: z
|
||||
.array(z.string().min(1, "Algorithm cannot be empty"))
|
||||
.min(1, "At least one algorithm must be selected")
|
||||
});
|
||||
|
||||
export const uiKeyAlgorithmSchema = z.object({
|
||||
allowedKeyTypes: z.array(z.string()).optional(),
|
||||
defaultKeyType: z.string().optional()
|
||||
allowedKeyTypes: z
|
||||
.array(z.string().min(1, "Key type cannot be empty"))
|
||||
.min(1, "At least one key type must be selected")
|
||||
});
|
||||
|
||||
export const templateSchema = z.object({
|
||||
@@ -69,8 +71,8 @@ export const templateSchema = z.object({
|
||||
keyUsages: uiKeyUsagesSchema.optional(),
|
||||
extendedKeyUsages: uiExtendedKeyUsagesSchema.optional(),
|
||||
validity: uiValiditySchema.optional(),
|
||||
signatureAlgorithm: uiSignatureAlgorithmSchema.optional(),
|
||||
keyAlgorithm: uiKeyAlgorithmSchema.optional()
|
||||
signatureAlgorithm: uiSignatureAlgorithmSchema,
|
||||
keyAlgorithm: uiKeyAlgorithmSchema
|
||||
});
|
||||
|
||||
export type TemplateFormData = z.infer<typeof templateSchema>;
|
||||
|
||||
Reference in New Issue
Block a user