You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.6 KiB
227 lines
6.6 KiB
import { FC, useCallback, useMemo, useRef, useState } from 'react'; |
|
import { Grid } from '@mui/material'; |
|
|
|
import ActionGroup from '../ActionGroup'; |
|
import api from '../../lib/api'; |
|
import handleAPIError from '../../lib/handleAPIError'; |
|
import MessageGroup, { MessageGroupForwardedRefContent } from '../MessageGroup'; |
|
import OutlinedInputWithLabel from '../OutlinedInputWithLabel'; |
|
import UncontrolledInput from '../UncontrolledInput'; |
|
import useFormikUtils from '../../hooks/useFormikUtils'; |
|
import Spinner from '../Spinner'; |
|
import schema from './testAccessSchema'; |
|
import { BodyText } from '../Text'; |
|
|
|
const TestAccessForm: FC<TestAccessFormProps> = (props) => { |
|
const { setResponse, tools } = props; |
|
|
|
const messageGroupRef = useRef<MessageGroupForwardedRefContent>(null); |
|
|
|
const [loadingInquiry, setLoadingInquiry] = useState<boolean>(false); |
|
const [moreActions, setMoreActions] = useState<ContainedButtonProps[]>([]); |
|
|
|
const setApiMessage = useCallback( |
|
(message?: Message) => |
|
messageGroupRef.current?.setMessage?.call(null, 'api', message), |
|
[], |
|
); |
|
|
|
const { disabledSubmit, formik, formikErrors, handleChange } = |
|
useFormikUtils<TestAccessFormikValues>({ |
|
initialValues: { |
|
ip: '', |
|
password: '', |
|
}, |
|
onSubmit: (values, { setSubmitting }) => { |
|
setApiMessage(); |
|
setLoadingInquiry(true); |
|
setMoreActions([]); |
|
setResponse(undefined); |
|
|
|
const { ip, password } = values; |
|
|
|
api |
|
.put<APICommandInquireHostResponseBody>('/command/inquire-host', { |
|
ipAddress: ip, |
|
password, |
|
}) |
|
.then(({ data }) => { |
|
const { badSshKeys, isConnected } = data; |
|
|
|
if (badSshKeys) { |
|
setApiMessage({ |
|
children: ( |
|
<> |
|
Host identification at {ip} changed. If this is valid, |
|
please delete the conflicting SSH host key. |
|
</> |
|
), |
|
type: 'warning', |
|
}); |
|
|
|
setMoreActions([ |
|
{ |
|
background: 'red', |
|
children: 'Delete keys', |
|
onClick: () => { |
|
tools.confirm.prepare({ |
|
actionProceedText: 'Delete', |
|
content: ( |
|
<BodyText> |
|
There's a different host key on {ip}, which could |
|
mean a MITM attack. But if this change is expected, |
|
you can delete the known host key(s) to resolve the |
|
conflict. |
|
</BodyText> |
|
), |
|
onProceedAppend: () => { |
|
tools.confirm.loading(true); |
|
|
|
api |
|
.delete('/ssh-key/conflict', { |
|
data: badSshKeys, |
|
}) |
|
.then(() => { |
|
tools.confirm.finish('Success', { |
|
children: ( |
|
<>Started job to delete host key(s) for {ip}.</> |
|
), |
|
}); |
|
|
|
setMoreActions([]); |
|
}) |
|
.catch((error) => { |
|
const emsg = handleAPIError(error); |
|
|
|
emsg.children = ( |
|
<>Failed to delete host key(s). {emsg.children}</> |
|
); |
|
|
|
tools.confirm.finish('Error', emsg); |
|
}); |
|
}, |
|
proceedColour: 'red', |
|
titleText: `Delete all known SSH host key(s) for ${ip}?`, |
|
}); |
|
|
|
tools.confirm.open(true); |
|
}, |
|
type: 'button', |
|
}, |
|
]); |
|
|
|
return; |
|
} |
|
|
|
if (!isConnected) { |
|
setApiMessage({ |
|
children: ( |
|
<> |
|
Failed to connect. Please make sure the credentials are |
|
correct, and the host is reachable from this striker. |
|
</> |
|
), |
|
type: 'warning', |
|
}); |
|
|
|
return; |
|
} |
|
|
|
setResponse({ |
|
...data, |
|
hostIpAddress: ip, |
|
hostPassword: password, |
|
}); |
|
|
|
setApiMessage(); |
|
}) |
|
.catch((error) => { |
|
const emsg = handleAPIError(error); |
|
|
|
emsg.children = ( |
|
<> |
|
Failed to access {ip}. {emsg.children} |
|
</> |
|
); |
|
|
|
setApiMessage(emsg); |
|
}) |
|
.finally(() => { |
|
setSubmitting(false); |
|
setLoadingInquiry(false); |
|
}); |
|
}, |
|
validationSchema: schema, |
|
}); |
|
|
|
const ipChain = useMemo<string>(() => 'ip', []); |
|
const passwordChain = useMemo<string>(() => 'password', []); |
|
|
|
return ( |
|
<Grid |
|
component="form" |
|
container |
|
columns={{ xs: 1, sm: 2 }} |
|
onSubmit={(event) => { |
|
event.preventDefault(); |
|
|
|
formik.submitForm(); |
|
}} |
|
spacing="1em" |
|
> |
|
<Grid item xs={1}> |
|
<UncontrolledInput |
|
input={ |
|
<OutlinedInputWithLabel |
|
disableAutofill |
|
id={ipChain} |
|
label="IP address" |
|
name={ipChain} |
|
onChange={handleChange} |
|
required |
|
value={formik.values.ip} |
|
/> |
|
} |
|
/> |
|
</Grid> |
|
<Grid item xs={1}> |
|
<UncontrolledInput |
|
input={ |
|
<OutlinedInputWithLabel |
|
disableAutofill |
|
id={passwordChain} |
|
label="Password" |
|
name={passwordChain} |
|
onChange={handleChange} |
|
required |
|
type="password" |
|
value={formik.values.password} |
|
/> |
|
} |
|
/> |
|
</Grid> |
|
<Grid item width="100%"> |
|
<MessageGroup count={1} messages={formikErrors} ref={messageGroupRef} /> |
|
</Grid> |
|
<Grid item width="100%"> |
|
{loadingInquiry ? ( |
|
<Spinner mt={0} /> |
|
) : ( |
|
<ActionGroup |
|
actions={[ |
|
...moreActions, |
|
{ |
|
background: 'blue', |
|
children: 'Test access', |
|
disabled: disabledSubmit, |
|
type: 'submit', |
|
}, |
|
]} |
|
/> |
|
)} |
|
</Grid> |
|
</Grid> |
|
); |
|
}; |
|
|
|
export default TestAccessForm;
|
|
|