fix(striker-ui): add delete SSH key conflict

main
Tsu-ba-me 2 years ago
parent a9941af621
commit eddb0ea393
  1. 260
      striker-ui/components/StrikerConfig/ManageChangedSSHKeysForm.tsx

@ -2,11 +2,14 @@ import { FC, useMemo, useRef } from 'react';
import API_BASE_URL from '../../lib/consts/API_BASE_URL'; import API_BASE_URL from '../../lib/consts/API_BASE_URL';
import api from '../../lib/api';
import ConfirmDialog from '../ConfirmDialog';
import Divider from '../Divider'; import Divider from '../Divider';
import FlexBox from '../FlexBox'; import FlexBox from '../FlexBox';
import handleAPIError from '../../lib/handleAPIError';
import Link from '../Link'; import Link from '../Link';
import List from '../List'; import List from '../List';
import MessageBox from '../MessageBox'; import MessageBox, { Message } from '../MessageBox';
import { ExpandablePanel } from '../Panels'; import { ExpandablePanel } from '../Panels';
import periodicFetch from '../../lib/fetchers/periodicFetch'; import periodicFetch from '../../lib/fetchers/periodicFetch';
import { BodyText } from '../Text'; import { BodyText } from '../Text';
@ -19,13 +22,31 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
}) => { }) => {
const { protect } = useProtect(); const { protect } = useProtect();
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const listRef = useRef<ListForwardedRefContent>({}); const listRef = useRef<ListForwardedRefContent>({});
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>(
undefined,
protect,
);
const [changedSSHKeys, setChangedSSHKeys] = useProtectedState<ChangedSSHKeys>( const [changedSSHKeys, setChangedSSHKeys] = useProtectedState<ChangedSSHKeys>(
{}, {},
protect, protect,
); );
const [confirmDialogProps, setConfirmDialogProps] =
useProtectedState<ConfirmDialogProps>(
{
actionProceedText: '',
content: '',
titleText: '',
},
protect,
);
const apiMessageElement = useMemo(
() => apiMessage && <MessageBox {...apiMessage} />,
[apiMessage],
);
const isAllowCheckAll = useMemo( const isAllowCheckAll = useMemo(
() => Object.keys(changedSSHKeys).length > 1, () => Object.keys(changedSSHKeys).length > 1,
[changedSSHKeys], [changedSSHKeys],
@ -34,7 +55,12 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
const { isLoading } = periodicFetch<APISSHKeyConflictOverviewList>( const { isLoading } = periodicFetch<APISSHKeyConflictOverviewList>(
`${API_BASE_URL}/ssh-key/conflict`, `${API_BASE_URL}/ssh-key/conflict`,
{ {
refreshInterval, onError: (error) => {
setAPIMessage({
children: `Failed to fetch SSH key conflicts. Error: ${error}`,
type: 'error',
});
},
onSuccess: (data) => { onSuccess: (data) => {
setChangedSSHKeys((previous) => setChangedSSHKeys((previous) =>
Object.values(data).reduce<ChangedSSHKeys>((nyu, stateList) => { Object.values(data).reduce<ChangedSSHKeys>((nyu, stateList) => {
@ -53,106 +79,154 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
}, {}), }, {}),
); );
}, },
refreshInterval,
}, },
); );
return ( return (
<ExpandablePanel <>
header={<BodyText>Manage changed SSH keys</BodyText>} <ExpandablePanel
loading={isLoading} header={<BodyText>Manage changed SSH keys</BodyText>}
> loading={isLoading}
<FlexBox spacing=".2em"> >
<BodyText> <FlexBox spacing=".2em">
The identity of the following targets have unexpectedly changed. <BodyText>
</BodyText> The identity of the following targets have unexpectedly changed.
<MessageBox type="warning" isAllowClose> </BodyText>
If you haven&apos;t rebuilt the listed targets, then you could be <MessageBox type="warning" isAllowClose>
experiencing a &quot; If you haven&apos;t rebuilt the listed targets, then you could be
<Link experiencing a &quot;
href={mitmExternalHref} <Link
sx={{ display: 'inline-flex' }} href={mitmExternalHref}
target="_blank" sx={{ display: 'inline-flex' }}
> target="_blank"
Man In The Middle
</Link>
&quot; attack. Please verify the targets have changed for a known
reason before proceeding to remove the broken keys.
</MessageBox>
<List
header={
<FlexBox
row
spacing=".3em"
sx={{
width: '100%',
'& > :not(:last-child)': {
display: { xs: 'none', sm: 'flex' },
},
'& > :last-child': {
display: { xs: 'initial', sm: 'none' },
marginLeft: 0,
},
}}
> >
<FlexBox row spacing=".3em" sx={{ flexBasis: 'calc(50% + 1em)' }}> Man In The Middle
<BodyText>Host name</BodyText> </Link>
<Divider sx={{ flexGrow: 1 }} /> &quot; attack. Please verify the targets have changed for a known
</FlexBox> reason before proceeding to remove the broken keys.
<FlexBox row spacing=".3em" sx={{ flexGrow: 1 }}> </MessageBox>
<BodyText>IP address</BodyText> <List
header={
<FlexBox
row
spacing=".3em"
sx={{
width: '100%',
'& > :not(:last-child)': {
display: { xs: 'none', sm: 'flex' },
},
'& > :last-child': {
display: { xs: 'initial', sm: 'none' },
marginLeft: 0,
},
}}
>
<FlexBox
row
spacing=".3em"
sx={{ flexBasis: 'calc(50% + 1em)' }}
>
<BodyText>Host name</BodyText>
<Divider sx={{ flexGrow: 1 }} />
</FlexBox>
<FlexBox row spacing=".3em" sx={{ flexGrow: 1 }}>
<BodyText>IP address</BodyText>
<Divider sx={{ flexGrow: 1 }} />
</FlexBox>
<Divider sx={{ flexGrow: 1 }} /> <Divider sx={{ flexGrow: 1 }} />
</FlexBox> </FlexBox>
<Divider sx={{ flexGrow: 1 }} /> }
</FlexBox> allowCheckAll={isAllowCheckAll}
} allowCheckItem
allowCheckAll={isAllowCheckAll} allowDelete
allowCheckItem allowEdit={false}
allowDelete edit
allowEdit={false} listEmpty={
edit <BodyText align="center">No conflicting keys found.</BodyText>
listEmpty={ }
<BodyText align="center">No conflicting keys found.</BodyText> listItems={changedSSHKeys}
} onAllCheckboxChange={(event, isChecked) => {
listItems={changedSSHKeys} Object.keys(changedSSHKeys).forEach((key) => {
onAllCheckboxChange={(event, isChecked) => { changedSSHKeys[key].isChecked = isChecked;
Object.keys(changedSSHKeys).forEach((key) => { });
setChangedSSHKeys((previous) => ({ ...previous }));
}}
onDelete={() => {
let deleteCount = 0;
const deleteRequestBody = Object.entries(changedSSHKeys).reduce<{
[hostUUID: string]: string[];
}>((previous, [stateUUID, { hostUUID, isChecked }]) => {
if (isChecked) {
if (!previous[hostUUID]) {
previous[hostUUID] = [];
}
previous[hostUUID].push(stateUUID);
deleteCount += 1;
}
return previous;
}, {});
setConfirmDialogProps({
actionProceedText: 'Delete',
content: `Resolve ${deleteCount} SSH key conflicts. Please make sure the identity change(s) are expected to avoid MITM attacks.`,
onProceedAppend: () => {
api
.delete('/ssh-key/conflict', { data: deleteRequestBody })
.catch((error) => {
const emsg = handleAPIError(error);
emsg.children = `Failed to delete selected SSH key conflicts. ${emsg.children}`;
setAPIMessage(emsg);
});
},
proceedColour: 'red',
titleText: `Delete ${deleteCount} conflicting SSH keys?`,
});
confirmDialogRef.current.setOpen?.call(null, true);
}}
onItemCheckboxChange={(key, event, isChecked) => {
changedSSHKeys[key].isChecked = isChecked; changedSSHKeys[key].isChecked = isChecked;
});
setChangedSSHKeys((previous) => ({ ...previous }));
}}
onItemCheckboxChange={(key, event, isChecked) => {
changedSSHKeys[key].isChecked = isChecked;
listRef.current.setCheckAll?.call(
null,
Object.values(changedSSHKeys).every(
({ isChecked: isItemChecked }) => isItemChecked,
),
);
setChangedSSHKeys((previous) => ({ ...previous })); listRef.current.setCheckAll?.call(
}} null,
renderListItem={(hostUUID, { hostName, ipAddress }) => ( Object.values(changedSSHKeys).every(
<FlexBox ({ isChecked: isItemChecked }) => isItemChecked,
spacing={0} ),
sm="row" );
sx={{ width: '100%', '& > *': { flexBasis: '50%' } }}
xs="column" setChangedSSHKeys((previous) => ({ ...previous }));
> }}
<BodyText>{hostName}</BodyText> renderListItem={(hostUUID, { hostName, ipAddress }) => (
<BodyText>{ipAddress}</BodyText> <FlexBox
</FlexBox> spacing={0}
)} sm="row"
renderListItemCheckboxState={(key, { isChecked }) => sx={{ width: '100%', '& > *': { flexBasis: '50%' } }}
isChecked === true xs="column"
} >
ref={listRef} <BodyText>{hostName}</BodyText>
/> <BodyText>{ipAddress}</BodyText>
</FlexBox> </FlexBox>
</ExpandablePanel> )}
renderListItemCheckboxState={(key, { isChecked }) =>
isChecked === true
}
ref={listRef}
/>
</FlexBox>
{apiMessageElement}
</ExpandablePanel>
<ConfirmDialog {...confirmDialogProps} ref={confirmDialogRef} />
</>
); );
}; };

Loading…
Cancel
Save