|
|
@ -13,35 +13,30 @@ 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'; |
|
|
|
import useProtect from '../../hooks/useProtect'; |
|
|
|
import useChecklist from '../../hooks/useChecklist'; |
|
|
|
import useProtectedState from '../../hooks/useProtectedState'; |
|
|
|
import useProtectedState from '../../hooks/useProtectedState'; |
|
|
|
|
|
|
|
|
|
|
|
const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
mitmExternalHref = 'https://en.wikipedia.org/wiki/Man-in-the-middle_attack', |
|
|
|
mitmExternalHref = 'https://en.wikipedia.org/wiki/Man-in-the-middle_attack', |
|
|
|
refreshInterval = 60000, |
|
|
|
refreshInterval = 60000, |
|
|
|
}) => { |
|
|
|
}) => { |
|
|
|
const { protect } = useProtect(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({}); |
|
|
|
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({}); |
|
|
|
const listRef = useRef<ListForwardedRefContent>({}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>( |
|
|
|
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>( |
|
|
|
undefined, |
|
|
|
undefined, |
|
|
|
protect, |
|
|
|
|
|
|
|
); |
|
|
|
); |
|
|
|
const [changedSSHKeys, setChangedSSHKeys] = useProtectedState<ChangedSSHKeys>( |
|
|
|
const [changedSSHKeys, setChangedSSHKeys] = useProtectedState<ChangedSSHKeys>( |
|
|
|
{}, |
|
|
|
{}, |
|
|
|
protect, |
|
|
|
|
|
|
|
); |
|
|
|
); |
|
|
|
const [confirmDialogProps, setConfirmDialogProps] = |
|
|
|
const [confirmDialogProps, setConfirmDialogProps] = |
|
|
|
useProtectedState<ConfirmDialogProps>( |
|
|
|
useProtectedState<ConfirmDialogProps>({ |
|
|
|
{ |
|
|
|
|
|
|
|
actionProceedText: '', |
|
|
|
actionProceedText: '', |
|
|
|
content: '', |
|
|
|
content: '', |
|
|
|
titleText: '', |
|
|
|
titleText: '', |
|
|
|
}, |
|
|
|
}); |
|
|
|
protect, |
|
|
|
|
|
|
|
); |
|
|
|
const { checks, getCheck, hasAllChecks, hasChecks, setAllChecks, setCheck } = |
|
|
|
|
|
|
|
useChecklist({ list: changedSSHKeys }); |
|
|
|
|
|
|
|
|
|
|
|
const apiMessageElement = useMemo( |
|
|
|
const apiMessageElement = useMemo( |
|
|
|
() => apiMessage && <MessageBox {...apiMessage} />, |
|
|
|
() => apiMessage && <MessageBox {...apiMessage} />, |
|
|
@ -140,40 +135,40 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
allowCheckItem |
|
|
|
allowCheckItem |
|
|
|
allowDelete |
|
|
|
allowDelete |
|
|
|
allowEdit={false} |
|
|
|
allowEdit={false} |
|
|
|
|
|
|
|
disableDelete={!hasChecks} |
|
|
|
edit |
|
|
|
edit |
|
|
|
|
|
|
|
getListCheckboxProps={() => ({ |
|
|
|
|
|
|
|
checked: hasAllChecks, |
|
|
|
|
|
|
|
})} |
|
|
|
listEmpty={ |
|
|
|
listEmpty={ |
|
|
|
<BodyText align="center">No conflicting keys found.</BodyText> |
|
|
|
<BodyText align="center">No conflicting keys found.</BodyText> |
|
|
|
} |
|
|
|
} |
|
|
|
listItems={changedSSHKeys} |
|
|
|
listItems={changedSSHKeys} |
|
|
|
onAllCheckboxChange={(event, isChecked) => { |
|
|
|
onAllCheckboxChange={(event, checked) => { |
|
|
|
Object.keys(changedSSHKeys).forEach((key) => { |
|
|
|
setAllChecks(checked); |
|
|
|
changedSSHKeys[key].isChecked = isChecked; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setChangedSSHKeys((previous) => ({ ...previous })); |
|
|
|
|
|
|
|
}} |
|
|
|
}} |
|
|
|
onDelete={() => { |
|
|
|
onDelete={() => { |
|
|
|
let deleteCount = 0; |
|
|
|
const deleteRequestBody = checks.reduce<{ |
|
|
|
|
|
|
|
|
|
|
|
const deleteRequestBody = Object.entries(changedSSHKeys).reduce<{ |
|
|
|
|
|
|
|
[hostUUID: string]: string[]; |
|
|
|
[hostUUID: string]: string[]; |
|
|
|
}>((previous, [stateUUID, { hostUUID, isChecked }]) => { |
|
|
|
}>((previous, stateUUID) => { |
|
|
|
if (isChecked) { |
|
|
|
const checked = getCheck(stateUUID); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!checked) return previous; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { hostUUID } = changedSSHKeys[stateUUID]; |
|
|
|
|
|
|
|
|
|
|
|
if (!previous[hostUUID]) { |
|
|
|
if (!previous[hostUUID]) { |
|
|
|
previous[hostUUID] = []; |
|
|
|
previous[hostUUID] = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
previous[hostUUID].push(stateUUID); |
|
|
|
previous[hostUUID].push(stateUUID); |
|
|
|
|
|
|
|
|
|
|
|
deleteCount += 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return previous; |
|
|
|
return previous; |
|
|
|
}, {}); |
|
|
|
}, {}); |
|
|
|
|
|
|
|
|
|
|
|
setConfirmDialogProps({ |
|
|
|
setConfirmDialogProps({ |
|
|
|
actionProceedText: 'Delete', |
|
|
|
actionProceedText: 'Delete', |
|
|
|
content: `Resolve ${deleteCount} SSH key conflicts. Please make sure the identity change(s) are expected to avoid MITM attacks.`, |
|
|
|
content: `Resolve ${checks.length} SSH key conflicts. Please make sure the identity change(s) are expected to avoid MITM attacks.`, |
|
|
|
onProceedAppend: () => { |
|
|
|
onProceedAppend: () => { |
|
|
|
api |
|
|
|
api |
|
|
|
.delete('/ssh-key/conflict', { data: deleteRequestBody }) |
|
|
|
.delete('/ssh-key/conflict', { data: deleteRequestBody }) |
|
|
@ -186,22 +181,13 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
proceedColour: 'red', |
|
|
|
proceedColour: 'red', |
|
|
|
titleText: `Delete ${deleteCount} conflicting SSH keys?`, |
|
|
|
titleText: `Delete ${checks.length} conflicting SSH keys?`, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
confirmDialogRef.current.setOpen?.call(null, true); |
|
|
|
confirmDialogRef.current.setOpen?.call(null, true); |
|
|
|
}} |
|
|
|
}} |
|
|
|
onItemCheckboxChange={(key, event, isChecked) => { |
|
|
|
onItemCheckboxChange={(key, event, checked) => { |
|
|
|
changedSSHKeys[key].isChecked = isChecked; |
|
|
|
setCheck(key, checked); |
|
|
|
|
|
|
|
|
|
|
|
listRef.current.setCheckAll?.call( |
|
|
|
|
|
|
|
null, |
|
|
|
|
|
|
|
Object.values(changedSSHKeys).every( |
|
|
|
|
|
|
|
({ isChecked: isItemChecked }) => isItemChecked, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setChangedSSHKeys((previous) => ({ ...previous })); |
|
|
|
|
|
|
|
}} |
|
|
|
}} |
|
|
|
renderListItem={(hostUUID, { hostName, ipAddress }) => ( |
|
|
|
renderListItem={(hostUUID, { hostName, ipAddress }) => ( |
|
|
|
<FlexBox |
|
|
|
<FlexBox |
|
|
@ -214,10 +200,7 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({ |
|
|
|
<BodyText>{ipAddress}</BodyText> |
|
|
|
<BodyText>{ipAddress}</BodyText> |
|
|
|
</FlexBox> |
|
|
|
</FlexBox> |
|
|
|
)} |
|
|
|
)} |
|
|
|
renderListItemCheckboxState={(key, { isChecked }) => |
|
|
|
renderListItemCheckboxState={(key) => getCheck(key)} |
|
|
|
isChecked === true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ref={listRef} |
|
|
|
|
|
|
|
/> |
|
|
|
/> |
|
|
|
</FlexBox> |
|
|
|
</FlexBox> |
|
|
|
{apiMessageElement} |
|
|
|
{apiMessageElement} |
|
|
|