Merge pull request #9884 from nixocio/ui_issue_9769

Do not allow EE to modify Org

Do not allow EE to modify Org.
See: #9769
Also: #9874

Reviewed-by: Jake McDermott <yo@jakemcdermott.me>
Reviewed-by: Tiago Góes <tiago.goes2009@gmail.com>
This commit is contained in:
softwarefactory-project-zuul[bot]
2021-04-15 21:59:19 +00:00
committed by GitHub
3 changed files with 136 additions and 19 deletions

View File

@@ -37,6 +37,7 @@ function ExecutionEnvironmentEdit({ executionEnvironment }) {
submitError={submitError} submitError={submitError}
onCancel={handleCancel} onCancel={handleCancel}
me={me || {}} me={me || {}}
isOrgLookupDisabled
/> />
)} )}
</Config> </Config>

View File

@@ -1,9 +1,9 @@
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect, useRef } from 'react';
import { func, shape } from 'prop-types'; import { func, shape, bool } from 'prop-types';
import { Formik, useField, useFormikContext } from 'formik'; import { Formik, useField, useFormikContext } from 'formik';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { Form, FormGroup } from '@patternfly/react-core'; import { Form, FormGroup, Tooltip } from '@patternfly/react-core';
import { ExecutionEnvironmentsAPI } from '../../../api'; import { ExecutionEnvironmentsAPI } from '../../../api';
import CredentialLookup from '../../../components/Lookup/CredentialLookup'; import CredentialLookup from '../../../components/Lookup/CredentialLookup';
@@ -22,6 +22,7 @@ function ExecutionEnvironmentFormFields({
me, me,
options, options,
executionEnvironment, executionEnvironment,
isOrgLookupDisabled,
}) { }) {
const [credentialField, credentialMeta, credentialHelpers] = useField( const [credentialField, credentialMeta, credentialHelpers] = useField(
'credential' 'credential'
@@ -33,6 +34,8 @@ function ExecutionEnvironmentFormFields({
required(i18n._(t`Select a value for this field`), i18n), required(i18n._(t`Select a value for this field`), i18n),
}); });
const isGloballyAvailable = useRef(!organizationField.value);
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
const onCredentialChange = useCallback( const onCredentialChange = useCallback(
@@ -61,6 +64,30 @@ function ExecutionEnvironmentFormFields({
([value, label]) => ({ value, label, key: value }) ([value, label]) => ({ value, label, key: value })
); );
const renderOrganizationLookup = () => {
return (
<OrganizationLookup
helperTextInvalid={organizationMeta.error}
isValid={!organizationMeta.touched || !organizationMeta.error}
onBlur={() => organizationHelpers.setTouched()}
onChange={onOrganizationChange}
value={organizationField.value}
required={!me.is_superuser}
helperText={
me?.is_superuser &&
((!isOrgLookupDisabled && isGloballyAvailable) ||
organizationField.value === null)
? i18n._(
t`Leave this field blank to make the execution environment globally available.`
)
: null
}
autoPopulate={!me?.is_superuser ? !executionEnvironment?.id : null}
isDisabled={!!isOrgLookupDisabled && isGloballyAvailable.current}
/>
);
};
return ( return (
<> <>
<FormField <FormField
@@ -107,22 +134,17 @@ function ExecutionEnvironmentFormFields({
name="description" name="description"
type="text" type="text"
/> />
<OrganizationLookup {isOrgLookupDisabled && isGloballyAvailable.current ? (
helperTextInvalid={organizationMeta.error} <Tooltip
isValid={!organizationMeta.touched || !organizationMeta.error} content={i18n._(
onBlur={() => organizationHelpers.setTouched()} t`Globally available execution environment can not be reassigned to a specific Organization`
onChange={onOrganizationChange} )}
value={organizationField.value} >
required={!me.is_superuser} {renderOrganizationLookup()}
helperText={ </Tooltip>
me?.is_superuser ) : (
? i18n._( renderOrganizationLookup()
t`Leave this field blank to make the execution environment globally available.` )}
)
: null
}
autoPopulate={!me?.is_superuser ? !executionEnvironment?.id : null}
/>
<CredentialLookup <CredentialLookup
label={i18n._(t`Registry credential`)} label={i18n._(t`Registry credential`)}
@@ -146,6 +168,7 @@ function ExecutionEnvironmentForm({
onCancel, onCancel,
submitError, submitError,
me, me,
isOrgLookupDisabled,
...rest ...rest
}) { }) {
const { const {
@@ -191,6 +214,7 @@ function ExecutionEnvironmentForm({
me={me} me={me}
options={options} options={options}
executionEnvironment={executionEnvironment} executionEnvironment={executionEnvironment}
isOrgLookupDisabled={isOrgLookupDisabled}
{...rest} {...rest}
/> />
{submitError && <FormSubmitError error={submitError} />} {submitError && <FormSubmitError error={submitError} />}
@@ -210,11 +234,13 @@ ExecutionEnvironmentForm.propTypes = {
onCancel: func.isRequired, onCancel: func.isRequired,
onSubmit: func.isRequired, onSubmit: func.isRequired,
submitError: shape({}), submitError: shape({}),
isOrgLookupDisabled: bool,
}; };
ExecutionEnvironmentForm.defaultProps = { ExecutionEnvironmentForm.defaultProps = {
executionEnvironment: {}, executionEnvironment: {},
submitError: null, submitError: null,
isOrgLookupDisabled: false,
}; };
export default withI18n()(ExecutionEnvironmentForm); export default withI18n()(ExecutionEnvironmentForm);

View File

@@ -29,6 +29,45 @@ const executionEnvironment = {
'/api/v2/execution_environments/16/unified_job_templates/', '/api/v2/execution_environments/16/unified_job_templates/',
credential: '/api/v2/credentials/4/', credential: '/api/v2/credentials/4/',
}, },
summary_fields: {
organization: {
id: 1,
name: 'Default',
description: '',
},
credential: {
id: 4,
name: 'Container Registry',
description: '',
kind: 'registry',
cloud: false,
kubernetes: false,
credential_type_id: 17,
},
},
created: '2020-09-17T16:06:57.346128Z',
modified: '2020-09-17T16:06:57.346147Z',
description: 'A simple EE',
organization: 1,
image: 'https://registry.com/image/container',
managed_by_tower: false,
credential: 4,
};
const globallyAvailableEE = {
id: 17,
name: 'GEE',
type: 'execution_environment',
pull: 'one',
url: '/api/v2/execution_environments/17/',
related: {
created_by: '/api/v2/users/1/',
modified_by: '/api/v2/users/1/',
activity_stream: '/api/v2/execution_environments/16/activity_stream/',
unified_job_templates:
'/api/v2/execution_environments/16/unified_job_templates/',
credential: '/api/v2/credentials/4/',
},
summary_fields: { summary_fields: {
credential: { credential: {
id: 4, id: 4,
@@ -180,4 +219,55 @@ describe('<ExecutionEnvironmentForm/>', () => {
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
expect(onCancel).toBeCalled(); expect(onCancel).toBeCalled();
}); });
test('globally available EE can not have organization reassigned', async () => {
let newWrapper;
await act(async () => {
newWrapper = mountWithContexts(
<ExecutionEnvironmentForm
onCancel={onCancel}
onSubmit={onSubmit}
executionEnvironment={globallyAvailableEE}
options={mockOptions}
me={mockMe}
isOrgLookupDisabled
/>
);
});
await waitForElement(newWrapper, 'ContentLoading', el => el.length === 0);
expect(newWrapper.find('OrganizationLookup').prop('isDisabled')).toEqual(
true
);
expect(newWrapper.find('Tooltip').prop('content')).toEqual(
'Globally available execution environment can not be reassigned to a specific Organization'
);
});
test('should allow an organization to be re-assigned as globally available EE', async () => {
let newWrapper;
await act(async () => {
newWrapper = mountWithContexts(
<ExecutionEnvironmentForm
onCancel={onCancel}
onSubmit={onSubmit}
executionEnvironment={executionEnvironment}
options={mockOptions}
me={mockMe}
isOrgLookupDisabled
/>
);
});
await waitForElement(newWrapper, 'ContentLoading', el => el.length === 0);
expect(newWrapper.find('OrganizationLookup').prop('isDisabled')).toEqual(
false
);
expect(newWrapper.find('Tooltip').length).toEqual(0);
await act(async () => {
newWrapper.find('OrganizationLookup').invoke('onBlur')();
newWrapper.find('OrganizationLookup').invoke('onChange')(null);
});
newWrapper.update();
expect(newWrapper.find('OrganizationLookup').prop('value')).toEqual(null);
});
}); });