diff --git a/src/components/create-vm-dialog/createVmDialog.jsx b/src/components/create-vm-dialog/createVmDialog.jsx index d1e9dfed1..ca493a4a1 100644 --- a/src/components/create-vm-dialog/createVmDialog.jsx +++ b/src/components/create-vm-dialog/createVmDialog.jsx @@ -24,6 +24,7 @@ import { Divider } from "@patternfly/react-core/dist/esm/components/Divider"; import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex"; import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form"; import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect"; +import { Grid, GridItem } from "@patternfly/react-core/dist/esm/layouts/Grid"; import { InputGroup } from "@patternfly/react-core/dist/esm/components/InputGroup"; import { Modal } from "@patternfly/react-core/dist/esm/components/Modal"; import { Select as PFSelect, SelectGroup, SelectOption } from "@patternfly/react-core/dist/esm/deprecated/components/Select"; @@ -33,7 +34,7 @@ import { Button } from "@patternfly/react-core/dist/esm/components/Button"; import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip"; import { TextArea } from "@patternfly/react-core/dist/esm/components/TextArea"; import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner"; -import { ExternalLinkAltIcon } from '@patternfly/react-icons'; +import { ExternalLinkAltIcon, TrashIcon } from '@patternfly/react-icons'; import { DialogsContext } from 'dialogs.jsx'; import cockpit from 'cockpit'; @@ -84,6 +85,7 @@ import { domainCreate } from '../../libvirtApi/domain.js'; import { storagePoolRefresh } from '../../libvirtApi/storagePool.js'; import { getAccessToken } from '../../libvirtApi/rhel-images.js'; import { PasswordFormFields, password_quality } from 'cockpit-components-password.jsx'; +import { DynamicListForm } from 'DynamicListForm.jsx'; import './createVmDialog.scss'; @@ -248,6 +250,8 @@ function validateParams(vmParams) { } if (vmParams.userPassword && !vmParams.userLogin) { validationFailed.userLogin = _("User login must not be empty when user password is set"); + } else if (vmParams.sshKeys.length > 0 && !vmParams.userLogin) { + validationFailed.userLogin = _("User login must not be empty when SSH keys are set"); } return validationFailed; @@ -748,6 +752,65 @@ const UsersConfigurationRow = ({ ); }; +// This method needs to be outside of component as re-render would create a new instance of debounce +const validateKey = debounce(500, (key, setKeyObject) => { + if (isEmpty(key)) + return + + return cockpit.script(`echo ${key} | ssh-keygen -l -f /dev/stdin`, {}) + .then(() => { + const parts = key.split(" "); + if (parts.length > 2) { + setKeyObject({ + type: parts[0], + data: parts[1], + comment: parts[2], // comment is optional in SSH-format + }); + } + }) + .catch(() => setKeyObject({ warning: "Could not validate a public key" })); +}); + +const SshKeysRow = ({ + id, item, onChange, idx, removeitem, +}) => { + const [keyObject, setKeyObject] = useState(); + + return ( + + + + {keyObject?.data + ? + {keyObject.comment} + {keyObject.comment ? " - " + keyObject.type : keyObject.type} +
{keyObject.data}
+
+ :