add and delete digicert (#27083)

For #26606

This adds the ability to add and delete digicert integrations in the
fleet UI. this includes:

**The delete modal**


![image](https://github.com/user-attachments/assets/11ab33eb-4db3-4445-8647-2a40436fe2fc)

**the add digicert modal**


![image](https://github.com/user-attachments/assets/c6ae27dc-5a38-4c99-bafa-ef5b9f832ffa)

**integration with API to perform these actions**

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [ ] Added/updated automated tests
- [ ] Manual QA for all new/changed functionality
This commit is contained in:
Gabriel Hernandez 2025-03-13 16:46:10 +00:00 committed by GitHub
parent 3195d0f974
commit 2f97fecf39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 765 additions and 76 deletions

View file

@ -169,10 +169,6 @@ export interface IConfig {
disable_data_sync: boolean;
recent_vulnerability_max_age: number;
};
// Note: `vulnerability_settings` is deprecated and should not be used
// vulnerability_settings: {
// databases_path: string;
// };
webhook_settings: IWebhookSettings;
integrations: IGlobalIntegrations;
logging: {

View file

@ -25,8 +25,8 @@ export interface ICertificatesIntegrationNDES {
}
export interface ICertificatesIntegrationDigicert {
id: number;
name: string;
url: string;
api_token: string;
profile_id: string;
certificate_common_name: string;
@ -37,10 +37,18 @@ export interface ICertificatesIntegrationDigicert {
export interface ICertificatesIntegrationCustomSCEP {
id: number;
name: string;
server_url: string;
url: string;
challenge: string;
}
export type ICertificateAuthorityType = "ndes" | "digicert" | "custom";
/** all the types of certificate integrations */
export type ICertificateIntegration =
| ICertificatesIntegrationNDES
| ICertificatesIntegrationDigicert
| ICertificatesIntegrationCustomSCEP;
export interface IIntegration {
url: string;
username?: string;

View file

@ -34,12 +34,12 @@ const integrationSettingsNavItems: ISideNavItem<any>[] = [
Card: ChangeManagement,
},
// TODO: digicert update: add this back when the feature is ready
// {
// title: "Certificates",
// urlSection: "certificate-authorities",
// path: PATHS.ADMIN_INTEGRATIONS_CERTIFICATE_AUTHORITIES,
// Card: CertificateAuthorities,
// },
{
title: "Certificates",
urlSection: "certificate-authorities",
path: PATHS.ADMIN_INTEGRATIONS_CERTIFICATE_AUTHORITIES,
Card: CertificateAuthorities,
},
];
export default integrationSettingsNavItems;

View file

@ -3,7 +3,7 @@ import { Link } from "react-router";
import paths from "router/paths";
import { AppContext } from "context/app";
import createMockConfig from "__mocks__/configMock";
import { ICertificateIntegration } from "interfaces/integration";
import SectionHeader from "components/SectionHeader";
import CustomLink from "components/CustomLink";
@ -12,52 +12,16 @@ import CertificateAuthorityList from "./components/CertificateAuthorityList";
import {
generateListData,
getCertificateAuthority,
ICertAuthority,
ICertAuthorityListData,
} from "./helpers";
import AddCertAuthorityCard from "./components/AddCertAuthorityCard";
import DeleteCertificateAuthorityModal from "./components/DeleteCertificateAuthorityModal";
import AddCertAuthorityModal from "./components/AddCertAuthorityModal";
const baseClass = "certificate-authorities";
const CertificateAuthorities = () => {
let { config } = useContext(AppContext);
config = createMockConfig({
integrations: {
zendesk: [],
jira: [],
digicert: [
{
name: "DigiCert CA",
id: 1,
api_token: "123456",
profile_id: "7ed77396-9186-4bfa-9fa7-63dddc46b8a3",
certificate_common_name:
"$FLEET_VAR_HOST_HARDWARE_SERIAL@example.com",
certificate_user_principal_names: ["$FLEET_VAR_HOST_HARDWARE_SERIAL"],
certificate_seat_id: "$FLEET_VAR_HOST_HARDWARE_SERIAL@example.com",
},
],
ndes_scep_proxy: {
url: "https://ndes.scep.com",
admin_url: "https://ndes.scep.com/admin",
username: "ndes",
password: "password",
},
custom_scep_proxy: [
{
id: 1,
name: "Custom SCEP Proxy",
server_url: "https://custom.scep.com",
challenge: "challenge",
},
{
id: 2,
name: "Custom SCEP Proxy 2",
server_url: "https://custom.scep2.com",
challenge: "challenge-2",
},
],
},
});
const { config } = useContext(AppContext);
const [showAddCertAuthorityModal, setShowAddCertAuthorityModal] = useState(
false
@ -70,6 +34,14 @@ const CertificateAuthorities = () => {
setShowDeleteCertAuthorityModal,
] = useState(false);
const [selectedListItemId, setSelectedListItemId] = useState<string | null>(
null
);
const [
selectedCertAuthority,
setSelectedCertAuthority,
] = useState<ICertificateIntegration | null>(null);
const certificateAuthorities = useMemo(() => {
if (!config) return [];
return generateListData(
@ -83,26 +55,29 @@ const CertificateAuthorities = () => {
setShowAddCertAuthorityModal(true);
};
const onEditCertAuthority = (cert: ICertAuthority) => {
const onEditCertAuthority = (cert: ICertAuthorityListData) => {
// TODO: use useCallback
const ca = getCertificateAuthority(
const certAuthority = getCertificateAuthority(
cert.id,
config?.integrations.ndes_scep_proxy,
config?.integrations.digicert,
config?.integrations.custom_scep_proxy
);
console.log(ca);
setSelectedListItemId(cert.id);
setSelectedCertAuthority(certAuthority);
setShowEditCertAuthorityModal(true);
};
const onDeleteCertAuthority = (cert: ICertAuthority) => {
const onDeleteCertAuthority = (cert: ICertAuthorityListData) => {
// TODO: use useCallback
getCertificateAuthority(
const certAuthority = getCertificateAuthority(
cert.id,
config?.integrations.ndes_scep_proxy,
config?.integrations.digicert,
config?.integrations.custom_scep_proxy
);
setSelectedListItemId(cert.id);
setSelectedCertAuthority(certAuthority);
setShowDeleteCertAuthorityModal(true);
};
@ -138,9 +113,21 @@ const CertificateAuthorities = () => {
/>
</p>
{renderContent()}
{showAddCertAuthorityModal && <div>Modal showing</div>}
{showAddCertAuthorityModal && (
<AddCertAuthorityModal
onExit={() => setShowAddCertAuthorityModal(false)}
/>
)}
{showEditCertAuthorityModal && <div>Modal showing</div>}
{showDeleteCertAuthoirtyModal && <div>Modal showing</div>}
{showDeleteCertAuthoirtyModal &&
selectedCertAuthority &&
selectedListItemId && (
<DeleteCertificateAuthorityModal
listItemId={selectedListItemId}
certAuthority={selectedCertAuthority}
onExit={() => setShowDeleteCertAuthorityModal(false)}
/>
)}
</div>
);
};

View file

@ -0,0 +1,96 @@
import React, { useContext, useState } from "react";
import { NotificationContext } from "context/notification";
import certificatesAPI from "services/entities/certificates";
import { ICertificateAuthorityType } from "interfaces/integration";
import { AppContext } from "context/app";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
import Modal from "components/Modal";
import { generateErrorMessage } from "./helpers";
import DigicertForm from "../DigicertForm";
import { IDigicertFormData } from "../DigicertForm/DigicertForm";
import { useCertAuthorityDataGenerator } from "../DeleteCertificateAuthorityModal/helpers";
const baseClass = "add-cert-authority-modal";
interface IAddCertAuthorityModalProps {
onExit: () => void;
}
const AddCertAuthorityModal = ({ onExit }: IAddCertAuthorityModalProps) => {
const { setConfig } = useContext(AppContext);
const { renderFlash } = useContext(NotificationContext);
const [
certAuthorityType,
setCertAuthorityType,
] = useState<ICertificateAuthorityType>("digicert");
const [isAdding, setIsAdding] = useState(false);
const [formData, setFormData] = useState<IDigicertFormData>({
name: "",
url: "https://one.digicert.com",
apiToken: "",
profileId: "",
commonName: "",
userPrincipalName: "",
certificateSeatId: "",
});
const { generateAddPatchData } = useCertAuthorityDataGenerator(
certAuthorityType
);
const onChangeDropdown = (value: ICertificateAuthorityType) => {
setCertAuthorityType(value);
};
const onChangeForm = (update: { name: string; value: string }) => {
setFormData({ ...formData, [update.name]: update.value });
};
const onAddCertAuthority = async () => {
const addPatchData = generateAddPatchData(formData);
setIsAdding(true);
try {
const newConfig = await certificatesAPI.addCertificateAuthority(
addPatchData
);
renderFlash("success", "Successfully added your certificate authority.");
onExit();
setConfig(newConfig);
} catch (e) {
renderFlash("error", generateErrorMessage(e));
}
setIsAdding(false);
};
return (
<Modal
className={baseClass}
title="Add certificate authority (CA)"
width="large"
onExit={onExit}
>
<>
<Dropdown
options={[{ label: "Digicert", value: "digicert" }]}
value={certAuthorityType}
className={`${baseClass}__cert-authority-dropdown`}
onChange={onChangeDropdown}
searchable={false}
/>
<DigicertForm
formData={formData}
submitBtnText="Add CA"
isSubmitting={isAdding}
onChange={onChangeForm}
onSubmit={onAddCertAuthority}
onCancel={onExit}
/>
</>
</Modal>
);
};
export default AddCertAuthorityModal;

View file

@ -0,0 +1,6 @@
.add-cert-authority-modal {
&__cert-authority-dropdown {
margin-bottom: $pad-large;
}
}

View file

@ -0,0 +1,7 @@
const DEFAULT_ERROR_MESSAGE =
"Couldn't add certificate authority. Please try again.";
// eslint-disable-next-line import/prefer-default-export
export const generateErrorMessage = (e: unknown) => {
return DEFAULT_ERROR_MESSAGE;
};

View file

@ -0,0 +1 @@
export { default } from "./AddCertAuthorityModal";

View file

@ -5,7 +5,7 @@ import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import Button from "components/buttons/Button";
import Icon from "components/Icon";
import { ICertAuthority } from "../../helpers";
import { ICertAuthorityListData } from "../../helpers";
const baseClass = "cert-authority-list-item";
@ -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) => (
@ -48,7 +48,7 @@ const Actions = ({ onEdit, onDelete }: IActionsProps) => {
};
interface ICertAuthorityListItemProps {
cert: ICertAuthority;
cert: ICertAuthorityListData;
onClickEdit: () => void;
onClickDelete: () => void;
}
@ -60,7 +60,7 @@ const CertAuthorityListItem = ({
}: ICertAuthorityListItemProps) => {
return (
<ListItem
className={`${baseClass}__list-item`}
className={baseClass}
graphic="file-certificate"
title={cert.name}
details={cert.name}

View file

@ -4,15 +4,15 @@ import UploadList from "pages/ManageControlsPage/components/UploadList";
import CertAuthorityListHeader from "../CertAuthorityListHeader";
import CertAuthorityListItem from "../CertAuthorityListItem";
import { ICertAuthority } from "../../helpers";
import { ICertAuthorityListData } from "../../helpers";
const baseClass = "certificate-authority-list";
interface ICertificateAuthorityListProps {
certAuthorities: ICertAuthority[];
certAuthorities: ICertAuthorityListData[];
onAddCertAuthority: () => void;
onClickEdit: (cert: ICertAuthority) => void;
onClickDelete: (cert: ICertAuthority) => void;
onClickEdit: (cert: ICertAuthorityListData) => void;
onClickDelete: (cert: ICertAuthorityListData) => void;
}
const CertificateAuthorityList = ({
@ -22,7 +22,7 @@ const CertificateAuthorityList = ({
onClickDelete,
}: ICertificateAuthorityListProps) => {
return (
<UploadList<ICertAuthority>
<UploadList<ICertAuthorityListData>
className={baseClass}
keyAttribute="name"
listItems={certAuthorities}

View file

@ -1,3 +1,11 @@
.certificate-authority-list {
.list-item__actions {
display: none;
}
&:hover {
.list-item__actions {
display: flex;
}
}
}

View file

@ -0,0 +1,99 @@
import React, { useContext, useState } from "react";
import {
ICertificateAuthorityType,
ICertificateIntegration,
} from "interfaces/integration";
import certificatesAPI from "services/entities/certificates";
import { NotificationContext } from "context/notification";
import { AppContext } from "context/app";
import Button from "components/buttons/Button";
import Modal from "components/Modal";
import {
generateCertAuthorityDisplayName,
useCertAuthorityDataGenerator,
} from "./helpers";
const baseClass = "delete-certificate-authority-modal";
interface IDeleteCertificateAuthorityModalProps {
listItemId: string;
certAuthority: ICertificateIntegration;
onExit: () => void;
}
const DeleteCertificateAuthorityModal = ({
listItemId,
certAuthority,
onExit,
}: IDeleteCertificateAuthorityModalProps) => {
const certAuthorityType = listItemId.split(
"-"
)[0] as ICertificateAuthorityType;
const { generateDeletePatchData } = useCertAuthorityDataGenerator(
certAuthorityType,
certAuthority
);
const { setConfig } = useContext(AppContext);
const { renderFlash } = useContext(NotificationContext);
const [isUpdating, setIsUpdating] = useState(false);
const onDeleteCertAuthority = async () => {
setIsUpdating(true);
try {
const newConfig = await certificatesAPI.deleteCertificateAuthority(
generateDeletePatchData()
);
renderFlash(
"success",
"Successfully deleted your certificate authority."
);
setConfig(newConfig);
} catch (e) {
renderFlash(
"error",
"Couldn't delete certificate authority. Please try again."
);
}
setIsUpdating(false);
onExit();
};
const certAuthorityName = generateCertAuthorityDisplayName(
certAuthorityType,
certAuthority
);
return (
<Modal
className={baseClass}
title="Delete certificate authority (CA)"
onExit={onExit}
>
<>
<p>
Fleet won&apos;t remove certificates from the certificate authority (
<b>{certAuthorityName}</b>) on existing hosts.
</p>
<div className="modal-cta-wrap">
<Button
variant="alert"
onClick={onDeleteCertAuthority}
isLoading={isUpdating}
disabled={isUpdating}
>
Delete
</Button>
<Button variant="inverse-alert" onClick={onExit}>
Cancel
</Button>
</div>
</>
</Modal>
);
};
export default DeleteCertificateAuthorityModal;

View file

@ -0,0 +1,3 @@
.delete-certificate-authority-modal {
}

View file

@ -0,0 +1,125 @@
import { AppContext } from "context/app";
import {
ICertificateAuthorityType,
ICertificateIntegration,
ICertificatesIntegrationCustomSCEP,
ICertificatesIntegrationDigicert,
IGlobalIntegrations,
} from "interfaces/integration";
import { useContext } from "react";
import { IDigicertFormData } from "../DigicertForm/DigicertForm";
export const useCertAuthorityDataGenerator = (
certAuthorityType: ICertificateAuthorityType,
certAuthority?: ICertificateIntegration
) => {
const { config } = useContext(AppContext);
const generateAddPatchData = (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 || []),
{
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;
};
/**
* 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 = () => {
if (!config) return null;
const data: { integrations: Partial<IGlobalIntegrations> } = {
integrations: {},
};
switch (certAuthorityType) {
case "ndes":
data.integrations.ndes_scep_proxy = null;
break;
case "digicert":
data.integrations.digicert = config.integrations.digicert?.filter(
(cert) => {
return (
(certAuthority as ICertificatesIntegrationDigicert).name !==
cert.name
);
}
);
break;
case "custom":
data.integrations.custom_scep_proxy = config.integrations.custom_scep_proxy?.filter(
(cert) => {
return (
(certAuthority as ICertificatesIntegrationCustomSCEP).id ===
cert.id
);
}
);
break;
default:
break;
}
return data;
};
/**
* 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;
};
return {
generateAddPatchData,
generateDeletePatchData,
generateEditPatchData,
};
};
export const generateCertAuthorityDisplayName = (
certAuthorityType: ICertificateAuthorityType,
certAuthority: ICertificateIntegration
) => {
switch (certAuthorityType) {
case "ndes":
return "NDES";
case "digicert":
return (certAuthority as ICertificatesIntegrationDigicert).name;
case "custom":
return (certAuthority as ICertificatesIntegrationCustomSCEP).name;
default:
return "";
}
};

View file

@ -0,0 +1 @@
export { default } from "./DeleteCertificateAuthorityModal";

View file

@ -0,0 +1,167 @@
import React, { useState } from "react";
// @ts-ignore
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";
const baseClass = "digicert-form";
export interface IDigicertFormData {
name: string;
url: string;
apiToken: string;
profileId: string;
commonName: string;
userPrincipalName: string;
certificateSeatId: string;
}
interface IDigicertFormProps {
formData: IDigicertFormData;
submitBtnText: string;
isSubmitting: boolean;
onChange: (update: { name: string; value: string }) => void;
onSubmit: () => void;
onCancel: () => void;
}
const DigicertForm = ({
formData,
submitBtnText,
isSubmitting,
onChange,
onSubmit,
onCancel,
}: IDigicertFormProps) => {
const [formValidation, setFormValidation] = useState<IDigicertFormValidation>(
{
isValid: false,
}
);
const {
name,
url,
apiToken,
profileId,
commonName,
userPrincipalName,
certificateSeatId,
} = formData;
const onSubmitForm = (evt: React.FormEvent<HTMLFormElement>) => {
evt.preventDefault();
onSubmit();
};
const onInputChange = (update: { name: string; value: string }) => {
setFormValidation(
generateFormValidation({ ...formData, [update.name]: update.value })
);
onChange(update);
};
return (
<form className={baseClass} onSubmit={onSubmitForm}>
<div className={`${baseClass}__fields`}>
<InputField
name="name"
label="Name"
value={name}
onChange={onInputChange}
error={formValidation.name?.message}
helpText="Letters, numbers, and underscores only. Fleet will create configuration profile variables with the name as suffix (e.g. $FLEET_VAR_CERT_DATA_DIGICERT_WIFI)."
parseTarget
placeholder="DIGICERT_WIFI"
/>
<InputField
name="url"
label="URL"
value={url}
onChange={onInputChange}
error={formValidation.url?.message}
parseTarget
helpText="DigiCert ONE instance URL."
/>
<InputField
type="password"
name="apiToken"
label="API token"
value={apiToken}
onChange={onInputChange}
parseTarget
helpText="DigiCert One API token for service user."
/>
<InputField
name="profileId"
label="Profile GUID"
value={profileId}
onChange={onInputChange}
parseTarget
helpText={
<>
You can find the <b>Profile GUID</b> by opening one of the{" "}
<CustomLink
text="Digicert profiles"
url="https://demo.one.digicert.com/mpki/policies/profiles"
newTab
/>
</>
}
/>
<InputField
name="commonName"
label="Certificate common name (CN)"
value={commonName}
onChange={onInputChange}
parseTarget
helpText="Certificates delivered to your hosts will have this CN in the subject."
placeholder="$FLEET_VAR_HOST_HARDWARE_SERIAL"
/>
<InputField
name="userPrincipalName"
label="User principal name (UPN)"
value={userPrincipalName}
onChange={onInputChange}
parseTarget
helpText="Certificates delivered to your hosts will have this UPN attribute in Subject Alternative Name (SAN). (optional)"
placeholder="$FLEET_VAR_HOST_HARDWARE_SERIAL"
/>
<InputField
name="certificateSeatId"
label="Certificate seat ID"
value={certificateSeatId}
onChange={onInputChange}
parseTarget
helpText="Certificates delivered to your hosts will be assigned to this seat ID in DigiCert."
placeholder="$FLEET_VAR_HOST_HARDWARE_SERIAL"
/>
</div>
<div className={`${baseClass}__cta`}>
<TooltipWrapper
tipContent="Complete all required fields to save."
underline={false}
position="top"
disableTooltip={formValidation.isValid}
showArrow
>
<Button
isLoading={isSubmitting}
disabled={!formValidation.isValid || isSubmitting}
type="submit"
>
{submitBtnText}
</Button>
</TooltipWrapper>
<Button variant="inverse" onClick={onCancel}>
Cancel
</Button>
</div>
</form>
);
};
export default DigicertForm;

View file

@ -0,0 +1,13 @@
.digicert-form {
&__fields {
display: flex;
flex-direction: column;
gap: $pad-large;
}
&__cta {
display: flex;
flex-direction: row-reverse;
gap: $pad-medium;
}
}

View file

@ -0,0 +1,144 @@
import valid_url from "components/forms/validators/valid_url";
import { IDigicertFormData } from "./DigicertForm";
// TODO: create a validator abstraction for this and the other form validation files
export interface IDigicertFormValidation {
isValid: boolean;
name?: { isValid: boolean; message?: string };
url?: { isValid: boolean; message?: string };
apiToken?: { isValid: boolean };
profileId?: { isValid: boolean };
commonName?: { isValid: boolean };
certificateSeatId?: { isValid: boolean };
}
type IMessageFunc = (formData: IDigicertFormData) => string;
type IValidationMessage = string | IMessageFunc;
type IFormValidationKey = keyof Omit<IDigicertFormValidation, "isValid">;
interface IValidation {
name: string;
isValid: (formData: IDigicertFormData) => boolean;
message?: IValidationMessage;
}
const FORM_VALIDATIONS: Record<
IFormValidationKey,
{ validations: IValidation[] }
> = {
name: {
validations: [
{
name: "required",
isValid: (formData: IDigicertFormData) => {
return formData.name.length > 0;
},
},
{
name: "invalidCharacters",
isValid: (formData: IDigicertFormData) => {
return /^[a-zA-Z0-9_]+$/.test(formData.name);
},
message:
"Inalid characters. Only letters, numbers and underscores allowed.",
},
],
},
url: {
validations: [
{
name: "required",
isValid: (formData: IDigicertFormData) => {
return formData.url.length > 0;
},
},
{
name: "validUrl",
isValid: (formData: IDigicertFormData) => {
return valid_url({ url: formData.url });
},
message: (formData: IDigicertFormData) =>
`${formData.url} is not a valid URL`,
},
],
},
apiToken: {
validations: [
{
name: "required",
isValid: (formData: IDigicertFormData) => {
return formData.apiToken.length > 0;
},
},
],
},
profileId: {
validations: [
{
name: "required",
isValid: (formData: IDigicertFormData) => {
return formData.profileId.length > 0;
},
},
],
},
commonName: {
validations: [
{
name: "required",
isValid: (formData: IDigicertFormData) => {
return formData.commonName.length > 0;
},
},
],
},
certificateSeatId: {
validations: [
{
name: "required",
isValid: (formData: IDigicertFormData) => {
return formData.certificateSeatId.length > 0;
},
},
],
},
};
const getErrorMessage = (
formData: IDigicertFormData,
message?: IValidationMessage
) => {
if (message === undefined || typeof message === "string") {
return message;
}
return message(formData);
};
// eslint-disable-next-line import/prefer-default-export
export const generateFormValidation = (formData: IDigicertFormData) => {
const formValidation: IDigicertFormValidation = {
isValid: true,
};
Object.keys(FORM_VALIDATIONS).forEach((key) => {
const objKey = key as keyof typeof FORM_VALIDATIONS;
const failedValidation = FORM_VALIDATIONS[objKey].validations.find(
(validation) => !validation.isValid(formData)
);
if (!failedValidation) {
formValidation[objKey] = {
isValid: true,
};
} else {
formValidation.isValid = false;
formValidation[objKey] = {
isValid: false,
message: getErrorMessage(formData, failedValidation.message),
};
}
});
return formValidation;
};

View file

@ -0,0 +1 @@
export { default } from "./DigicertForm";

View file

@ -4,7 +4,7 @@ import {
ICertificatesIntegrationNDES,
} from "interfaces/integration";
export interface ICertAuthority {
export interface ICertAuthorityListData {
id: string;
name: string;
description: string;
@ -15,7 +15,7 @@ export const generateListData = (
digicertCerts?: ICertificatesIntegrationDigicert[],
customProxies?: ICertificatesIntegrationCustomSCEP[]
) => {
const listData: ICertAuthority[] = [];
const listData: ICertAuthorityListData[] = [];
// these values for the certificateAuthority is meant to be a hard coded .
if (ndesProxy) {
@ -29,7 +29,7 @@ export const generateListData = (
if (digicertCerts?.length) {
digicertCerts.forEach((cert) => {
listData.push({
id: `digicert-${cert.id}`,
id: `digicert-${cert.name}`,
name: cert.name,
description: "DigiCert",
});
@ -51,6 +51,21 @@ export const generateListData = (
);
};
export interface ICertIntegrationNDESWithListId
extends ICertificatesIntegrationNDES {
listId: string;
}
export interface ICertIntegrationDigicertWithListId
extends ICertificatesIntegrationDigicert {
listId: string;
}
export interface ICertIntegrationCustomSCEPWithListId
extends ICertificatesIntegrationCustomSCEP {
listId: string;
}
/**
* Gets the original certificate aithority integration object from the id
* of the data representing a certificate authority list item.
@ -67,14 +82,15 @@ export const getCertificateAuthority = (
if (id.includes("digicert")) {
return digicertCerts?.find(
(cert) => id.split("digicert-")[1] === cert.id.toString()
);
(cert) => id.split("digicert-")[1] === cert.name
) as ICertificatesIntegrationDigicert;
}
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;
}
return null;

View file

@ -0,0 +1,11 @@
import configAPI from "./config";
export default {
addCertificateAuthority: (updateData: any): Promise<any> => {
return configAPI.update(updateData);
},
deleteCertificateAuthority: (updateData: any): Promise<any> => {
return configAPI.update(updateData);
},
};