diff --git a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json index a66320a29..d2e632030 100644 --- a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json +++ b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json @@ -10,8 +10,11 @@ "24 hours": "24 hours", "404: Not Found": "404: Not Found", "7 days": "7 days", + "A CA certificate to be trusted when connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificates, leave the field empty.": "A CA certificate to be trusted when connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificates, leave the field empty.", "A Secret containing credentials and other confidential information.": "A Secret containing credentials and other confidential information.", "A Secret containing credentials and other confidential information. Empty may be used for the host provider.": "A Secret containing credentials and other confidential information. Empty may be used for the host provider.", + "A user name for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the user name is in the format of username@user-domain. For example: admin@internal.": "A user name for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the user name is in the format of username@user-domain. For example: admin@internal.", + "A user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.": "A user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.", "Actions": "Actions", "Add source and target providers for the migration.": "Add source and target providers for the migration.", "Application credential ID": "Application credential ID", @@ -28,7 +31,9 @@ "Authentication type": "Authentication type", "Bandwidth": "Bandwidth", "CA certificate": "CA certificate", + "CA certificate - disabled and ignored when 'Skip certificate validation' is checked": "CA certificate - disabled and ignored when 'Skip certificate validation' is checked", "CA certificate - disabled when skip certificate validation is checked": "CA certificate - disabled when skip certificate validation is checked", + "CA certificate - leave empty to use system CA certificates": "CA certificate - leave empty to use system CA certificates", "CA certificate - leave empty to use system certificates": "CA certificate - leave empty to use system certificates", "Cancel": "Cancel", "Cancel scheduled cutover": "Cancel scheduled cutover", @@ -65,7 +70,6 @@ "Credentials": "Credentials", "Critical concerns": "Critical concerns", "Custom certification used to verify the OpenStack REST API server, when empty use system certificate.": "Custom certification used to verify the OpenStack REST API server, when empty use system certificate.", - "Custom certification used to verify the RH Virtualization REST API server, when empty use system certificate.": "Custom certification used to verify the RH Virtualization REST API server, when empty use system certificate.", "Data centers": "Data centers", "Data stores": "Data stores", "Dates are compared in UTC. End of the interval is included.": "Dates are compared in UTC. End of the interval is included.", @@ -113,9 +117,13 @@ "Error: Name is required and must be a unique within a namespace and valid Kubernetes name (i.e., must contain no more than 253 characters, consists of lower case alphanumeric characters , '-' or '.' and starts and ends with an alphanumeric character).": "Error: Name is required and must be a unique within a namespace and valid Kubernetes name (i.e., must contain no more than 253 characters, consists of lower case alphanumeric characters , '-' or '.' and starts and ends with an alphanumeric character).", "Error: NFS mount end point should be in the form NFS_SERVER:EXPORTED_DIRECTORY, for example: 10.10.0.10:/ova.": "Error: NFS mount end point should be in the form NFS_SERVER:EXPORTED_DIRECTORY, for example: 10.10.0.10:/ova.", "Error: Password is required and must be valid.": "Error: Password is required and must be valid.", + "Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.": "Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.", "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://rhv-host-example.com/ovirt-engine/api.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://rhv-host-example.com/ovirt-engine/api.", "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.", + "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.": "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.", + "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.": "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.", "Error: This field must be a boolean.": "Error: This field must be a boolean.", + "Error: this field must be set to a boolean value.": "Error: this field must be set to a boolean value.", "Error: token is a required field, the token must be a valid kubernetes token.": "Error: token is a required field, the token must be a valid kubernetes token.", "Error: URL is required and must be valid.": "Error: URL is required and must be valid.", "Error: URL must be valid.": "Error: URL must be valid.", @@ -146,6 +154,7 @@ "Host": "Host", "Host cluster": "Host cluster", "Hosts": "Hosts", + "If true (check box is checked), the provider's CA certificate won't be validated.": "If true (check box is checked), the provider's CA certificate won't be validated.", "If true, the provider's REST API TLS certificate won't be validated.": "If true, the provider's REST API TLS certificate won't be validated.", "If true, the provider's TLS certificate won't be validated.": "If true, the provider's TLS certificate won't be validated.", "Image": "Image", @@ -229,6 +238,8 @@ "No StorageMaps found in namespace <1>{namespace}.": "No StorageMaps found in namespace <1>{namespace}.", "No StorageMaps found.": "No StorageMaps found.", "Not Ready": "Not Ready", + "Note: If this field is checked/true, the migration from this provider will be insecure.

