Skip to content

Commit 2d79ffe

Browse files
authored
Merge pull request #1523 from jpuzz0/MTV-1794-update-vcenter-validation
[MTV-1794] Update provider vCenter validation
2 parents 83549eb + d0d9e67 commit 2d79ffe

File tree

10 files changed

+58
-53
lines changed

10 files changed

+58
-53
lines changed

packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@
588588
"The target namespace is the namespace within your selected target provider that your virtual machines will be migrated to. This is different from the namespace that your migration plan will be created in and where your provider was created.": "The target namespace is the namespace within your selected target provider that your virtual machines will be migrated to. This is different from the namespace that your migration plan will be created in and where your provider was created.",
589589
"The URL of the OpenStack Identity (Keystone) endpoint, for example: https://identity_service.com:5000/v3": "The URL of the OpenStack Identity (Keystone) endpoint, for example: https://identity_service.com:5000/v3",
590590
"The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api .": "The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api .",
591-
"The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk .": "The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk .",
591+
"The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk.": "The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk.",
592592
"The username for the ESXi host admin": "The username for the ESXi host admin",
593593
"The virtual machines will be permanently deleted from your migration plan.": "The virtual machines will be permanently deleted from your migration plan.",
594594
"The VM specific template will override the template set in the plan.": "The VM specific template will override the template set in the plan.",

packages/forklift-console-plugin/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"jsonpath": "^1.1.1",
4242
"jsrsasign": "11.1.0",
4343
"luxon": "^3.5.0",
44-
"node-forge": "^1",
4544
"react": "17.0.2",
4645
"react-dom": "17.0.2",
4746
"react-i18next": "^11.14.3",

packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/EditProviderURLModal.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type EditProviderURLModalProps = Modify<
1818
label?: string;
1919
model?: K8sModel;
2020
jsonPath?: string | string[];
21+
insecureSkipVerify?: string;
2122
}
2223
>;
2324

packages/forklift-console-plugin/src/modules/Providers/modals/EditProviderURL/VSphereEditURLModal.tsx

+12-7
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,22 @@ import { EditModal, ValidationHookType } from '../EditModal';
1010
import { patchProviderURL } from './utils/patchProviderURL';
1111
import { EditProviderURLModalProps } from './EditProviderURLModal';
1212

