mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
add UI for editing digicert CA (#27103)
This commit is contained in:
parent
a86253d2bf
commit
e85b46e915
13 changed files with 296 additions and 56 deletions
|
|
@ -41,6 +41,33 @@ export interface ICertificatesIntegrationCustomSCEP {
|
|||
challenge: string;
|
||||
}
|
||||
|
||||
export const isNDESCertIntegration = (
|
||||
integration: ICertificateIntegration
|
||||
): integration is ICertificatesIntegrationNDES => {
|
||||
return (
|
||||
"admin_url" in integration &&
|
||||
"username" in integration &&
|
||||
"password" in integration
|
||||
);
|
||||
};
|
||||
|
||||
export const isDigicertCertIntegration = (
|
||||
integration: ICertificateIntegration
|
||||
): integration is ICertificatesIntegrationDigicert => {
|
||||
return (
|
||||
"profile_id" in integration &&
|
||||
"certificate_common_name" in integration &&
|
||||
"certificate_user_principal_names" in integration &&
|
||||
"certificate_seat_id" in integration
|
||||
);
|
||||
};
|
||||
|
||||
export const isCustomSCEPCertIntegration = (
|
||||
integration: ICertificateIntegration
|
||||
): integration is ICertificatesIntegrationCustomSCEP => {
|
||||
return "id" in integration && "challenge" in integration;
|
||||
};
|
||||
|
||||
export type ICertificateAuthorityType = "ndes" | "digicert" | "custom";
|
||||
|
||||
/** all the types of certificate integrations */
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
import AddCertAuthorityCard from "./components/AddCertAuthorityCard";
|
||||
import DeleteCertificateAuthorityModal from "./components/DeleteCertificateAuthorityModal";
|
||||
import AddCertAuthorityModal from "./components/AddCertAuthorityModal";
|
||||
import EditCertAuthorityModal from "./components/EditCertAuthorityModal";
|
||||
|
||||
const baseClass = "certificate-authorities";
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ const CertificateAuthorities = () => {
|
|||
false
|
||||
);
|
||||
const [
|
||||
showDeleteCertAuthoirtyModal,
|
||||
showDeleteCertAuthorityModal,
|
||||
setShowDeleteCertAuthorityModal,
|
||||
] = useState(false);
|
||||
|
||||
|
|
@ -56,7 +57,6 @@ const CertificateAuthorities = () => {
|
|||
};
|
||||
|
||||
const onEditCertAuthority = (cert: ICertAuthorityListData) => {
|
||||
// TODO: use useCallback
|
||||
const certAuthority = getCertificateAuthority(
|
||||
cert.id,
|
||||
config?.integrations.ndes_scep_proxy,
|
||||
|
|
@ -69,7 +69,6 @@ const CertificateAuthorities = () => {
|
|||
};
|
||||
|
||||
const onDeleteCertAuthority = (cert: ICertAuthorityListData) => {
|
||||
// TODO: use useCallback
|
||||
const certAuthority = getCertificateAuthority(
|
||||
cert.id,
|
||||
config?.integrations.ndes_scep_proxy,
|
||||
|
|
@ -118,8 +117,13 @@ const CertificateAuthorities = () => {
|
|||
onExit={() => setShowAddCertAuthorityModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showEditCertAuthorityModal && <div>Modal showing</div>}
|
||||
{showDeleteCertAuthoirtyModal &&
|
||||
{showEditCertAuthorityModal && selectedCertAuthority && (
|
||||
<EditCertAuthorityModal
|
||||
certAuthority={selectedCertAuthority}
|
||||
onExit={() => setShowEditCertAuthorityModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showDeleteCertAuthorityModal &&
|
||||
selectedCertAuthority &&
|
||||
selectedListItemId && (
|
||||
<DeleteCertificateAuthorityModal
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import DigicertForm from "../DigicertForm";
|
|||
import { IDigicertFormData } from "../DigicertForm/DigicertForm";
|
||||
import { useCertAuthorityDataGenerator } from "../DeleteCertificateAuthorityModal/helpers";
|
||||
|
||||
export type ICertFormData = IDigicertFormData;
|
||||
// | IAnotherCertFormData
|
||||
// | IYetAnotherCertFormData;
|
||||
|
||||
const baseClass = "add-cert-authority-modal";
|
||||
|
||||
interface IAddCertAuthorityModalProps {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ interface IActionsProps {
|
|||
const Actions = ({ onEdit, onDelete }: IActionsProps) => {
|
||||
return (
|
||||
<>
|
||||
{/* <GitOpsModeTooltipWrapper
|
||||
<GitOpsModeTooltipWrapper
|
||||
position="left"
|
||||
renderChildren={(disableChildren) => (
|
||||
<Button
|
||||
|
|
@ -29,7 +29,7 @@ const Actions = ({ onEdit, onDelete }: IActionsProps) => {
|
|||
<Icon name="pencil" color="ui-fleet-black-75" />
|
||||
</Button>
|
||||
)}
|
||||
/> */}
|
||||
/>
|
||||
<GitOpsModeTooltipWrapper
|
||||
position="left"
|
||||
renderChildren={(disableChildren) => (
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
ICertificatesIntegrationDigicert,
|
||||
IGlobalIntegrations,
|
||||
} from "interfaces/integration";
|
||||
import { useContext } from "react";
|
||||
import { useCallback, useContext } from "react";
|
||||
import { IDigicertFormData } from "../DigicertForm/DigicertForm";
|
||||
|
||||
export const useCertAuthorityDataGenerator = (
|
||||
|
|
@ -16,45 +16,48 @@ export const useCertAuthorityDataGenerator = (
|
|||
) => {
|
||||
const { config } = useContext(AppContext);
|
||||
|
||||
const generateAddPatchData = (formData: IDigicertFormData) => {
|
||||
if (!config) return null;
|
||||
const generateAddPatchData = useCallback(
|
||||
(formData: IDigicertFormData) => {
|
||||
if (!config) return null;
|
||||
|
||||
const data: { integrations: Partial<IGlobalIntegrations> } = {
|
||||
integrations: {},
|
||||
};
|
||||
const data: { integrations: Partial<IGlobalIntegrations> } = {
|
||||
integrations: {},
|
||||
};
|
||||
|
||||
switch (certAuthorityType) {
|
||||
case "ndes":
|
||||
break;
|
||||
case "digicert":
|
||||
data.integrations.digicert = [
|
||||
...(config.integrations.digicert || []),
|
||||
{
|
||||
name: formData.name,
|
||||
url: formData.url,
|
||||
api_token: formData.apiToken,
|
||||
profile_id: formData.profileId,
|
||||
certificate_common_name: formData.commonName,
|
||||
certificate_user_principal_names: [formData.userPrincipalName],
|
||||
certificate_seat_id: formData.certificateSeatId,
|
||||
},
|
||||
];
|
||||
break;
|
||||
case "custom":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (certAuthorityType) {
|
||||
case "ndes":
|
||||
break;
|
||||
case "digicert":
|
||||
data.integrations.digicert = [
|
||||
...(config.integrations.digicert || []),
|
||||
{
|
||||
name: formData.name,
|
||||
url: formData.url,
|
||||
api_token: formData.apiToken,
|
||||
profile_id: formData.profileId,
|
||||
certificate_common_name: formData.commonName,
|
||||
certificate_user_principal_names: [formData.userPrincipalName],
|
||||
certificate_seat_id: formData.certificateSeatId,
|
||||
},
|
||||
];
|
||||
break;
|
||||
case "custom":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
return data;
|
||||
},
|
||||
[certAuthorityType, config]
|
||||
);
|
||||
|
||||
/**
|
||||
* generates the data to be sent to the API to delete the certificate authority.
|
||||
* under the hood we are updating the app config object with the new data and
|
||||
* have to generate the correct data for the PATCH request.
|
||||
*/
|
||||
const generateDeletePatchData = () => {
|
||||
const generateDeletePatchData = useCallback(() => {
|
||||
if (!config) return null;
|
||||
|
||||
const data: { integrations: Partial<IGlobalIntegrations> } = {
|
||||
|
|
@ -90,16 +93,58 @@ export const useCertAuthorityDataGenerator = (
|
|||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
}, [certAuthority, certAuthorityType, config]);
|
||||
|
||||
/**
|
||||
* generates the data to be sent to the API to edit the certificate authority.
|
||||
* under the hood we are updating the app config object with the new data and
|
||||
* have to generate the correct data for the PATCH request.
|
||||
*/
|
||||
const generateEditPatchData = (formData: IDigicertFormData) => {
|
||||
if (!config) return null;
|
||||
};
|
||||
const generateEditPatchData = useCallback(
|
||||
(formData: IDigicertFormData) => {
|
||||
if (!config) return null;
|
||||
|
||||
const data: { integrations: Partial<IGlobalIntegrations> } = {
|
||||
integrations: {},
|
||||
};
|
||||
|
||||
switch (certAuthorityType) {
|
||||
case "ndes":
|
||||
break;
|
||||
case "digicert":
|
||||
data.integrations.digicert = config.integrations.digicert?.map(
|
||||
(cert) => {
|
||||
// only update the certificate authority that we are editing
|
||||
if (
|
||||
(certAuthority as ICertificatesIntegrationDigicert).name ===
|
||||
cert.name
|
||||
) {
|
||||
return {
|
||||
name: formData.name,
|
||||
url: formData.url,
|
||||
api_token: formData.apiToken,
|
||||
profile_id: formData.profileId,
|
||||
certificate_common_name: formData.commonName,
|
||||
certificate_user_principal_names: [
|
||||
formData.userPrincipalName,
|
||||
],
|
||||
certificate_seat_id: formData.certificateSeatId,
|
||||
};
|
||||
}
|
||||
return cert;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case "custom":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
[certAuthority, certAuthorityType, config]
|
||||
);
|
||||
|
||||
return {
|
||||
generateAddPatchData,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import InputField from "components/forms/fields/InputField";
|
|||
import Button from "components/buttons/Button";
|
||||
import CustomLink from "components/CustomLink";
|
||||
import TooltipWrapper from "components/TooltipWrapper";
|
||||
import { generateFormValidation, IDigicertFormValidation } from "./helpers";
|
||||
import { validateFormData, IDigicertFormValidation } from "./helpers";
|
||||
|
||||
const baseClass = "digicert-form";
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ const DigicertForm = ({
|
|||
|
||||
const onInputChange = (update: { name: string; value: string }) => {
|
||||
setFormValidation(
|
||||
generateFormValidation({ ...formData, [update.name]: update.value })
|
||||
validateFormData({ ...formData, [update.name]: update.value })
|
||||
);
|
||||
onChange(update);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ const getErrorMessage = (
|
|||
};
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const generateFormValidation = (formData: IDigicertFormData) => {
|
||||
export const validateFormData = (formData: IDigicertFormData) => {
|
||||
const formValidation: IDigicertFormValidation = {
|
||||
isValid: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
import React, { useContext, useRef, useState } from "react";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { AppContext } from "context/app";
|
||||
import {
|
||||
ICertificateIntegration,
|
||||
isDigicertCertIntegration,
|
||||
} from "interfaces/integration";
|
||||
import certificatesAPI from "services/entities/certificates";
|
||||
|
||||
import Modal from "components/Modal";
|
||||
|
||||
import DigicertForm from "../DigicertForm";
|
||||
import {
|
||||
generateDefaultFormData,
|
||||
generateErrorMessage,
|
||||
getCertificateAuthorityType,
|
||||
} from "./helpers";
|
||||
import { ICertFormData } from "../AddCertAuthorityModal/AddCertAuthorityModal";
|
||||
import { useCertAuthorityDataGenerator } from "../DeleteCertificateAuthorityModal/helpers";
|
||||
|
||||
const baseClass = "edit-cert-authority-modal";
|
||||
|
||||
interface IEditCertAuthorityModalProps {
|
||||
certAuthority: ICertificateIntegration;
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
const EditCertAuthorityModal = ({
|
||||
certAuthority,
|
||||
onExit,
|
||||
}: IEditCertAuthorityModalProps) => {
|
||||
const certType = useRef(getCertificateAuthorityType(certAuthority));
|
||||
const { setConfig } = useContext(AppContext);
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
const [formData, setFormData] = useState<ICertFormData>(() =>
|
||||
generateDefaultFormData(certAuthority)
|
||||
);
|
||||
const { generateEditPatchData } = useCertAuthorityDataGenerator(
|
||||
certType.current,
|
||||
certAuthority
|
||||
);
|
||||
|
||||
const onChangeForm = (update: { name: string; value: string }) => {
|
||||
setFormData((prevFormData) => {
|
||||
if (!prevFormData) return prevFormData;
|
||||
|
||||
return {
|
||||
...prevFormData,
|
||||
[update.name]: update.value,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onEditCertAuthority = async () => {
|
||||
const editPatchData = generateEditPatchData(formData);
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const newConfig = await certificatesAPI.editCertAuthorityModal(
|
||||
editPatchData
|
||||
);
|
||||
renderFlash("success", "Successfully edited your certificate authority.");
|
||||
onExit();
|
||||
setConfig(newConfig);
|
||||
} catch (e) {
|
||||
renderFlash("error", generateErrorMessage(e));
|
||||
}
|
||||
setIsUpdating(false);
|
||||
};
|
||||
|
||||
const getFormComponent = () => {
|
||||
if (isDigicertCertIntegration(certAuthority)) {
|
||||
return DigicertForm;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderForm = () => {
|
||||
const FormComponent = getFormComponent();
|
||||
if (!FormComponent || !formData) return <></>;
|
||||
|
||||
return (
|
||||
<FormComponent
|
||||
formData={formData}
|
||||
submitBtnText="Save"
|
||||
isSubmitting={isUpdating}
|
||||
onChange={onChangeForm}
|
||||
onSubmit={onEditCertAuthority}
|
||||
onCancel={onExit}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={baseClass}
|
||||
title="Edit certificate authority (CA)"
|
||||
width="large"
|
||||
onExit={onExit}
|
||||
>
|
||||
{renderForm()}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditCertAuthorityModal;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.edit-cert-authority-modal {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import {
|
||||
ICertificateAuthorityType,
|
||||
ICertificateIntegration,
|
||||
ICertificatesIntegrationDigicert,
|
||||
isCustomSCEPCertIntegration,
|
||||
isNDESCertIntegration,
|
||||
} from "interfaces/integration";
|
||||
|
||||
import { ICertFormData } from "../AddCertAuthorityModal/AddCertAuthorityModal";
|
||||
|
||||
const DEFAULT_ERROR_MESSAGE =
|
||||
"Couldn't edit certificate authority. Please try again.";
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const generateErrorMessage = (e: unknown) => {
|
||||
return DEFAULT_ERROR_MESSAGE;
|
||||
};
|
||||
|
||||
export const generateDefaultFormData = (
|
||||
certAuthority: ICertificateIntegration
|
||||
): ICertFormData => {
|
||||
const cert = certAuthority as ICertificatesIntegrationDigicert;
|
||||
return {
|
||||
name: cert.name,
|
||||
url: cert.url,
|
||||
apiToken: cert.api_token,
|
||||
profileId: cert.profile_id,
|
||||
commonName: cert.certificate_common_name,
|
||||
userPrincipalName: cert.certificate_user_principal_names[0],
|
||||
certificateSeatId: cert.certificate_seat_id,
|
||||
};
|
||||
};
|
||||
|
||||
export const getCertificateAuthorityType = (
|
||||
certAuthority: ICertificateIntegration
|
||||
): ICertificateAuthorityType => {
|
||||
if (isNDESCertIntegration(certAuthority)) return "ndes";
|
||||
if (isCustomSCEPCertIntegration(certAuthority)) return "custom";
|
||||
return "digicert";
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./EditCertAuthorityModal";
|
||||
|
|
@ -2,6 +2,7 @@ import {
|
|||
ICertificatesIntegrationCustomSCEP,
|
||||
ICertificatesIntegrationDigicert,
|
||||
ICertificatesIntegrationNDES,
|
||||
isNDESCertIntegration,
|
||||
} from "interfaces/integration";
|
||||
|
||||
export interface ICertAuthorityListData {
|
||||
|
|
@ -76,21 +77,24 @@ export const getCertificateAuthority = (
|
|||
digicertCerts?: ICertificatesIntegrationDigicert[],
|
||||
customProxies?: ICertificatesIntegrationCustomSCEP[]
|
||||
) => {
|
||||
if (id === "ndes") {
|
||||
return ncspProxy as ICertificatesIntegrationNDES;
|
||||
if (id === "ndes" && ncspProxy) {
|
||||
return ncspProxy;
|
||||
}
|
||||
|
||||
if (id.includes("digicert")) {
|
||||
return digicertCerts?.find(
|
||||
(cert) => id.split("digicert-")[1] === cert.name
|
||||
) as ICertificatesIntegrationDigicert;
|
||||
if (id.includes("digicert") && digicertCerts) {
|
||||
return (
|
||||
digicertCerts.find((cert) => id.split("digicert-")[1] === cert.name) ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (id.includes("custom-scep-proxy")) {
|
||||
return customProxies?.find(
|
||||
// TODO: remove custom scep id
|
||||
(cert) => id.split("custom-scep-proxy-")[1] === cert.id.toString()
|
||||
) as ICertificatesIntegrationCustomSCEP;
|
||||
if (id.includes("custom-scep-proxy") && customProxies) {
|
||||
return (
|
||||
customProxies?.find(
|
||||
// TODO: remove custom scep id
|
||||
(cert) => id.split("custom-scep-proxy-")[1] === cert.id.toString()
|
||||
) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import EditCertAuthorityModal from "pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/EditCertAuthorityModal";
|
||||
import configAPI from "./config";
|
||||
|
||||
export default {
|
||||
|
|
@ -5,6 +6,10 @@ export default {
|
|||
return configAPI.update(updateData);
|
||||
},
|
||||
|
||||
editCertAuthorityModal: (updateData: any): Promise<any> => {
|
||||
return configAPI.update(updateData);
|
||||
},
|
||||
|
||||
deleteCertificateAuthority: (updateData: any): Promise<any> => {
|
||||
return configAPI.update(updateData);
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue