From 9484ac30bc6850dfcfb4d6bdbc0c59dfce4c435d Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Tue, 11 Mar 2025 14:31:02 +0000 Subject: [PATCH] add UI for certificate authority list (#26955) For #26605 This is the UI for the certificate authority list that shows the added CAs. This includes: **new CA section and list on integration page** ![image](https://github.com/user-attachments/assets/4159bd6d-632e-4adc-ae45-d83f824380ae) **empty CA list state** ![image](https://github.com/user-attachments/assets/8f27cd8b-53b2-4cf3-ac64-8fa6ec0a2ae2) **gitops mode on add CA card and CA list** ![image](https://github.com/user-attachments/assets/981a353c-f515-44ed-90d5-f55e412053ba) ![image](https://github.com/user-attachments/assets/286dc503-f2cd-4329-aa46-6301df75b826) - [ ] Added/updated automated tests - [x] Manual QA for all new/changed functionality --- .../GitOpsModeTooltipWrapper.tsx | 10 +- .../GitOpsModeTooltipWrapper/_styles.scss | 6 + frontend/components/ListItem/ListItem.tsx | 1 + .../components/graphics/FileCertificate.tsx | 66 ++++++++ frontend/components/graphics/index.ts | 2 + frontend/interfaces/integration.ts | 23 ++- .../components/UploadList/UploadList.tsx | 28 ++-- .../IntegrationsPage/IntegrationNavItems.tsx | 8 + .../CertificateAuthorities.tsx | 148 ++++++++++++++++++ .../cards/CertificateAuthorities/_styles.scss | 6 + .../AddCertAuthorityCard.tsx | 39 +++++ .../AddCertAuthorityCard/_styles.scss | 20 +++ .../components/AddCertAuthorityCard/index.ts | 1 + .../CertAuthorityListHeader.tsx | 40 +++++ .../CertAuthorityListHeader/_styles.scss | 15 ++ .../CertAuthorityListHeader/index.ts | 1 + .../CertAuthorityListItem.tsx | 72 +++++++++ .../CertAuthorityListItem/_styles.scss | 6 + .../components/CertAuthorityListItem/index.ts | 1 + .../CertificateAuthorityList.tsx | 43 +++++ .../CertificateAuthorityList/_styles.scss | 3 + .../CertificateAuthorityList/index.ts | 1 + .../cards/CertificateAuthorities/helpers.tsx | 81 ++++++++++ .../cards/CertificateAuthorities/index.ts | 1 + .../cards/MdmSettings/MdmSettings.tsx | 1 + frontend/router/index.tsx | 2 + frontend/router/paths.ts | 1 + 27 files changed, 606 insertions(+), 20 deletions(-) create mode 100644 frontend/components/graphics/FileCertificate.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/CertificateAuthorities.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/AddCertAuthorityCard.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/CertAuthorityListHeader.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/CertAuthorityListItem.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/CertificateAuthorityList.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/helpers.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/index.ts diff --git a/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx b/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx index 1f21a19b08..79a249be74 100644 --- a/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx +++ b/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx @@ -30,16 +30,16 @@ const GitOpsModeTooltipWrapper = ({ const tipContent = ( // at this point repoURL will always be defined - <> +
{repoURL && ( - <> + Manage in{" "}
- +
)} - (GitOps mode enabled) - + (GitOps mode enabled) +
); return ( diff --git a/frontend/components/GitOpsModeTooltipWrapper/_styles.scss b/frontend/components/GitOpsModeTooltipWrapper/_styles.scss index 580b002f08..d30a20b039 100644 --- a/frontend/components/GitOpsModeTooltipWrapper/_styles.scss +++ b/frontend/components/GitOpsModeTooltipWrapper/_styles.scss @@ -1,4 +1,10 @@ .gitops-mode-tooltip-wrapper { + &__tooltip-content { + display: flex; + flex-direction: column; + align-items: center; + } + .component__tooltip-wrapper { &__tip-text { cursor: default; diff --git a/frontend/components/ListItem/ListItem.tsx b/frontend/components/ListItem/ListItem.tsx index a6fffa2bb2..121b30ddcf 100644 --- a/frontend/components/ListItem/ListItem.tsx +++ b/frontend/components/ListItem/ListItem.tsx @@ -17,6 +17,7 @@ export type ISupportedGraphicNames = Extract< | "file-pkg" | "file-p7m" | "file-pem" + | "file-certificate" >; /** diff --git a/frontend/components/graphics/FileCertificate.tsx b/frontend/components/graphics/FileCertificate.tsx new file mode 100644 index 0000000000..64f17e2453 --- /dev/null +++ b/frontend/components/graphics/FileCertificate.tsx @@ -0,0 +1,66 @@ +import React from "react"; + +const FileCertificate = () => { + return ( + + + + + + + + + + + + + + + + + + + ); +}; + +export default FileCertificate; diff --git a/frontend/components/graphics/index.ts b/frontend/components/graphics/index.ts index 6944193046..4c45b44141 100644 --- a/frontend/components/graphics/index.ts +++ b/frontend/components/graphics/index.ts @@ -13,6 +13,7 @@ import FilePkg from "./FilePkg"; import FileP7m from "./FileP7m"; import FilePem from "./FilePem"; import FileVpp from "./FileVpp"; +import FileCertificate from "./FileCertificate"; import AppStore from "./AppStore"; import EmptyHosts from "./EmptyHosts"; import EmptyTeams from "./EmptyTeams"; @@ -49,6 +50,7 @@ export const GRAPHIC_MAP = { "file-p7m": FileP7m, "file-pem": FilePem, "file-vpp": FileVpp, + "file-certificate": FileCertificate, "app-store": AppStore, // Used in non-editable file uploader for vpp apps edit modal // Other graphics "collecting-results": CollectingResults, diff --git a/frontend/interfaces/integration.ts b/frontend/interfaces/integration.ts index 802fd1fe5f..26e7f79eda 100644 --- a/frontend/interfaces/integration.ts +++ b/frontend/interfaces/integration.ts @@ -17,13 +17,30 @@ export interface IZendeskIntegration { enable_software_vulnerabilities?: boolean; } -export interface IScepIntegration { +export interface ICertificatesIntegrationNDES { url: string; admin_url: string; username: string; password: string; } +export interface ICertificatesIntegrationDigicert { + id: number; + name: string; + api_token: string; + profile_id: string; + certificate_common_name: string; + certificate_user_principal_names: string[]; + certificate_seat_id: string; +} + +export interface ICertificatesIntegrationCustomSCEP { + id: number; + name: string; + server_url: string; + challenge: string; +} + export interface IIntegration { url: string; username?: string; @@ -91,7 +108,9 @@ export interface IZendeskJiraIntegrations { // Partial`, but that leads to a mess of types to resolve. export interface IGlobalIntegrations extends IZendeskJiraIntegrations { google_calendar?: IGlobalCalendarIntegration[] | null; - ndes_scep_proxy?: IScepIntegration | null; + ndes_scep_proxy?: ICertificatesIntegrationNDES | null; + digicert?: ICertificatesIntegrationDigicert[]; + custom_scep_proxy?: ICertificatesIntegrationCustomSCEP[]; } export interface ITeamIntegrations extends IZendeskJiraIntegrations { diff --git a/frontend/pages/ManageControlsPage/components/UploadList/UploadList.tsx b/frontend/pages/ManageControlsPage/components/UploadList/UploadList.tsx index 80475259ea..6603a5136c 100644 --- a/frontend/pages/ManageControlsPage/components/UploadList/UploadList.tsx +++ b/frontend/pages/ManageControlsPage/components/UploadList/UploadList.tsx @@ -1,23 +1,24 @@ import React from "react"; +import classnames from "classnames"; const baseClass = "upload-list"; -interface IUploadListProps { +interface IUploadListProps { /** The attribute name that is used for the react key for each list item */ - keyAttribute: string; - listItems: any[]; // TODO: typings - HeadingComponent?: (props: any) => JSX.Element; // TODO: Typings - ListItemComponent: (props: { listItem: any }) => JSX.Element; // TODO: types - sortCompareFn?: (a: any, b: any) => number; + keyAttribute: keyof T; + listItems: T[]; + HeadingComponent?: (props: any) => JSX.Element; + ListItemComponent: (props: { listItem: T }) => JSX.Element; + className?: string; } -const UploadList = ({ +const UploadList = ({ keyAttribute, listItems, HeadingComponent, ListItemComponent, - sortCompareFn, -}: IUploadListProps) => { + className, +}: IUploadListProps) => { const items = listItems.map((listItem) => { return (
  • ); }); + + const classNames = classnames(baseClass, className); + return ( -
    +
    {HeadingComponent && (
    )} -
      - {sortCompareFn ? items.sort(sortCompareFn) : items} -
    +
      {items}
    ); }; diff --git a/frontend/pages/admin/IntegrationsPage/IntegrationNavItems.tsx b/frontend/pages/admin/IntegrationsPage/IntegrationNavItems.tsx index f048926491..40d175edff 100644 --- a/frontend/pages/admin/IntegrationsPage/IntegrationNavItems.tsx +++ b/frontend/pages/admin/IntegrationsPage/IntegrationNavItems.tsx @@ -5,6 +5,7 @@ import Integrations from "./cards/Integrations"; import MdmSettings from "./cards/MdmSettings"; import Calendars from "./cards/Calendars"; import ChangeManagement from "./cards/ChangeManagement"; +import CertificateAuthorities from "./cards/CertificateAuthorities"; const integrationSettingsNavItems: ISideNavItem[] = [ // TODO: types @@ -32,6 +33,13 @@ const integrationSettingsNavItems: ISideNavItem[] = [ path: PATHS.ADMIN_INTEGRATIONS_CHANGE_MANAGEMENT, 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, + // }, ]; export default integrationSettingsNavItems; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/CertificateAuthorities.tsx b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/CertificateAuthorities.tsx new file mode 100644 index 0000000000..a639c6cc5d --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/CertificateAuthorities.tsx @@ -0,0 +1,148 @@ +import React, { useContext, useMemo, useState } from "react"; +import { Link } from "react-router"; + +import paths from "router/paths"; +import { AppContext } from "context/app"; +import createMockConfig from "__mocks__/configMock"; + +import SectionHeader from "components/SectionHeader"; +import CustomLink from "components/CustomLink"; + +import CertificateAuthorityList from "./components/CertificateAuthorityList"; +import { + generateListData, + getCertificateAuthority, + ICertAuthority, +} from "./helpers"; +import AddCertAuthorityCard from "./components/AddCertAuthorityCard"; + +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 [showAddCertAuthorityModal, setShowAddCertAuthorityModal] = useState( + false + ); + const [showEditCertAuthorityModal, setShowEditCertAuthorityModal] = useState( + false + ); + const [ + showDeleteCertAuthoirtyModal, + setShowDeleteCertAuthorityModal, + ] = useState(false); + + const certificateAuthorities = useMemo(() => { + if (!config) return []; + return generateListData( + config?.integrations.ndes_scep_proxy, + config?.integrations.digicert, + config?.integrations.custom_scep_proxy + ); + }, [config]); + + const onAddCertAuthority = () => { + setShowAddCertAuthorityModal(true); + }; + + const onEditCertAuthority = (cert: ICertAuthority) => { + // TODO: use useCallback + const ca = getCertificateAuthority( + cert.id, + config?.integrations.ndes_scep_proxy, + config?.integrations.digicert, + config?.integrations.custom_scep_proxy + ); + console.log(ca); + setShowEditCertAuthorityModal(true); + }; + + const onDeleteCertAuthority = (cert: ICertAuthority) => { + // TODO: use useCallback + getCertificateAuthority( + cert.id, + config?.integrations.ndes_scep_proxy, + config?.integrations.digicert, + config?.integrations.custom_scep_proxy + ); + setShowDeleteCertAuthorityModal(true); + }; + + const renderContent = () => { + if (certificateAuthorities.length === 0) { + return ; + } + + return ( + + ); + }; + + return ( +
    + +

    + To help your end users connect to Wi-Fi or VPNs, you can add your + certificate authority. Then, head over to{" "} + + Controls {">"} OS Settings {">"} Custom + {" "} + settings to configure how certificates are delivered to your hosts.{" "} + +

    + {renderContent()} + {showAddCertAuthorityModal &&
    Modal showing
    } + {showEditCertAuthorityModal &&
    Modal showing
    } + {showDeleteCertAuthoirtyModal &&
    Modal showing
    } +
    + ); +}; + +export default CertificateAuthorities; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/_styles.scss new file mode 100644 index 0000000000..15a6c8dede --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/_styles.scss @@ -0,0 +1,6 @@ +.certificate-authorities { + + &__page-description { + margin: 0 0 $pad-xlarge; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/AddCertAuthorityCard.tsx b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/AddCertAuthorityCard.tsx new file mode 100644 index 0000000000..d2e30a1657 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/AddCertAuthorityCard.tsx @@ -0,0 +1,39 @@ +import React from "react"; + +import Button from "components/buttons/Button"; +import Card from "components/Card"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; + +const baseClass = "add-cert-authority-card"; + +interface IAddCertAuthoityCardProps { + onAddCertAuthority: () => void; +} + +const AddCertAuthorityCard = ({ + onAddCertAuthority, +}: IAddCertAuthoityCardProps) => { + return ( + +
    +

    + Add your certificate authority (CA) +

    +

    Help your end users connect to Wi-Fi or VPNs.

    +
    + ( + + )} + /> +
    + ); +}; + +export default AddCertAuthorityCard; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/_styles.scss new file mode 100644 index 0000000000..0ffb1e3f81 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/_styles.scss @@ -0,0 +1,20 @@ +.add-cert-authority-card { + display: flex; + flex-direction: column; + align-items: center; + gap: $pad-medium; + + &__content { + text-align: center; + + p { + margin: 0; + } + } + + &__title { + font-size: $small; + font-weight: $bold; + margin-bottom: $pad-small; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/index.ts b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/index.ts new file mode 100644 index 0000000000..45aa0e07e2 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/AddCertAuthorityCard/index.ts @@ -0,0 +1 @@ +export { default } from "./AddCertAuthorityCard"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/CertAuthorityListHeader.tsx b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/CertAuthorityListHeader.tsx new file mode 100644 index 0000000000..b3046eec6d --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/CertAuthorityListHeader.tsx @@ -0,0 +1,40 @@ +import Button from "components/buttons/Button"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; +import Icon from "components/Icon"; +import React from "react"; + +const baseClass = "cert-authority-list-header"; + +interface ICertAuthorityListHeaderProps { + onClickAddCertAuthority: () => void; +} + +const CertAuthorityListHeader = ({ + onClickAddCertAuthority, +}: ICertAuthorityListHeaderProps) => { + return ( +
    + Certificate authority (CA) + + ( + + )} + /> + +
    + ); +}; + +export default CertAuthorityListHeader; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/_styles.scss new file mode 100644 index 0000000000..8326ad57e7 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/_styles.scss @@ -0,0 +1,15 @@ +.cert-authority-list-header { + display: flex; + justify-content: space-between; + align-items: center; + + &__name { + font-size: $x-small; + font-weight: $bold; + } + + &__btn-label { + display: flex; + gap: $pad-small; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/index.ts b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/index.ts new file mode 100644 index 0000000000..49c123c2ff --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListHeader/index.ts @@ -0,0 +1 @@ +export { default } from "./CertAuthorityListHeader"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/CertAuthorityListItem.tsx b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/CertAuthorityListItem.tsx new file mode 100644 index 0000000000..3c70797613 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/CertAuthorityListItem.tsx @@ -0,0 +1,72 @@ +import React from "react"; + +import ListItem from "components/ListItem"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; +import Button from "components/buttons/Button"; +import Icon from "components/Icon"; + +import { ICertAuthority } from "../../helpers"; + +const baseClass = "cert-authority-list-item"; + +interface IActionsProps { + onEdit: () => void; + onDelete: () => void; +} + +const Actions = ({ onEdit, onDelete }: IActionsProps) => { + return ( + <> + ( + + )} + /> + ( + + )} + /> + + ); +}; + +interface ICertAuthorityListItemProps { + cert: ICertAuthority; + onClickEdit: () => void; + onClickDelete: () => void; +} + +const CertAuthorityListItem = ({ + cert, + onClickEdit, + onClickDelete, +}: ICertAuthorityListItemProps) => { + return ( + } + /> + ); +}; + +export default CertAuthorityListItem; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/_styles.scss new file mode 100644 index 0000000000..1d167faf56 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/_styles.scss @@ -0,0 +1,6 @@ +.cert-authority-list-item { + &__action-button { + width: 40px; + height: 40px; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/index.ts b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/index.ts new file mode 100644 index 0000000000..83fd53f67f --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertAuthorityListItem/index.ts @@ -0,0 +1 @@ +export { default } from "./CertAuthorityListItem"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/CertificateAuthorityList.tsx b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/CertificateAuthorityList.tsx new file mode 100644 index 0000000000..b7695f0661 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/CertificateAuthorityList.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +import UploadList from "pages/ManageControlsPage/components/UploadList"; + +import CertAuthorityListHeader from "../CertAuthorityListHeader"; +import CertAuthorityListItem from "../CertAuthorityListItem"; +import { ICertAuthority } from "../../helpers"; + +const baseClass = "certificate-authority-list"; + +interface ICertificateAuthorityListProps { + certAuthorities: ICertAuthority[]; + onAddCertAuthority: () => void; + onClickEdit: (cert: ICertAuthority) => void; + onClickDelete: (cert: ICertAuthority) => void; +} + +const CertificateAuthorityList = ({ + certAuthorities, + onAddCertAuthority, + onClickEdit, + onClickDelete, +}: ICertificateAuthorityListProps) => { + return ( + + className={baseClass} + keyAttribute="name" + listItems={certAuthorities} + HeadingComponent={() => ( + + )} + ListItemComponent={({ listItem }) => ( + onClickEdit(listItem)} + onClickDelete={() => onClickDelete(listItem)} + /> + )} + /> + ); +}; + +export default CertificateAuthorityList; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/_styles.scss new file mode 100644 index 0000000000..71bc9ca3e7 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/_styles.scss @@ -0,0 +1,3 @@ +.certificate-authority-list { + +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/index.ts b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/index.ts new file mode 100644 index 0000000000..b6a11ecf76 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/components/CertificateAuthorityList/index.ts @@ -0,0 +1 @@ +export { default } from "./CertificateAuthorityList"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/helpers.tsx b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/helpers.tsx new file mode 100644 index 0000000000..a69feb63f4 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/helpers.tsx @@ -0,0 +1,81 @@ +import { + ICertificatesIntegrationCustomSCEP, + ICertificatesIntegrationDigicert, + ICertificatesIntegrationNDES, +} from "interfaces/integration"; + +export interface ICertAuthority { + id: string; + name: string; + description: string; +} + +export const generateListData = ( + ndesProxy?: ICertificatesIntegrationNDES | null, + digicertCerts?: ICertificatesIntegrationDigicert[], + customProxies?: ICertificatesIntegrationCustomSCEP[] +) => { + const listData: ICertAuthority[] = []; + + // these values for the certificateAuthority is meant to be a hard coded . + if (ndesProxy) { + listData.push({ + id: "ndes", // only ever one NDES so no need to make the id dynamic + name: "NDES", + description: "Microsoft Network Device Enrollment Service (NDES)", + }); + } + + if (digicertCerts?.length) { + digicertCerts.forEach((cert) => { + listData.push({ + id: `digicert-${cert.id}`, + name: cert.name, + description: "DigiCert", + }); + }); + } + + if (customProxies?.length) { + customProxies.forEach((cert) => { + listData.push({ + id: `custom-scep-proxy-${cert.id}`, + name: cert.name, + description: "Custom Simple Certificate Enrollment Protocol (SCEP)", + }); + }); + } + + return listData.sort((a, b) => + a.name.toLowerCase().localeCompare(b.name.toLocaleLowerCase()) + ); +}; + +/** + * Gets the original certificate aithority integration object from the id + * of the data representing a certificate authority list item. + */ +export const getCertificateAuthority = ( + id: string, + ncspProxy?: ICertificatesIntegrationNDES | null, + digicertCerts?: ICertificatesIntegrationDigicert[], + customProxies?: ICertificatesIntegrationCustomSCEP[] +) => { + if (id === "ndes") { + return ncspProxy as ICertificatesIntegrationNDES; + } + + if (id.includes("digicert")) { + return digicertCerts?.find( + (cert) => id.split("digicert-")[1] === cert.id.toString() + ); + } + + if (id.includes("custom-scep-proxy")) { + return customProxies?.find( + (cert) => id.split("custom-scep-proxy-")[1] === cert.id.toString() + ); + } + + return null; +}; diff --git a/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/index.ts b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/index.ts new file mode 100644 index 0000000000..deed23e97c --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/CertificateAuthorities/index.ts @@ -0,0 +1 @@ +export { default } from "./CertificateAuthorities"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx index 1177404348..d663ff9545 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx @@ -133,6 +133,7 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => { isVppOn={!noVppTokenUploaded} isPremiumTier={!!isPremiumTier} /> + {/* TODO: digicert update: remove scep section when digicert feature is ready */} + {/* TODO: digicert update: remove scep route when digicert feature is ready */} {/* This redirect is used to handle old apple automatic enrollments page */}