From 0c44e76479d786507f2da3fab151a81a090e8a71 Mon Sep 17 00:00:00 2001 From: Sharon Gratch Date: Tue, 21 Nov 2023 16:52:05 +0200 Subject: [PATCH] Enhabce oVirt provider create/edit credentials help text fields Reference: https://github.com/kubev2v/forklift-console-plugin/issues/646 Reference: https://github.com/kubev2v/forklift-console-plugin/issues/541 Rephrase all oVirt fields help text messages and add hints for the following dialogs: 1. Create oVirt provider, 2. oVirt Credentials view/edit modes. The main changes include: 1. Rephrase all help text messages. 2. Support 'warning' validation state for the 'user' field. 3. Add hint sections for insecureSkipVerify, cacert fields. 4. In Create/edit dialogs, hide the 'cacert' field help text message and the hint section if insecureSkipVerify=true. This fix assumes that leaving the 'cacert' field empty, will trigger verifying the the system CA certificates. Signed-off-by: Sharon Gratch --- .../en/plugin__forklift-console-plugin.json | 18 ++- .../Providers/utils/validators/common.ts | 6 + .../ovirtSecretFieldValidator.ts | 11 +- .../BaseCredentialsSection.style.css | 5 + .../components/edit/OvirtCredentialsEdit.tsx | 112 ++++++++++++++++-- .../components/list/OvirtCredentialsList.tsx | 36 +++++- 6 files changed, 163 insertions(+), 25 deletions(-) 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 fbbe58f2c..d49593bfb 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 @@ -7,11 +7,15 @@ "{{name}} Details": "{{name}} Details", "{{selectedLength}} hosts selected.": "{{selectedLength}} hosts selected.", "{{vmDone}} of {{vmCount}} VMs migrated": "{{vmDone}} of {{vmCount}} VMs migrated", + "{cacertHelperTextMsgs.hint}<1>{cacertHelperTextMsgs.hintHyperReference}.": "{cacertHelperTextMsgs.hint}<1>{cacertHelperTextMsgs.hintHyperReference}.", "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 custom 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 custom 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 +32,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", @@ -63,7 +69,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.", @@ -110,9 +115,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.", @@ -140,6 +149,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", @@ -223,6 +233,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: enter the Manager CA certificate. unless it was replaced by a third-party certificate, in which case enter the Manager Apache CA certificate. You can retrieve the Manager CA certificate at: ": "Note: enter the Manager CA certificate. unless it was replaced by a third-party certificate, in which case enter the Manager Apache CA certificate. You can retrieve the Manager CA certificate at: ", + "Note: if 'Skip certificate validation' 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 'Skip certificate validation' 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.", "Number of cluster in provider": "Number of cluster in provider", "Number of data centers in provider": "Number of data centers in provider", "Number of data stores in provider": "Number of data stores in provider", @@ -298,8 +310,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", @@ -345,7 +355,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.", @@ -416,6 +425,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..70162349e 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 @@ -8,6 +8,11 @@ padding-bottom: var(--pf-global--spacer--xm); } +.forklift-page-secret-hint-div { + padding-top: var(--pf-global--spacer--md); + padding-bottom: var(--pf-global--spacer--xm); +} + .forklift-page-secret-title { margin-top: var(--pf-global--spacer--sm); margin-bottom: 0; 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..5c4e6d29f 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,6 +16,8 @@ import { FileUpload, Form, FormGroup, + Hint, + HintBody, TextInput, } from '@patternfly/react-core'; import EyeIcon from '@patternfly/react-icons/dist/esm/icons/eye-icon'; @@ -29,12 +33,58 @@ 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."), + hint: t( + "Note: if 'Skip certificate validation' 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 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 custom CA certificate, drag the file to the text box or browse for it. To use the system CA certificates, leave the field empty.', + ), + hint: t( + 'Note: enter the Manager CA certificate. unless it was replaced by a third-party certificate, in which case enter the Manager Apache CA certificate. You can retrieve the Manager CA certificate at: ', + ), + hintHyperReference: + '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 +111,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 +151,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 = ({ secret, onC onChange={(value) => handleChange('insecureSkipVerify', value ? 'true' : 'false')} /> + + {insecureSkipVerifyHelperTextMsgs.hint} + = ({ secret, onC isDisabled={insecureSkipVerify} /> + {insecureSkipVerify ? ( + '' + ) : ( + + + + {cacertHelperTextMsgs.hint} + + {cacertHelperTextMsgs.hintHyperReference} + + . + + + + )} ); }; diff --git a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/list/OvirtCredentialsList.tsx b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/list/OvirtCredentialsList.tsx index 99aa6e057..f79f50f0b 100644 --- a/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/list/OvirtCredentialsList.tsx +++ b/packages/forklift-console-plugin/src/modules/Providers/views/details/components/CredentialsSection/components/list/OvirtCredentialsList.tsx @@ -2,7 +2,14 @@ import React from 'react'; import { Base64 } from 'js-base64'; import { useForkliftTranslation } from 'src/utils/i18n'; -import { ClipboardCopy, ClipboardCopyVariant, Text, TextVariants } from '@patternfly/react-core'; +import { + ClipboardCopy, + ClipboardCopyVariant, + Hint, + HintBody, + Text, + TextVariants, +} from '@patternfly/react-core'; import { MaskedData } from '../../MaskedData'; import { ListComponentProps } from '../BaseCredentialsSection'; @@ -13,19 +20,31 @@ export const OvirtCredentialsList: React.FC = ({ 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.", + ), + hint: t( + "Note: if 'Skip certificate validation' 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 custom CA certificate, drag the file to the text box or browse for it. To use the system CA certificates, leave the field empty.', ), }, }; @@ -60,6 +79,13 @@ export const OvirtCredentialsList: React.FC = ({ secret, rev )} +
+ {key === 'insecureSkipVerify' ? ( + + {field.hint} + + ) : null} +
, ); }