From 6977131834ac9893f9bf0533bb2d65d9f27eb60c Mon Sep 17 00:00:00 2001 From: Carla Martinez Date: Tue, 17 Oct 2023 10:30:39 +0200 Subject: [PATCH] Implement 'Remove hold' option The option 6 on revokation reasons refers to the 'Certificate hold' one. When selecting this option, the 'Remove hold' option should be enabled in the UI to allow users to enable again the given certificate. Signed-off-by: Carla Martinez --- src/components/Form/IpaCertificates.tsx | 45 +++++++-- .../modals/RemoveHoldCertificate.tsx | 97 +++++++++++++++++++ src/services/rpc.ts | 17 ++++ 3 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 src/components/modals/RemoveHoldCertificate.tsx diff --git a/src/components/Form/IpaCertificates.tsx b/src/components/Form/IpaCertificates.tsx index d8f934d6..33b637e4 100644 --- a/src/components/Form/IpaCertificates.tsx +++ b/src/components/Form/IpaCertificates.tsx @@ -16,6 +16,7 @@ import ModalWithTextAreaLayout from "../layouts/ModalWithTextAreaLayout"; import DeletionConfirmationModal from "../modals/DeletionConfirmationModal"; import CertificatesInformationModal from "../modals/CertificatesInformationModal"; import RevokeCertificate from "../modals/RevokeCertificate"; +import RemoveHoldCertificate from "../modals/RemoveHoldCertificate"; // Components import SecondaryButton from "../layouts/SecondaryButton"; // RTK @@ -48,12 +49,6 @@ export interface CertificateData { certInfo: Certificate; } -interface DN { - c: string; - cn: string; - o: string; -} - export interface DictWithName { key: string | React.ReactNode; name: string; @@ -183,6 +178,16 @@ const IpaCertificates = (props: PropsToIpaCertificates) => { return false; }; + // Check if the certificate can be removed from hold + // - i.e.: certificate has been revoked with CRL reason #6: 'Certificate hold' + const canHoldBeRemoved = (idx: number) => { + const cert: CertificateData = certificatesList[idx]; + if (cert.certInfo.revocation_reason !== undefined) { + return cert.certInfo.revocation_reason === 6; + } + return false; + }; + // Function to get the dropdown items (based on 'idx') const getDropdownItems = (idx: number) => { return [ @@ -215,7 +220,12 @@ const IpaCertificates = (props: PropsToIpaCertificates) => { > Revoke , - + onRemoveHoldCertificate(idx)} + > Remove hold , , @@ -487,6 +497,21 @@ const IpaCertificates = (props: PropsToIpaCertificates) => { setIsRevokeModalOpen(false); }; + // 'REMOVE HOLD' OPTION + const [isRemoveHoldModalOpen, setIsRemoveHoldModalOpen] = + React.useState(false); + + const onRemoveHoldCertificate = (idx: number) => { + // Track index of the selected certificate + setIdxSelected(idx); + // show revoke modal + setIsRemoveHoldModalOpen(true); + }; + + const onCloseRemoveHoldModal = () => { + setIsRemoveHoldModalOpen(false); + }; + // Render component return ( <> @@ -587,6 +612,12 @@ const IpaCertificates = (props: PropsToIpaCertificates) => { onClose={onCloseRevokeModal} onRefresh={props.onRefresh} /> + )} diff --git a/src/components/modals/RemoveHoldCertificate.tsx b/src/components/modals/RemoveHoldCertificate.tsx new file mode 100644 index 00000000..28be9c4d --- /dev/null +++ b/src/components/modals/RemoveHoldCertificate.tsx @@ -0,0 +1,97 @@ +import React from "react"; +// PatternFly +import { Button } from "@patternfly/react-core"; +// Modals +import InformationModalLayout from "../layouts/InformationModalLayout"; +// Components +import TextLayout from "../layouts/TextLayout"; +// Hooks +import useAlerts from "src/hooks/useAlerts"; +// Data types +import { CertificateData } from "../Form/IpaCertificates"; +// Utils +import { parseDn } from "src/utils/utils"; +// RPC +import { + ErrorResult, + useRemoveHoldCertificateMutation, +} from "src/services/rpc"; + +interface PropsToRemoveHoldCertificate { + certificate: CertificateData; + isOpen: boolean; + onClose: () => void; + onRefresh: () => void; +} + +const RemoveHoldCertificate = (props: PropsToRemoveHoldCertificate) => { + // Alerts to show in the UI + const alerts = useAlerts(); + + // Prepare "cert_remove_hold" API call + const [certRemoveHold] = useRemoveHoldCertificateMutation(); + + const onRemoveHold = () => { + // Prepare payload + const serialNumber = props.certificate.certInfo.serial_number; + const cacn = props.certificate.certInfo.cacn; + const payload = [serialNumber, cacn]; + + // Perform the API call + certRemoveHold(payload).then((response) => { + if ("data" in response) { + if (response.data.result) { + // Close modal + props.onClose(); + // Set alert: success + alerts.addAlert( + "remove-hold-certificate-success", + "Certificate hold removed", + "success" + ); + } else if (response.data.error) { + // Set alert: error + const errorMessage = response.data.error as ErrorResult; + alerts.addAlert( + "remove-hold-certificate-error", + errorMessage.message, + "danger" + ); + } + // Refresh data to show new changes in the UI + props.onRefresh(); + } + }); + }; + + const infoModalActions = [ + , + , + ]; + + const contentMessage = ( + Do you want to remove the certificate hold? + ); + + return ( + <> + + + + ); +}; + +export default RemoveHoldCertificate; diff --git a/src/services/rpc.ts b/src/services/rpc.ts index 79fb0a99..cbba3e70 100644 --- a/src/services/rpc.ts +++ b/src/services/rpc.ts @@ -422,6 +422,22 @@ export const api = createApi({ }); }, }), + removeHoldCertificate: build.mutation({ + query: (payload) => { + const params = [ + [payload[0]], + { + cacn: payload[1], + version: API_VERSION_BACKUP, + }, + ]; + + return getCommand({ + method: "cert_remove_hold", + params: params, + }); + }, + }), }), }); @@ -442,4 +458,5 @@ export const { useRemoveCertificateMutation, useGetCertificateAuthorityQuery, useRevokeCertificateMutation, + useRemoveHoldCertificateMutation, } = api;