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
commit a194dfdbbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 136 additions and 19 deletions

View File

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

View File

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

View File

@ -29,6 +29,45 @@ const executionEnvironment = {
'/api/v2/execution_environments/16/unified_job_templates/',
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: {
credential: {
id: 4,
@ -180,4 +219,55 @@ describe('<ExecutionEnvironmentForm/>', () => {
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
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);
});
});