Skip to content

Commit 0e9e35a

Browse files
committed
[MTV-1794] Update provider vCenter validation
Signed-off-by: Jeff Puzzo <[email protected]>
1 parent d8a29b0 commit 0e9e35a

File tree

9 files changed

+57
-52
lines changed

9 files changed

+57
-52
lines changed

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)