13-
export const VSphereEditURLModal: React.FC<EditProviderURLModalProps> = (props) => {
13+
export const VSphereEditURLModal: React.FC<EditProviderURLModalProps> = ({
14+
title,
15+
label,
16+
resource: provider,
17+
insecureSkipVerify,
18+
...props
19+
}) => {
1420
const { t } = useForkliftTranslation();
15-
const provider = props.resource;
16-
1721
let validationHook: ValidationHookType;
1822

1923
// VCenter of ESXi
2024
const sdkEndpoint = provider?.spec?.settings?.['sdkEndpoint'] || '';
2125
if (sdkEndpoint === 'esxi') {
2226
validationHook = validateEsxiURL;
2327
} else {
24-
validationHook = validateVCenterURL;
28+
validationHook = (url) => validateVCenterURL(url, insecureSkipVerify);
2529
}
2630

2731
const ModalBody = (
@@ -39,14 +43,15 @@ export const VSphereEditURLModal: React.FC<EditProviderURLModalProps> = (props)
3943
return (
4044
<EditModal
4145
{...props}
46+
resource={provider}
4247
jsonPath={'spec.url'}
43-
title={props?.title || t('Edit URL')}
44-
label={props?.label || t('URL')}
48+
title={title || t('Edit URL')}
49+
label={label || t('URL')}
4550
model={ProviderModel}
4651
variant={ModalVariant.large}
4752
body={ModalBody}
4853
helperText={t(
49-
'The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk .',
54+
'The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk.',
5055
)}
5156
onConfirmHook={patchProviderURL}
5257
validationHook={validationHook}

packages/forklift-console-plugin/src/modules/Providers/utils/validators/provider/providerValidator.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function providerValidator(
2727
validationError = ovirtProviderValidator(provider);
2828
break;
2929
case 'vsphere':
30-
validationError = vsphereProviderValidator(provider, subType, secret?.data?.cacert);
30+
validationError = vsphereProviderValidator(provider, subType, secret);
3131
break;
3232
case 'ova':
3333
validationError = ovaProviderValidator(provider);

packages/forklift-console-plugin/src/modules/Providers/utils/validators/provider/vsphere/validateVCenterURL.ts

+10-30
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,10 @@
1-
import { pki } from 'node-forge';
2-
31
import { safeBase64Decode } from '../../../helpers';
42
import { validateIpv4, validateURL, ValidationMsg } from '../../common';
53

6-
export const urlMatchesCertFqdn = (urlHostname: string, caCert: string): boolean => {
7-
try {
8-
const decodedCaCert = safeBase64Decode(caCert);
9-
const cert = pki.certificateFromPem(decodedCaCert);
10-
const dnsAltName = cert.extensions
11-
.find((ext) => ext.name === 'subjectAltName')
12-
?.altNames.find((altName) => altName.type === 2)?.value;
13-
const commonName = cert.subject.attributes.find((attr) => attr.name === 'commonName')?.value;
14-
15-
return urlHostname === (dnsAltName || commonName);
16-
} catch (e) {
17-
console.error('Unable to parse certificate object from PEM.');
18-
}
19-
20-
return false;
21-
};
22-
23-
export const validateVCenterURL = (url: string, caCert?: string): ValidationMsg => {
4+
export const validateVCenterURL = (
5+
url: string | number,
6+
insecureSkipVerify?: string,
7+
): ValidationMsg => {
248
// For a newly opened form where the field is not set yet, set the validation type to default.
259
if (url === undefined) {
2610
return {
@@ -38,6 +22,7 @@ export const validateVCenterURL = (url: string, caCert?: string): ValidationMsg
3822
const isValidURL = validateURL(trimmedUrl);
3923
const urlObject = getUrlObject(url);
4024
const urlHostname = urlObject?.hostname;
25+
const isSecure = !insecureSkipVerify || safeBase64Decode(insecureSkipVerify) === 'false';
4126

4227
if (trimmedUrl === '') {
4328
return {
@@ -53,18 +38,13 @@ export const validateVCenterURL = (url: string, caCert?: string): ValidationMsg
5338
};
5439
}
5540

56-
if (urlObject?.protocol === 'https:') {
57-
if (validateIpv4(urlHostname)) {
58-
return {
59-
type: 'error',
60-
msg: 'Invalid URL. The URL must be a fully qualified domain name (FQDN).',
61-
};
62-
}
41+
if (isSecure) {
42+
const isValidIpAddress = validateIpv4(urlHostname);
6343

64-
if (caCert && !urlMatchesCertFqdn(urlHostname, caCert)) {
44+
if (isValidIpAddress) {
6545
return {
66-
type: 'error',
67-
msg: 'Invalid URL. The URL must be a fully qualified domain name (FQDN) and match the FQDN in the certificate you uploaded.',
46+
type: 'warning',
47+
msg: 'The URL is not a fully qualified domain name (FQDN). If the certificate is not skipped and does not match the URL, the connection might fail.',
6848
};
6949
}
7050
}

packages/forklift-console-plugin/src/modules/Providers/utils/validators/provider/vsphere/vsphereProviderValidator.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { V1beta1Provider } from '@kubev2v/types';
2+
import { IoK8sApiCoreV1Secret } from '@kubev2v/types';
23

34
import { validateK8sName, validateURL, ValidationMsg } from '../../common';
45
import { SecretSubType } from '../secretValidator';
@@ -9,7 +10,7 @@ import { validateVDDKImage } from './validateVDDKImage';
910
export function vsphereProviderValidator(
1011
provider: V1beta1Provider,
1112
subType?: SecretSubType,
12-
caCert?: string,
13+
secret?: IoK8sApiCoreV1Secret,
1314
): ValidationMsg {
1415
const name = provider?.metadata?.name;
1516
const url = provider?.spec?.url || '';
@@ -23,8 +24,8 @@ export function vsphereProviderValidator(
2324
}
2425

2526
if (
26-
subType === 'vcenter' && caCert
27-
? validateVCenterURL(url, caCert).type === 'error'
27+
subType === 'vcenter'
28+
? validateVCenterURL(url, secret?.data?.insecureSkipVerify).type === 'error'
2829
: !validateURL(url)
2930
) {
3031
return { type: 'error', msg: 'invalid URL' };

packages/forklift-console-plugin/src/modules/Providers/views/create/components/EditProvider.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const EditProvider: React.FC<ProvidersCreateFormProps> = ({
7373
<>
7474
<VCenterProviderCreateForm
7575
provider={newProvider}
76-
caCert={newSecret.data.cacert}
76+
secret={newSecret}
7777
onChange={onNewProviderChange}
7878
/>
7979

packages/forklift-console-plugin/src/modules/Providers/views/create/components/VCenterProviderCreateForm.tsx

+11-8
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import {
88
import { useForkliftTranslation } from 'src/utils/i18n';
99

1010
import { FormGroupWithHelpText } from '@kubev2v/common';
11-
import { V1beta1Provider } from '@kubev2v/types';
11+
import { IoK8sApiCoreV1Secret, V1beta1Provider } from '@kubev2v/types';
1212
import { Alert, Checkbox, Form, Popover, Radio, TextInput } from '@patternfly/react-core';
1313
import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
1414

1515
export interface VCenterProviderCreateFormProps {
1616
provider: V1beta1Provider;
17-
caCert: string;
17+
secret: IoK8sApiCoreV1Secret;
1818
onChange: (newValue: V1beta1Provider) => void;
1919
}
2020

2121
export const VCenterProviderCreateForm: React.FC<VCenterProviderCreateFormProps> = ({
2222
provider,
23-
caCert,
23+
secret,
2424
onChange,
2525
}) => {
2626
const { t } = useForkliftTranslation();
@@ -33,7 +33,7 @@ export const VCenterProviderCreateForm: React.FC<VCenterProviderCreateFormProps>
3333

3434
const initialState = {
3535
validation: {
36-
url: validateVCenterURL(url, caCert),
36+
url: validateVCenterURL(url, secret?.data?.insecureSkipVerify),
3737
vddkInitImage: validateVDDKImage(vddkInitImage),
3838
},
3939
};
@@ -42,9 +42,12 @@ export const VCenterProviderCreateForm: React.FC<VCenterProviderCreateFormProps>
4242
useEffect(() => {
4343
dispatch({
4444
type: 'SET_FIELD_VALIDATED',
45-
payload: { field: 'url', validationState: validateVCenterURL(url, caCert) },
45+
payload: {
46+
field: 'url',
47+
validationState: validateVCenterURL(url, secret?.data?.insecureSkipVerify),
48+
},
4649
});
47-
}, [caCert]);
50+
}, [secret]);
4851

4952
const reducer = (state, action) => {
5053
switch (action.type) {
@@ -127,14 +130,14 @@ export const VCenterProviderCreateForm: React.FC<VCenterProviderCreateFormProps>
127130

128131
if (id === 'url') {
129132
// Validate URL - VCenter of ESXi
130-
const validationState = validateVCenterURL(trimmedValue, caCert);
133+
const validationState = validateVCenterURL(trimmedValue, secret?.data?.insecureSkipVerify);
131134

132135
dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: 'url', validationState } });
133136

134137
onChange({ ...provider, spec: { ...provider.spec, url: trimmedValue } });
135138
}
136139
},
137-
[provider, caCert],
140+
[provider, secret],
138141
);
139142

140143
const onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void = (event) => {

packages/forklift-console-plugin/src/modules/Providers/views/details/components/DetailsSection/components/URLDetailsItem.tsx

+17-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import React from 'react';
22
import { EditProviderURLModal, useModal } from 'src/modules/Providers/modals';
33
import { useForkliftTranslation } from 'src/utils/i18n';
44

5+
import { IoK8sApiCoreV1Secret } from '@kubev2v/types';
6+
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
7+
58
import { DetailsItem } from '../../../../../utils';
69

710
import { ProviderDetailsItemProps } from './ProviderDetailsItem';
@@ -15,6 +18,13 @@ export const URLDetailsItem: React.FC<ProviderDetailsItemProps> = ({
1518
const { t } = useForkliftTranslation();
1619
const { showModal } = useModal();
1720

21+
const [secret] = useK8sWatchResource<IoK8sApiCoreV1Secret>({
22+
groupVersionKind: { version: 'v1', kind: 'Secret' },
23+
namespaced: true,
24+
namespace: provider?.spec?.secret?.namespace,
25+
name: provider?.spec?.secret?.name,
26+
});
27+
1828
const defaultMoreInfoLink =
1929
'https://docs.redhat.com/en/documentation/migration_toolkit_for_virtualization/2.7/html-single/installing_and_using_the_migration_toolkit_for_virtualization/index#adding-source-providers';
2030
const defaultHelpContent =
@@ -31,7 +41,13 @@ export const URLDetailsItem: React.FC<ProviderDetailsItemProps> = ({
3141
onEdit={
3242
canPatch &&
3343
provider?.spec?.url &&
34-
(() => showModal(<EditProviderURLModal resource={provider} />))
44+
(() =>
45+
showModal(
46+
<EditProviderURLModal
47+
resource={provider}
48+
insecureSkipVerify={secret?.data?.insecureSkipVerify}
49+
/>,
50+
))
3551
}
3652
/>
3753
);

0 commit comments

Comments
 (0)