mirror of
https://github.com/ansible/awx.git
synced 2026-04-11 13:09:21 -02:30
Merge pull request #8781 from marshmalien/setting-ldap-edit-forms
Add all LDAP (Default, 1-5) setting forms Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -6,6 +6,7 @@ import 'codemirror/mode/javascript/javascript';
|
|||||||
import 'codemirror/mode/yaml/yaml';
|
import 'codemirror/mode/yaml/yaml';
|
||||||
import 'codemirror/mode/jinja2/jinja2';
|
import 'codemirror/mode/jinja2/jinja2';
|
||||||
import 'codemirror/lib/codemirror.css';
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
import 'codemirror/addon/display/placeholder';
|
||||||
|
|
||||||
const LINE_HEIGHT = 24;
|
const LINE_HEIGHT = 24;
|
||||||
const PADDING = 12;
|
const PADDING = 12;
|
||||||
@@ -55,6 +56,17 @@ const CodeMirror = styled(ReactCodeMirror)`
|
|||||||
background-color: var(--pf-c-form-control--disabled--BackgroundColor);
|
background-color: var(--pf-c-form-control--disabled--BackgroundColor);
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
|
${props =>
|
||||||
|
props.options &&
|
||||||
|
props.options.placeholder &&
|
||||||
|
`
|
||||||
|
.CodeMirror-empty {
|
||||||
|
pre.CodeMirror-placeholder {
|
||||||
|
color: var(--pf-c-form-control--placeholder--Color);
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function CodeMirrorInput({
|
function CodeMirrorInput({
|
||||||
@@ -66,6 +78,7 @@ function CodeMirrorInput({
|
|||||||
rows,
|
rows,
|
||||||
fullHeight,
|
fullHeight,
|
||||||
className,
|
className,
|
||||||
|
placeholder,
|
||||||
}) {
|
}) {
|
||||||
// Workaround for CodeMirror bug: If CodeMirror renders in a modal on the
|
// Workaround for CodeMirror bug: If CodeMirror renders in a modal on the
|
||||||
// modal's initial render, it appears as an empty box due to mis-calculated
|
// modal's initial render, it appears as an empty box due to mis-calculated
|
||||||
@@ -92,6 +105,7 @@ function CodeMirrorInput({
|
|||||||
smartIndent: false,
|
smartIndent: false,
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
lineWrapping: true,
|
lineWrapping: true,
|
||||||
|
placeholder,
|
||||||
readOnly,
|
readOnly,
|
||||||
}}
|
}}
|
||||||
fullHeight={fullHeight}
|
fullHeight={fullHeight}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import {
|
|||||||
waitForElement,
|
waitForElement,
|
||||||
} from '../../../../testUtils/enzymeHelpers';
|
} from '../../../../testUtils/enzymeHelpers';
|
||||||
import { SettingsAPI } from '../../../api';
|
import { SettingsAPI } from '../../../api';
|
||||||
|
import { SettingsProvider } from '../../../contexts/Settings';
|
||||||
|
import mockAllOptions from '../shared/data.allSettingOptions.json';
|
||||||
|
import mockLDAP from '../shared/data.ldapSettings.json';
|
||||||
import LDAP from './LDAP';
|
import LDAP from './LDAP';
|
||||||
|
|
||||||
jest.mock('../../../api/models/Settings');
|
jest.mock('../../../api/models/Settings');
|
||||||
SettingsAPI.readCategory.mockResolvedValue({
|
SettingsAPI.readCategory.mockResolvedValue({ data: mockLDAP });
|
||||||
data: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('<LDAP />', () => {
|
describe('<LDAP />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
@@ -39,9 +40,14 @@ describe('<LDAP />', () => {
|
|||||||
initialEntries: ['/settings/ldap/default/edit'],
|
initialEntries: ['/settings/ldap/default/edit'],
|
||||||
});
|
});
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<LDAP />, {
|
wrapper = mountWithContexts(
|
||||||
context: { router: { history } },
|
<SettingsProvider value={mockAllOptions.actions}>
|
||||||
});
|
<LDAP />
|
||||||
|
</SettingsProvider>,
|
||||||
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
expect(wrapper.find('LDAPEdit').length).toBe(1);
|
expect(wrapper.find('LDAPEdit').length).toBe(1);
|
||||||
|
|||||||
@@ -1,25 +1,250 @@
|
|||||||
import React from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { useHistory, useRouteMatch } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { Formik } from 'formik';
|
||||||
import { t } from '@lingui/macro';
|
import { Form } from '@patternfly/react-core';
|
||||||
import { Button } from '@patternfly/react-core';
|
import { CardBody } from '../../../../components/Card';
|
||||||
import { CardBody, CardActionsRow } from '../../../../components/Card';
|
import ContentError from '../../../../components/ContentError';
|
||||||
|
import ContentLoading from '../../../../components/ContentLoading';
|
||||||
|
import { FormSubmitError } from '../../../../components/FormField';
|
||||||
|
import {
|
||||||
|
FormColumnLayout,
|
||||||
|
FormFullWidthLayout,
|
||||||
|
} from '../../../../components/FormLayout';
|
||||||
|
import { useSettings } from '../../../../contexts/Settings';
|
||||||
|
import { RevertAllAlert, RevertFormActionGroup } from '../../shared';
|
||||||
|
import {
|
||||||
|
BooleanField,
|
||||||
|
ChoiceField,
|
||||||
|
EncryptedField,
|
||||||
|
InputField,
|
||||||
|
ObjectField,
|
||||||
|
} from '../../shared/SharedFields';
|
||||||
|
import { formatJson } from '../../shared/settingUtils';
|
||||||
|
import useModal from '../../../../util/useModal';
|
||||||
|
import useRequest from '../../../../util/useRequest';
|
||||||
|
import { SettingsAPI } from '../../../../api';
|
||||||
|
|
||||||
|
function filterByPrefix(data, prefix) {
|
||||||
|
return Object.keys(data)
|
||||||
|
.filter(key => key.includes(prefix))
|
||||||
|
.reduce((obj, key) => {
|
||||||
|
obj[key] = data[key];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function LDAPEdit() {
|
||||||
|
const history = useHistory();
|
||||||
|
const { isModalOpen, toggleModal, closeModal } = useModal();
|
||||||
|
const { PUT: options } = useSettings();
|
||||||
|
const {
|
||||||
|
params: { category },
|
||||||
|
} = useRouteMatch('/settings/ldap/:category/edit');
|
||||||
|
const ldapCategory =
|
||||||
|
category === 'default' ? 'AUTH_LDAP_' : `AUTH_LDAP_${category}_`;
|
||||||
|
|
||||||
|
const { isLoading, error, request: fetchLDAP, result: ldap } = useRequest(
|
||||||
|
useCallback(async () => {
|
||||||
|
const { data } = await SettingsAPI.readCategory('ldap');
|
||||||
|
|
||||||
|
const mergedData = {};
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
if (!options[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mergedData[key] = options[key];
|
||||||
|
mergedData[key].value = data[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
const allCategories = {
|
||||||
|
AUTH_LDAP_1_: filterByPrefix(mergedData, 'AUTH_LDAP_1_'),
|
||||||
|
AUTH_LDAP_2_: filterByPrefix(mergedData, 'AUTH_LDAP_2_'),
|
||||||
|
AUTH_LDAP_3_: filterByPrefix(mergedData, 'AUTH_LDAP_3_'),
|
||||||
|
AUTH_LDAP_4_: filterByPrefix(mergedData, 'AUTH_LDAP_4_'),
|
||||||
|
AUTH_LDAP_5_: filterByPrefix(mergedData, 'AUTH_LDAP_5_'),
|
||||||
|
AUTH_LDAP_: Object.assign({}, mergedData),
|
||||||
|
};
|
||||||
|
Object.keys({
|
||||||
|
...allCategories.AUTH_LDAP_1_,
|
||||||
|
...allCategories.AUTH_LDAP_2_,
|
||||||
|
...allCategories.AUTH_LDAP_3_,
|
||||||
|
...allCategories.AUTH_LDAP_4_,
|
||||||
|
...allCategories.AUTH_LDAP_5_,
|
||||||
|
}).forEach(keyToOmit => {
|
||||||
|
delete allCategories.AUTH_LDAP_[keyToOmit];
|
||||||
|
});
|
||||||
|
|
||||||
|
return allCategories[ldapCategory];
|
||||||
|
}, [options, ldapCategory]),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchLDAP();
|
||||||
|
}, [fetchLDAP]);
|
||||||
|
|
||||||
|
const { error: submitError, request: submitForm } = useRequest(
|
||||||
|
useCallback(
|
||||||
|
async values => {
|
||||||
|
await SettingsAPI.updateAll(values);
|
||||||
|
history.push(`/settings/ldap/${category}/details`);
|
||||||
|
},
|
||||||
|
[history, category]
|
||||||
|
),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSubmit = async form => {
|
||||||
|
await submitForm({
|
||||||
|
[`${ldapCategory}BIND_DN`]: form[`${ldapCategory}BIND_DN`],
|
||||||
|
[`${ldapCategory}BIND_PASSWORD`]: form[`${ldapCategory}BIND_PASSWORD`],
|
||||||
|
[`${ldapCategory}DENY_GROUP`]: form[`${ldapCategory}DENY_GROUP`],
|
||||||
|
[`${ldapCategory}GROUP_TYPE`]: form[`${ldapCategory}GROUP_TYPE`],
|
||||||
|
[`${ldapCategory}REQUIRE_GROUP`]: form[`${ldapCategory}REQUIRE_GROUP`],
|
||||||
|
[`${ldapCategory}SERVER_URI`]: form[`${ldapCategory}SERVER_URI`],
|
||||||
|
[`${ldapCategory}START_TLS`]: form[`${ldapCategory}START_TLS`],
|
||||||
|
[`${ldapCategory}USER_DN_TEMPLATE`]: form[
|
||||||
|
`${ldapCategory}USER_DN_TEMPLATE`
|
||||||
|
],
|
||||||
|
[`${ldapCategory}GROUP_SEARCH`]: formatJson(
|
||||||
|
form[`${ldapCategory}GROUP_SEARCH`]
|
||||||
|
),
|
||||||
|
[`${ldapCategory}GROUP_TYPE_PARAMS`]: formatJson(
|
||||||
|
form[`${ldapCategory}GROUP_TYPE_PARAMS`]
|
||||||
|
),
|
||||||
|
[`${ldapCategory}ORGANIZATION_MAP`]: formatJson(
|
||||||
|
form[`${ldapCategory}ORGANIZATION_MAP`]
|
||||||
|
),
|
||||||
|
[`${ldapCategory}TEAM_MAP`]: formatJson(form[`${ldapCategory}TEAM_MAP`]),
|
||||||
|
[`${ldapCategory}USER_ATTR_MAP`]: formatJson(
|
||||||
|
form[`${ldapCategory}USER_ATTR_MAP`]
|
||||||
|
),
|
||||||
|
[`${ldapCategory}USER_FLAGS_BY_GROUP`]: formatJson(
|
||||||
|
form[`${ldapCategory}USER_FLAGS_BY_GROUP`]
|
||||||
|
),
|
||||||
|
[`${ldapCategory}USER_SEARCH`]: formatJson(
|
||||||
|
form[`${ldapCategory}USER_SEARCH`]
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRevertAll = async () => {
|
||||||
|
const defaultValues = Object.assign(
|
||||||
|
...Object.entries(ldap).map(([key, value]) => ({
|
||||||
|
[key]: value.default,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
await submitForm(defaultValues);
|
||||||
|
closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
history.push(`/settings/ldap/${category}/details`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialValues = fields =>
|
||||||
|
Object.keys(fields).reduce((acc, key) => {
|
||||||
|
if (fields[key].type === 'list' || fields[key].type === 'nested object') {
|
||||||
|
const emptyDefault = fields[key].type === 'list' ? '[]' : '{}';
|
||||||
|
acc[key] = fields[key].value
|
||||||
|
? JSON.stringify(fields[key].value, null, 2)
|
||||||
|
: emptyDefault;
|
||||||
|
} else {
|
||||||
|
acc[key] = fields[key].value ?? '';
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
function LDAPEdit({ i18n }) {
|
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{i18n._(t`Edit form coming soon :)`)}
|
{isLoading && <ContentLoading />}
|
||||||
<CardActionsRow>
|
{!isLoading && error && <ContentError error={error} />}
|
||||||
<Button
|
{!isLoading && ldap && (
|
||||||
aria-label={i18n._(t`Cancel`)}
|
<Formik initialValues={initialValues(ldap)} onSubmit={handleSubmit}>
|
||||||
component={Link}
|
{formik => (
|
||||||
to="/settings/ldap/details"
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
>
|
<FormColumnLayout>
|
||||||
{i18n._(t`Cancel`)}
|
<InputField
|
||||||
</Button>
|
name={`${ldapCategory}SERVER_URI`}
|
||||||
</CardActionsRow>
|
config={ldap[`${ldapCategory}SERVER_URI`]}
|
||||||
|
/>
|
||||||
|
<EncryptedField
|
||||||
|
name={`${ldapCategory}BIND_PASSWORD`}
|
||||||
|
config={ldap[`${ldapCategory}BIND_PASSWORD`]}
|
||||||
|
/>
|
||||||
|
<ChoiceField
|
||||||
|
name={`${ldapCategory}GROUP_TYPE`}
|
||||||
|
config={ldap[`${ldapCategory}GROUP_TYPE`]}
|
||||||
|
/>
|
||||||
|
<BooleanField
|
||||||
|
name={`${ldapCategory}START_TLS`}
|
||||||
|
config={ldap[`${ldapCategory}START_TLS`]}
|
||||||
|
/>
|
||||||
|
<FormFullWidthLayout>
|
||||||
|
<InputField
|
||||||
|
name={`${ldapCategory}BIND_DN`}
|
||||||
|
config={ldap[`${ldapCategory}BIND_DN`]}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
name={`${ldapCategory}USER_DN_TEMPLATE`}
|
||||||
|
config={ldap[`${ldapCategory}USER_DN_TEMPLATE`]}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
name={`${ldapCategory}REQUIRE_GROUP`}
|
||||||
|
config={ldap[`${ldapCategory}REQUIRE_GROUP`]}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
name={`${ldapCategory}DENY_GROUP`}
|
||||||
|
config={ldap[`${ldapCategory}DENY_GROUP`]}
|
||||||
|
/>
|
||||||
|
</FormFullWidthLayout>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}USER_SEARCH`}
|
||||||
|
config={ldap[`${ldapCategory}USER_SEARCH`]}
|
||||||
|
/>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}GROUP_SEARCH`}
|
||||||
|
config={ldap[`${ldapCategory}GROUP_SEARCH`]}
|
||||||
|
/>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}USER_ATTR_MAP`}
|
||||||
|
config={ldap[`${ldapCategory}USER_ATTR_MAP`]}
|
||||||
|
/>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}GROUP_TYPE_PARAMS`}
|
||||||
|
config={ldap[`${ldapCategory}GROUP_TYPE_PARAMS`]}
|
||||||
|
/>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}USER_FLAGS_BY_GROUP`}
|
||||||
|
config={ldap[`${ldapCategory}USER_FLAGS_BY_GROUP`]}
|
||||||
|
/>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}ORGANIZATION_MAP`}
|
||||||
|
config={ldap[`${ldapCategory}ORGANIZATION_MAP`]}
|
||||||
|
/>
|
||||||
|
<ObjectField
|
||||||
|
name={`${ldapCategory}TEAM_MAP`}
|
||||||
|
config={ldap[`${ldapCategory}TEAM_MAP`]}
|
||||||
|
/>
|
||||||
|
{submitError && <FormSubmitError error={submitError} />}
|
||||||
|
</FormColumnLayout>
|
||||||
|
<RevertFormActionGroup
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
onRevert={toggleModal}
|
||||||
|
/>
|
||||||
|
{isModalOpen && (
|
||||||
|
<RevertAllAlert
|
||||||
|
onClose={closeModal}
|
||||||
|
onRevertAll={handleRevertAll}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withI18n()(LDAPEdit);
|
export default LDAPEdit;
|
||||||
|
|||||||
@@ -1,16 +1,265 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers';
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { useRouteMatch } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
mountWithContexts,
|
||||||
|
waitForElement,
|
||||||
|
} from '../../../../../testUtils/enzymeHelpers';
|
||||||
|
import mockAllOptions from '../../shared/data.allSettingOptions.json';
|
||||||
|
import mockLDAP from '../../shared/data.ldapSettings.json';
|
||||||
|
import { SettingsProvider } from '../../../../contexts/Settings';
|
||||||
|
import { SettingsAPI } from '../../../../api';
|
||||||
import LDAPEdit from './LDAPEdit';
|
import LDAPEdit from './LDAPEdit';
|
||||||
|
|
||||||
|
jest.mock('../../../../api/models/Settings');
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useRouteMatch: jest.fn(),
|
||||||
|
}));
|
||||||
|
SettingsAPI.readCategory.mockResolvedValue({ data: mockLDAP });
|
||||||
|
|
||||||
describe('<LDAPEdit />', () => {
|
describe('<LDAPEdit />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
beforeEach(() => {
|
let history;
|
||||||
wrapper = mountWithContexts(<LDAPEdit />);
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
history = createMemoryHistory({
|
||||||
|
initialEntries: ['/settings/ldap/default/edit'],
|
||||||
|
});
|
||||||
|
useRouteMatch.mockImplementation(() => ({
|
||||||
|
url: '/settings/ldap/default/edit',
|
||||||
|
path: '/settings/ldap/:category/edit',
|
||||||
|
params: { category: 'default' },
|
||||||
|
}));
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SettingsProvider value={mockAllOptions.actions}>
|
||||||
|
<LDAPEdit />
|
||||||
|
</SettingsProvider>,
|
||||||
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders without crashing', () => {
|
test('initially renders without crashing', () => {
|
||||||
expect(wrapper.find('LDAPEdit').length).toBe(1);
|
expect(wrapper.find('LDAPEdit').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should display expected form fields', async () => {
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Server URI"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Bind DN"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Bind Password"]').length).toBe(
|
||||||
|
1
|
||||||
|
);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP User Search"]').length).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[label="LDAP User DN Template"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[label="LDAP User Attribute Map"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Group Search"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Group Type"]').length).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[label="LDAP Group Type Parameters"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Require Group"]').length).toBe(
|
||||||
|
1
|
||||||
|
);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Deny Group"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Start TLS"]').length).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[label="LDAP User Flags By Group"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[label="LDAP Organization Map"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="LDAP Team Map"]').length).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[fieldId="AUTH_LDAP_SERVER_URI"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[fieldId="AUTH_LDAP_5_SERVER_URI"]').length
|
||||||
|
).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should successfully send default values to api on form revert all', async () => {
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||||
|
expect(wrapper.find('RevertAllAlert')).toHaveLength(0);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper
|
||||||
|
.find('button[aria-label="Revert all to default"]')
|
||||||
|
.invoke('onClick')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('RevertAllAlert')).toHaveLength(1);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper
|
||||||
|
.find('RevertAllAlert button[aria-label="Confirm revert all"]')
|
||||||
|
.invoke('onClick')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||||
|
AUTH_LDAP_BIND_DN: '',
|
||||||
|
AUTH_LDAP_BIND_PASSWORD: '',
|
||||||
|
AUTH_LDAP_CONNECTION_OPTIONS: {
|
||||||
|
OPT_NETWORK_TIMEOUT: 30,
|
||||||
|
OPT_REFERRALS: 0,
|
||||||
|
},
|
||||||
|
AUTH_LDAP_DENY_GROUP: null,
|
||||||
|
AUTH_LDAP_GROUP_SEARCH: [],
|
||||||
|
AUTH_LDAP_GROUP_TYPE: 'MemberDNGroupType',
|
||||||
|
AUTH_LDAP_GROUP_TYPE_PARAMS: {
|
||||||
|
member_attr: 'member',
|
||||||
|
name_attr: 'cn',
|
||||||
|
},
|
||||||
|
AUTH_LDAP_ORGANIZATION_MAP: {},
|
||||||
|
AUTH_LDAP_REQUIRE_GROUP: null,
|
||||||
|
AUTH_LDAP_SERVER_URI: '',
|
||||||
|
AUTH_LDAP_START_TLS: false,
|
||||||
|
AUTH_LDAP_TEAM_MAP: {},
|
||||||
|
AUTH_LDAP_USER_ATTR_MAP: {},
|
||||||
|
AUTH_LDAP_USER_DN_TEMPLATE: null,
|
||||||
|
AUTH_LDAP_USER_FLAGS_BY_GROUP: {},
|
||||||
|
AUTH_LDAP_USER_SEARCH: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should successfully send request to api on form submission', async () => {
|
||||||
|
act(() => {
|
||||||
|
wrapper
|
||||||
|
.find(
|
||||||
|
'FormGroup[fieldId="AUTH_LDAP_BIND_PASSWORD"] button[aria-label="Revert"]'
|
||||||
|
)
|
||||||
|
.invoke('onClick')();
|
||||||
|
wrapper
|
||||||
|
.find(
|
||||||
|
'FormGroup[fieldId="AUTH_LDAP_BIND_DN"] button[aria-label="Revert"]'
|
||||||
|
)
|
||||||
|
.invoke('onClick')();
|
||||||
|
wrapper.find('input#AUTH_LDAP_SERVER_URI').simulate('change', {
|
||||||
|
target: {
|
||||||
|
value: 'ldap://mock.example.com',
|
||||||
|
name: 'AUTH_LDAP_SERVER_URI',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
wrapper.find('CodeMirrorInput#AUTH_LDAP_TEAM_MAP').invoke('onChange')(
|
||||||
|
'{\n"LDAP Sales":{\n"organization":\n"mock org"\n}\n}'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Form').invoke('onSubmit')();
|
||||||
|
});
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledWith({
|
||||||
|
AUTH_LDAP_BIND_DN: '',
|
||||||
|
AUTH_LDAP_BIND_PASSWORD: '',
|
||||||
|
AUTH_LDAP_DENY_GROUP: '',
|
||||||
|
AUTH_LDAP_GROUP_SEARCH: [],
|
||||||
|
AUTH_LDAP_GROUP_TYPE: 'MemberDNGroupType',
|
||||||
|
AUTH_LDAP_GROUP_TYPE_PARAMS: { name_attr: 'cn', member_attr: 'member' },
|
||||||
|
AUTH_LDAP_ORGANIZATION_MAP: {},
|
||||||
|
AUTH_LDAP_REQUIRE_GROUP: 'CN=Tower Users,OU=Users,DC=example,DC=com',
|
||||||
|
AUTH_LDAP_SERVER_URI: 'ldap://mock.example.com',
|
||||||
|
AUTH_LDAP_START_TLS: false,
|
||||||
|
AUTH_LDAP_USER_ATTR_MAP: {},
|
||||||
|
AUTH_LDAP_USER_DN_TEMPLATE: 'uid=%(user)s,OU=Users,DC=example,DC=com',
|
||||||
|
AUTH_LDAP_USER_FLAGS_BY_GROUP: {},
|
||||||
|
AUTH_LDAP_USER_SEARCH: [],
|
||||||
|
AUTH_LDAP_TEAM_MAP: {
|
||||||
|
'LDAP Sales': {
|
||||||
|
organization: 'mock org',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should navigate to ldap default detail on successful submission', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Form').invoke('onSubmit')();
|
||||||
|
});
|
||||||
|
expect(history.location.pathname).toEqual('/settings/ldap/default/details');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should navigate to ldap default detail when cancel is clicked', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||||
|
});
|
||||||
|
expect(history.location.pathname).toEqual('/settings/ldap/default/details');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display error message on unsuccessful submission', async () => {
|
||||||
|
const error = {
|
||||||
|
response: {
|
||||||
|
data: { detail: 'An error occurred' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
SettingsAPI.updateAll.mockImplementation(() => Promise.reject(error));
|
||||||
|
expect(wrapper.find('FormSubmitError').length).toBe(0);
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Form').invoke('onSubmit')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||||
|
expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display ContentError on throw', async () => {
|
||||||
|
SettingsAPI.readCategory.mockImplementationOnce(() =>
|
||||||
|
Promise.reject(new Error())
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SettingsProvider value={mockAllOptions.actions}>
|
||||||
|
<LDAPEdit />
|
||||||
|
</SettingsProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
expect(wrapper.find('ContentError').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display ldap category 5 edit form', async () => {
|
||||||
|
history = createMemoryHistory({
|
||||||
|
initialEntries: ['/settings/ldap/5/edit'],
|
||||||
|
});
|
||||||
|
useRouteMatch.mockImplementation(() => ({
|
||||||
|
url: '/settings/ldap/5/edit',
|
||||||
|
path: '/settings/ldap/:category/edit',
|
||||||
|
params: { category: '5' },
|
||||||
|
}));
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SettingsProvider value={mockAllOptions.actions}>
|
||||||
|
<LDAPEdit />
|
||||||
|
</SettingsProvider>,
|
||||||
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[fieldId="AUTH_LDAP_SERVER_URI"]').length
|
||||||
|
).toBe(0);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[fieldId="AUTH_LDAP_5_SERVER_URI"]').length
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('FormGroup[fieldId="AUTH_LDAP_5_SERVER_URI"] input').props()
|
||||||
|
.value
|
||||||
|
).toEqual('ldap://ldap5.example.com');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import { FormFullWidthLayout } from '../../../components/FormLayout';
|
|||||||
import Popover from '../../../components/Popover';
|
import Popover from '../../../components/Popover';
|
||||||
import {
|
import {
|
||||||
combine,
|
combine,
|
||||||
required,
|
|
||||||
url,
|
|
||||||
integer,
|
integer,
|
||||||
minMaxValue,
|
minMaxValue,
|
||||||
|
required,
|
||||||
|
url,
|
||||||
} from '../../../util/validators';
|
} from '../../../util/validators';
|
||||||
import RevertButton from './RevertButton';
|
import RevertButton from './RevertButton';
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ const SettingGroup = withI18n()(
|
|||||||
isRequired={isRequired}
|
isRequired={isRequired}
|
||||||
label={label}
|
label={label}
|
||||||
validated={validated}
|
validated={validated}
|
||||||
|
id={fieldId}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
<>
|
<>
|
||||||
<Popover
|
<Popover
|
||||||
@@ -84,13 +85,13 @@ const BooleanField = withI18n()(
|
|||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
id={name}
|
id={name}
|
||||||
|
ouiaId={name}
|
||||||
isChecked={field.value}
|
isChecked={field.value}
|
||||||
isDisabled={disabled}
|
isDisabled={disabled}
|
||||||
label={i18n._(t`On`)}
|
label={i18n._(t`On`)}
|
||||||
labelOff={i18n._(t`Off`)}
|
labelOff={i18n._(t`Off`)}
|
||||||
onChange={checked => helpers.setValue(checked)}
|
onChange={checked => helpers.setValue(checked)}
|
||||||
aria-label={ariaLabel || config.label}
|
aria-label={ariaLabel || config.label}
|
||||||
ouiaId={ariaLabel || config.label}
|
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
) : null;
|
) : null;
|
||||||
@@ -242,11 +243,13 @@ const ObjectField = withI18n()(({ i18n, name, config, isRequired = false }) => {
|
|||||||
>
|
>
|
||||||
<CodeMirrorInput
|
<CodeMirrorInput
|
||||||
{...field}
|
{...field}
|
||||||
|
fullHeight
|
||||||
id={name}
|
id={name}
|
||||||
|
mode="javascript"
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
helpers.setValue(value);
|
helpers.setValue(value);
|
||||||
}}
|
}}
|
||||||
mode="javascript"
|
placeholder={JSON.stringify(config?.placeholder, null, 2)}
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
"AUTH_LDAP_4_USER_FLAGS_BY_GROUP": {},
|
"AUTH_LDAP_4_USER_FLAGS_BY_GROUP": {},
|
||||||
"AUTH_LDAP_4_ORGANIZATION_MAP": {},
|
"AUTH_LDAP_4_ORGANIZATION_MAP": {},
|
||||||
"AUTH_LDAP_4_TEAM_MAP": {},
|
"AUTH_LDAP_4_TEAM_MAP": {},
|
||||||
"AUTH_LDAP_5_SERVER_URI": "",
|
"AUTH_LDAP_5_SERVER_URI": "ldap://ldap5.example.com",
|
||||||
"AUTH_LDAP_5_BIND_DN": "",
|
"AUTH_LDAP_5_BIND_DN": "",
|
||||||
"AUTH_LDAP_5_BIND_PASSWORD": "",
|
"AUTH_LDAP_5_BIND_PASSWORD": "",
|
||||||
"AUTH_LDAP_5_START_TLS": false,
|
"AUTH_LDAP_5_START_TLS": false,
|
||||||
|
|||||||
Reference in New Issue
Block a user