|
|
@ -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,10 +79,12 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
}, {}), |
|
|
|
}, {}), |
|
|
|
); |
|
|
|
); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
refreshInterval, |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
|
|
|
|
<> |
|
|
|
<ExpandablePanel |
|
|
|
<ExpandablePanel |
|
|
|
header={<BodyText>Manage changed SSH keys</BodyText>} |
|
|
|
header={<BodyText>Manage changed SSH keys</BodyText>} |
|
|
|
loading={isLoading} |
|
|
|
loading={isLoading} |
|
|
@ -96,7 +124,11 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
}, |
|
|
|
}, |
|
|
|
}} |
|
|
|
}} |
|
|
|
> |
|
|
|
> |
|
|
|
<FlexBox row spacing=".3em" sx={{ flexBasis: 'calc(50% + 1em)' }}> |
|
|
|
<FlexBox |
|
|
|
|
|
|
|
row |
|
|
|
|
|
|
|
spacing=".3em" |
|
|
|
|
|
|
|
sx={{ flexBasis: 'calc(50% + 1em)' }} |
|
|
|
|
|
|
|
> |
|
|
|
<BodyText>Host name</BodyText> |
|
|
|
<BodyText>Host name</BodyText> |
|
|
|
<Divider sx={{ flexGrow: 1 }} /> |
|
|
|
<Divider sx={{ flexGrow: 1 }} /> |
|
|
|
</FlexBox> |
|
|
|
</FlexBox> |
|
|
@ -123,6 +155,45 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
|
|
|
|
|
|
|
|
setChangedSSHKeys((previous) => ({ ...previous })); |
|
|
|
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) => { |
|
|
|
onItemCheckboxChange={(key, event, isChecked) => { |
|
|
|
changedSSHKeys[key].isChecked = isChecked; |
|
|
|
changedSSHKeys[key].isChecked = isChecked; |
|
|
|
|
|
|
|
|
|
|
@ -152,7 +223,10 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
ref={listRef} |
|
|
|
ref={listRef} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</FlexBox> |
|
|
|
</FlexBox> |
|
|
|
|
|
|
|
{apiMessageElement} |
|
|
|
</ExpandablePanel> |
|
|
|
</ExpandablePanel> |
|
|
|
|
|
|
|
<ConfirmDialog {...confirmDialogProps} ref={confirmDialogRef} /> |
|
|
|
|
|
|
|
</> |
|
|
|
); |
|
|
|
); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|