From 1f262366ff0c3c1f483fd841f3a2e1799487bb99 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:49:08 -0800 Subject: [PATCH] WIP -- add new client cert UI I thought I'd use a selected} + > + None + CN (Common Name) + O (Organization Name) + OU (Organizational Unit Name) + C (Country Name) + ST (State or Province Name) + L (Locality Name) + + + + + { attribute === '' + ? + No filter + + : setValue(evt.target.value)} + /> } + + + + Limits the search to certificates where the {label} has a particular + attribute value (exact match). + + + ); +}; + +export default CertFilter; diff --git a/src/renderer/pages/ConnectForm.tsx b/src/renderer/pages/ConnectForm.tsx index a97b2dbe..e71ca41c 100644 --- a/src/renderer/pages/ConnectForm.tsx +++ b/src/renderer/pages/ConnectForm.tsx @@ -8,17 +8,20 @@ import { CardContent, Chip, Container, + FormControl, FormControlLabel, FormHelperText, Grid, + MenuItem, IconButton, styled, + Select, Switch, Typography, } from '@mui/material'; import React, { FC, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { CheckCircle, ChevronDown, Trash } from 'react-feather'; +import { CheckCircle, ChevronDown, ChevronRight, Trash } from 'react-feather'; import { ipcRenderer } from 'electron'; import { useSnackbar } from 'notistack'; import { set } from 'lodash'; @@ -33,9 +36,10 @@ import { import TextField from '../components/TextField'; import StyledCard from '../components/StyledCard'; import { formatTag } from '../../shared/validators'; -import { Connection, Record, Selector } from '../../shared/pb/api'; +import { ClientCertFromStore, Connection, Record, Selector } from '../../shared/pb/api'; import BeforeBackActionDialog from '../components/BeforeBackActionDialog'; import CertDetails from '../components/CertDetails'; +import CertFilter from '../components/CertFilter'; export const TextArea = styled(TextField)({ '& div.MuiFilledInput-root': { @@ -67,6 +71,44 @@ export const TextArea = styled(TextField)({ }, }); +const NestedAccordion = styled((props: AccordionProps) => ( + +))(({ theme }) => ({ + border: `1px solid ${theme.palette.divider}`, + marginLeft: theme.spacing(2), + width: '100%', + '&:not(:last-child)': { + borderBottom: 0, + }, + '&:before': { + display: 'none', + }, +})); + +const NestedAccordionSummary = styled((props: AccordionSummaryProps) => ( + } + {...props} + /> +))(({ theme }) => ({ + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .05)' + : 'rgba(0, 0, 0, .03)', + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .MuiAccordionSummary-content': { + marginLeft: theme.spacing(1), + }, +})); + +const NestedAccordionDetails = styled(AccordionDetails)(({ theme }) => ({ + borderTop: `1px solid ${theme.palette.divider}`, + paddingTop: theme.spacing(2), +})); + interface Props { onComplete?: () => void; } @@ -79,6 +121,7 @@ const initialConnData: Connection = { disableTlsVerification: false, caCert: undefined, clientCert: undefined, + clientCertFromStore: undefined, }; const ConnectForm: FC = () => { @@ -92,6 +135,9 @@ const ConnectForm: FC = () => { }; const { connectionID } = useParams(); const { enqueueSnackbar } = useSnackbar(); + const [clientCertPanel, setClientCertPanel] = React.useState( + false + ); const certRef = React.useRef(null); const keyRef = React.useRef(null); const [certText, setCertText] = useState(''); @@ -108,9 +154,15 @@ const ConnectForm: FC = () => { }); } else if (args.res.records.length === 1) { setTags(args.res.records[0].tags || []); - setConnection(args.res.records[0].conn || initialConnData); - setOriginalConnection(args.res.records[0].conn || initialConnData); - setShowCertInput(!args.res.records[0].conn.clientCert); + const { conn } = args.res.records[0]; + setConnection(conn || initialConnData); + setOriginalConnection(conn || initialConnData); + setShowCertInput(!conn.clientCert); + if (conn.clientCertFromStore !== undefined) { + setClientCertPanel('store'); + } else if (conn.clientCert) { + setClientCertPanel('file'); + } } }); ipcRenderer.once(GET_UNIQUE_TAGS, (_, args) => { @@ -180,6 +232,30 @@ const ConnectForm: FC = () => { } }; + useEffect(() => { + }, []); + + const saveClientCertFromStore = (value: ClientCertFromStore | undefined): void => { + setConnection({ + ...connection, + ...{ clientCertFromStore: value }, + }); + }; + + const saveClientCertIssuerFilter = (value: string | undefined): void => { + saveClientCertFromStore({ + ...connection?.clientCertFromStore, + ...{ issuerFilter: value }, + }); + }; + + const saveClientCertSubjectFilter = (value: string | undefined): void => { + saveClientCertFromStore({ + ...clientCertFromStore, + ...{ subjectFilter: value }, + }); + }; + const saveCertText = (value: string): void => { setCertText(value); setConnection((oldConnection) => { @@ -253,6 +329,14 @@ const ConnectForm: FC = () => { } return !!connection?.name && !!connection?.remoteAddr.trim(); }; + + const handleClientCertPanel = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setClientCertPanel(newExpanded ? panel : false); + }; + + const clientCertFromStoreEnabled = connection?.clientCertFromStore !== undefined; + return ( = () => { - {showCertInput && ( - - - - )} - {showCertInput && ( - - - Client Certificate Text + + + + Client certificate from system trust store -