Insecure migration means that the transferred data is sent over an insecure connection and potentially sensitive data could be exposed.": "Note: If this field is checked/true, the migration from this provider will be insecure.

Insecure migration means that the transferred data is sent over an insecure connection and potentially sensitive data could be exposed.", + "Note: Use the Manager CA certificate unless it was replaced by a third-party certificate, in which case use the Manager Apache CA certificate.

You can retrieve the Manager CA certificate at:
<1>https://‹rhv-host-example.com›/ovirt-engine/services/pki-resource?resource=ca-certificate&format=X509-PEM-CA .": "Note: Use the Manager CA certificate unless it was replaced by a third-party certificate, in which case use the Manager Apache CA certificate.

You can retrieve the Manager CA certificate at:
<1>https://‹rhv-host-example.com›/ovirt-engine/services/pki-resource?resource=ca-certificate&format=X509-PEM-CA .", "NUMA": "NUMA", "Number of cluster in provider": "Number of cluster in provider", "Number of data centers in provider": "Number of data centers in provider", @@ -306,8 +317,6 @@ "Restore default columns": "Restore default columns", "Return to the providers list page": "Return to the providers list page", "Reveal values": "Reveal values", - "RH Virtualization engine REST API password credentials.": "RH Virtualization engine REST API password credentials.", - "RH Virtualization engine REST API user name.": "RH Virtualization engine REST API user name.", "Run the migration plan.": "Run the migration plan.", "Running - performing incremental data copies": "Running - performing incremental data copies", "Running - preparing for cutover": "Running - preparing for cutover", @@ -353,7 +362,6 @@ "Target provider": "Target provider", "Template": "Template", "Tenant": "Tenant", - "The CA certificate is the /etc/pki/ovirt-engine/apache-ca.pem file on the Manager machine.": "The CA certificate is the /etc/pki/ovirt-engine/apache-ca.pem file on the Manager machine.", "The interval in minutes for precopy. Default value is 60.": "The interval in minutes for precopy. Default value is 60.", "The interval in seconds for snapshot pooling. Default value is 10.": "The interval in seconds for snapshot pooling. Default value is 10.", "The limit for CPU usage by the controller, specified in milliCPU. Default value is 500m.": "The limit for CPU usage by the controller, specified in milliCPU. Default value is 500m.", @@ -424,6 +432,7 @@ "Warning concerns": "Warning concerns", "Warning: The provided URL does not end with the RHVM API endpoint path: \"/ovirt-engine/api\". Ensure the URL includes the correct path. For example: https://rhv-host-example.com/ovirt-engine/api.": "Warning: The provided URL does not end with the RHVM API endpoint path: \"/ovirt-engine/api\". Ensure the URL includes the correct path. For example: https://rhv-host-example.com/ovirt-engine/api.", "Warning: The provided URL does not end with the SDK endpoint path: \"/sdk\". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk.": "Warning: The provided URL does not end with the SDK endpoint path: \"/sdk\". Ensure the URL includes the correct path. For example: https://vCenter-host-example.com/sdk.", + "Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: admin@internal.": "Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: admin@internal.", "Welcome": "Welcome", "When a plan is archived, its history, metadata, and logs are deleted. The plan cannot be edited or restarted but it can be viewed.": "When a plan is archived, its history, metadata, and logs are deleted. The plan cannot be edited or restarted but it can be viewed.", "YAML": "YAML", diff --git a/packages/forklift-console-plugin/src/modules/Providers/utils/validators/common.ts b/packages/forklift-console-plugin/src/modules/Providers/utils/validators/common.ts index 13a0b57be..8e58a5dfa 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/utils/validators/common.ts +++ b/packages/forklift-console-plugin/src/modules/Providers/utils/validators/common.ts @@ -90,3 +90,9 @@ export function validateNoSpaces(value: string) { // any string without spaces return /^[^\s]+$/.test(value); } + +export function validateUsernameAndDomain(value: string) { + // any string without spaces that includes appearance of the at '@' sign + // example: user@domain + return /^[^\s]+(@+)+[^\s]+$/.test(value); +} diff --git a/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/ovirtSecretFieldValidator.ts b/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/ovirtSecretFieldValidator.ts index 4ec5efba5..3614a8856 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/ovirtSecretFieldValidator.ts +++ b/packages/forklift-console-plugin/src/modules/Providers/utils/validators/secret-fields/ovirtSecretFieldValidator.ts @@ -1,5 +1,5 @@ import { Validation } from '../../types'; -import { validateNoSpaces, validatePublicCert } from '../common'; +import { validateNoSpaces, validatePublicCert, validateUsernameAndDomain } from '../common'; /** * Validates form input fields based on their id. @@ -10,6 +10,7 @@ import { validateNoSpaces, validatePublicCert } from '../common'; * @return {Validation} - The validation state of the form field. Can be one of the following: * 'default' - The default state of the form field, used when the field is empty or a value hasn't been entered yet. * 'success' - The field's value has passed validation. + * 'warning' - The field's value might fail the validation, but it's not mandatory and not disabling the form saving. * 'error' - The field's value has failed validation. */ export const ovirtSecretFieldValidator = (id: string, value: string) => { @@ -19,7 +20,7 @@ export const ovirtSecretFieldValidator = (id: string, value: string) => { switch (id) { case 'user': - validationState = validateUser(trimmedValue) ? 'success' : 'error'; + validationState = validateUser(trimmedValue); break; case 'password': validationState = validatePassword(trimmedValue) ? 'success' : 'error'; @@ -39,7 +40,11 @@ export const ovirtSecretFieldValidator = (id: string, value: string) => { }; const validateUser = (value: string) => { - return validateNoSpaces(value); + return validateNoSpaces(value) + ? validateUsernameAndDomain(value) + ? 'success' + : 'warning' + : 'error'; }; const validatePassword = (value: string) => { diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/BaseCredentialsSection.style.css b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/BaseCredentialsSection.style.css index 2bc9f9426..20017bef9 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/BaseCredentialsSection.style.css +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/BaseCredentialsSection.style.css @@ -1,5 +1,5 @@ .forklift-page-secret-title-div { - padding-top: var(--pf-global--spacer--sm); + padding-top: var(--pf-global--spacer--xl); padding-bottom: var(--pf-global--spacer--xm); } diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/edit/OvirtCredentialsEdit.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/edit/OvirtCredentialsEdit.tsx index 40537ee88..d4be27737 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/edit/OvirtCredentialsEdit.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/edit/OvirtCredentialsEdit.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useReducer } from 'react'; +import { Trans } from 'react-i18next'; import { Base64 } from 'js-base64'; import { ovirtSecretFieldValidator, @@ -7,6 +8,7 @@ import { } from 'src/modules/Providers/utils'; import { useForkliftTranslation } from 'src/utils/i18n'; +import { ExternalLink } from '@kubev2v/common'; import { Button, Checkbox, @@ -14,10 +16,12 @@ import { FileUpload, Form, FormGroup, + Popover, TextInput, } from '@patternfly/react-core'; import EyeIcon from '@patternfly/react-icons/dist/esm/icons/eye-icon'; import EyeSlashIcon from '@patternfly/react-icons/dist/esm/icons/eye-slash-icon'; +import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon'; import { EditComponentProps } from '../BaseCredentialsSection'; @@ -29,12 +33,74 @@ export const OvirtCredentialsEdit: React.FC = ({ secret, onC const insecureSkipVerify = safeBase64Decode(secret?.data?.insecureSkipVerify || '') === 'true'; const cacert = safeBase64Decode(secret?.data?.cacert || ''); + const usernameHelperTextMsgs = { + error: t( + "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.", + ), + warning: t( + 'Warning: The provided user name does not include the user domain. Ensure the user name is in the format of username@user-domain. For example: admin@internal.', + ), + success: t( + 'A user name for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the user name is in the format of username@user-domain. For example: admin@internal.', + ), + }; + + const passwordHelperTextMsgs = { + error: t( + "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.", + ), + success: t( + 'A user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.', + ), + }; + + const insecureSkipVerifyHelperTextMsgs = { + error: t('Error: this field must be set to a boolean value.'), + success: t("If true (check box is checked), the provider's CA certificate won't be validated."), + }; + + const cacertHelperTextMsgs = { + error: t( + 'Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.', + ), + success: t( + 'A CA certificate to be trusted when connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificates, leave the field empty.', + ), + }; + + const insecureSkipVerifyHelperTextPopover = ( + + { + 'Note: If this field is checked/true, the migration from this provider will be insecure.

