diff --git a/apps/demo/src/migration-wizard/steps/connect/sources-table/empty-state/DiscoverySourceSetupModal.tsx b/apps/demo/src/migration-wizard/steps/connect/sources-table/empty-state/DiscoverySourceSetupModal.tsx index 244fec0..96fc728 100644 --- a/apps/demo/src/migration-wizard/steps/connect/sources-table/empty-state/DiscoverySourceSetupModal.tsx +++ b/apps/demo/src/migration-wizard/steps/connect/sources-table/empty-state/DiscoverySourceSetupModal.tsx @@ -15,32 +15,6 @@ import { ModalFooter, ModalHeader, } from "@patternfly/react-core/next"; -import * as Yup from 'yup'; - -const SSH_PUBLIC_KEY_REGEX = - /^(ssh-rsa AAAAB3NzaC1yc2|ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT|ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD|ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|ssh-dss AAAAB3NzaC1kc3)[0-9A-Za-z+/]+[=]{0,3}( .*)?$/; - -// eslint-disable-next-line react-refresh/only-export-components, @typescript-eslint/explicit-function-return-type -export const trimSshPublicKey = (key: string) => - key - .split('\n') - .map((row) => row.trim()) - .filter(Boolean) - .join('\n'); - -// Define your validation schema -// eslint-disable-next-line react-refresh/only-export-components -export const sshPublicKeyValidationSchema = Yup.string().test( - 'ssh-public-key', - 'SSH public key must consist of "[TYPE] key [[EMAIL]]", supported types are: ssh-rsa, ssh-ed25519, ecdsa-[VARIANT]. A single key can be provided only.', - (value?: string) => { - if (!value) { - return true; - } - - return !!trimSshPublicKey(value).match(SSH_PUBLIC_KEY_REGEX); - }, -); // eslint-disable-next-line @typescript-eslint/no-namespace export namespace DiscoverySourceSetupModal { @@ -52,38 +26,51 @@ export namespace DiscoverySourceSetupModal { }; } -// Your component export const DiscoverySourceSetupModal: React.FC< DiscoverySourceSetupModal.Props > = (props) => { const { isOpen = false, isDisabled = false, onClose, onSubmit } = props; - const [sshKey, setSshKey] = useState(""); - const [isSshKeyValid, setIsSshKeyValid] = useState(undefined); - const [sshKeyErrorMessage, setSshKeyErrorMessage] = useState(""); + const [sshKeyError, setSshKeyError] = useState(null); + + const validateSshKey = useCallback((key: string): string | null => { + // SSH key validation regex patterns + const SSH_KEY_PATTERNS = { + RSA: /^ssh-rsa\s+[A-Za-z0-9+/]+[=]{0,2}(\s+.*)?$/, + ED25519: /^ssh-ed25519\s+[A-Za-z0-9+/]+[=]{0,2}(\s+.*)?$/, + ECDSA: /^ssh-(ecdsa|sk-ecdsa)-sha2-nistp[0-9]+\s+[A-Za-z0-9+/]+[=]{0,2}(\s+.*)?$/, + }; + + // Optional field, so empty is valid + if (!key) return null; + + // Check if the key matches any of the known SSH key formats + const isValidKey = Object.values(SSH_KEY_PATTERNS).some(pattern => pattern.test(key.trim())); - // Validate SSH key when it changes - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const handleSshKeyChange = async (event: React.ChangeEvent) => { - const value = event.target.value; + return isValidKey ? null : "Invalid SSH key format. Please provide a valid SSH public key."; + }, []); + + const handleSshKeyChange = (value: string): void => { setSshKey(value); - const isValid = await sshPublicKeyValidationSchema.isValid(value); - setIsSshKeyValid(isValid); - if (!isValid) { - setSshKeyErrorMessage("SSH key is not valid."); - } else { - setSshKeyErrorMessage(""); - } + setSshKeyError(validateSshKey(value)); }; const handleSubmit = useCallback>( (event) => { event.preventDefault(); + + // Validate SSH key before submission + const keyValidationError = validateSshKey(sshKey); + if (keyValidationError) { + setSshKeyError(keyValidationError); + return; + } + if (onSubmit) { onSubmit(event); } }, - [onSubmit] + [onSubmit, sshKey, validateSshKey] ); return ( @@ -132,33 +119,22 @@ export const DiscoverySourceSetupModal: React.FC<