Insecure migration means that the transferred data is sent over an insecure connection and potentially sensitive data could be exposed.' + } +
+ ); + + const cacertHelperTextPopover = ( + + { + 'Note: Use the Manager CA certificate unless it was replaced by a third-party certificate, in which case use the Manager Apache CA certificate.

You can retrieve the Manager CA certificate at:
' + } + + https://‹rhv-host-example.com›/ovirt-engine/services/pki-resource?resource=ca-certificate&format=X509-PEM-CA + + {' .'} +
+ ); + const initialState = { passwordHidden: true, validation: { user: 'default' as Validation, password: 'default' as Validation, + insecureSkipVerify: 'default' as Validation, cacert: 'default' as Validation, + // The 'warning' validation state is currently supported only for the 'user' field. + userHelperText: usernameHelperTextMsgs.success, }, }; @@ -61,7 +127,25 @@ export const OvirtCredentialsEdit: React.FC = ({ secret, onC const handleChange = useCallback( (id, value) => { const validationState = ovirtSecretFieldValidator(id, value); - dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: id, validationState } }); + dispatch({ + type: 'SET_FIELD_VALIDATED', + payload: { field: id, validationState }, + }); + + // The 'warning' validation state is currently supported only for the 'user' field. + switch (id) { + case 'user': + dispatch({ + type: 'SET_FIELD_VALIDATED', + payload: { + field: 'userHelperText', + validationState: usernameHelperTextMsgs[validationState], + }, + }); + break; + default: + break; + } // don't trim fields that allow spaces const encodedValue = ['cacert'].includes(id) @@ -83,9 +167,9 @@ export const OvirtCredentialsEdit: React.FC = ({ secret, onC label={t('Username')} isRequired fieldId="user" - helperText={t('RH Virtualization engine REST API user name.')} + helperText={state.validation.userHelperText} validated={state.validation.user} - helperTextInvalid={t('Error: Username is required and must be valid.')} + helperTextInvalid={state.validation.userHelperText} > = ({ secret, onC label={t('Password')} isRequired fieldId="password" - helperText={t('RH Virtualization engine REST API password credentials.')} + helperText={passwordHelperTextMsgs.success} validated={state.validation.password} - helperTextInvalid={t('Error: Password is required and must be valid.')} + helperTextInvalid={passwordHelperTextMsgs.error} > = ({ secret, onC Skip certificate validation} + bodyContent={
{insecureSkipVerifyHelperTextPopover}
} + alertSeverityVariant="info" + > + + + } fieldId="insecureSkipVerify" - helperText={t("If true, the provider's REST API TLS certificate won't be validated.")} + helperText={insecureSkipVerifyHelperTextMsgs.success} validated={state.validation.insecureSkipVerify} - helperTextInvalid={t('Error: Insecure Skip Verify must be a boolean value.')} + helperTextInvalid={insecureSkipVerifyHelperTextMsgs.error} > = ({ secret, onC CA certificate} + bodyContent={
{cacertHelperTextPopover}
} + alertSeverityVariant="info" + > + + } fieldId="cacert" - helperText={t( - 'Custom certification used to verify the RH Virtualization REST API server, when empty use system certificate.', - )} + helperText={cacertHelperTextMsgs.success} validated={state.validation.cacert} - helperTextInvalid={t('Error: CA Certificate must be valid.')} + helperTextInvalid={cacertHelperTextMsgs.error} > = ({ secret, rev const items = []; const fields = { - user: { label: t('Username'), description: t('RH Virtualization engine REST API user name.') }, + user: { + label: t('Username'), + description: t( + 'A user name for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the user name is in the format of username@user-domain. For example: admin@internal.', + ), + }, password: { label: t('Password'), - description: t('RH Virtualization engine REST API password credentials.'), + description: t( + 'A user password for connecting to the Red Hat Virtualization Manager (RHVM) API endpoint.', + ), }, insecureSkipVerify: { label: t('Skip certificate validation'), - description: t("If true, the provider's REST API TLS certificate won't be validated."), + description: t( + "If true (check box is checked), the provider's CA certificate won't be validated.", + ), + helperTextPopover: ( + + { + 'Note: If this field is checked/true, the migration from this provider will be insecure.

Insecure migration means that the transferred data is sent over an insecure connection and potentially sensitive data could be exposed.' + } +
+ ), }, cacert: { label: t('CA certificate'), description: t( - 'The CA certificate is the /etc/pki/ovirt-engine/apache-ca.pem file on the Manager machine.', + 'A CA certificate to be trusted when connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificates, leave the field empty.', + ), + helperTextPopover: ( + + { + 'Note: Use the Manager CA certificate unless it was replaced by a third-party certificate, in which case use the Manager Apache CA certificate.

You can retrieve the Manager CA certificate at:
' + } + + https://‹rhv-host-example.com›/ovirt-engine/services/pki-resource?resource=ca-certificate&format=X509-PEM-CA + + {' .'} +
), }, }; @@ -38,9 +78,18 @@ export const OvirtCredentialsList: React.FC = ({ secret, rev items.push( <>
- - {field.label} - + + {field?.helperTextPopover} + ) : null + } + showHelpIconNextToTitle={true} + content={''} + /> + {field.description}