Skip to content

Commit ad99ae8

Browse files
committed
Merge remote-tracking branch 'upstream/master' into nls/tpr_url_forwarding
2 parents f0209a9 + 582e8e9 commit ad99ae8

File tree

45 files changed

+1370
-239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1370
-239
lines changed

src/core/server/elasticsearch/client/client_config.test.ts

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -216,28 +216,14 @@ describe('parseClientOptions', () => {
216216
);
217217
});
218218

219-
it('adds auth to the nodes if both `username` and `password` are set', () => {
220-
let options = parseClientOptions(
219+
it('does not add auth to the nodes', () => {
220+
const options = parseClientOptions(
221221
createConfig({
222222
username: 'user',
223-
hosts: ['http://node-A:9200'],
224-
}),
225-
false
226-
);
227-
expect(options.nodes).toMatchInlineSnapshot(`
228-
Array [
229-
Object {
230-
"url": "http://node-a:9200/",
231-
},
232-
]
233-
`);
234-
235-
options = parseClientOptions(
236-
createConfig({
237223
password: 'pass',
238224
hosts: ['http://node-A:9200'],
239225
}),
240-
false
226+
true
241227
);
242228
expect(options.nodes).toMatchInlineSnapshot(`
243229
Array [
@@ -246,22 +232,6 @@ describe('parseClientOptions', () => {
246232
},
247233
]
248234
`);
249-
250-
options = parseClientOptions(
251-
createConfig({
252-
username: 'user',
253-
password: 'pass',
254-
hosts: ['http://node-A:9200'],
255-
}),
256-
false
257-
);
258-
expect(options.nodes).toMatchInlineSnapshot(`
259-
Array [
260-
Object {
261-
"url": "http://user:pass@node-a:9200/",
262-
},
263-
]
264-
`);
265235
});
266236
});
267237
describe('when `scoped` is true', () => {

src/core/server/elasticsearch/client/client_config.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function parseClientOptions(
9393
};
9494
}
9595

96-
clientOptions.nodes = config.hosts.map((host) => convertHost(host, !scoped, config));
96+
clientOptions.nodes = config.hosts.map((host) => convertHost(host));
9797

9898
if (config.ssl) {
9999
clientOptions.ssl = generateSslConfig(
@@ -140,18 +140,10 @@ const generateSslConfig = (
140140
return ssl;
141141
};
142142

143-
const convertHost = (
144-
host: string,
145-
needAuth: boolean,
146-
{ username, password }: ElasticsearchClientConfig
147-
): NodeOptions => {
143+
const convertHost = (host: string): NodeOptions => {
148144
const url = new URL(host);
149145
const isHTTPS = url.protocol === 'https:';
150146
url.port = url.port || (isHTTPS ? '443' : '80');
151-
if (needAuth && username && password) {
152-
url.username = username;
153-
url.password = password;
154-
}
155147

156148
return {
157149
url,

x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,5 @@ export const TOKEN_TYPE_INFO = [
9393
];
9494

9595
export const FLYOUT_ARIA_LABEL_ID = 'credentialsFlyoutTitle';
96+
97+
export const DOCS_HREF = 'https://www.elastic.co/guide/en/app-search/current/authentication.html';

x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_flyout/body.test.tsx

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,98 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7+
import { setMockValues, setMockActions } from '../../../../__mocks__/kea.mock';
8+
79
import React from 'react';
810
import { shallow } from 'enzyme';
9-
import { EuiFlyoutBody } from '@elastic/eui';
11+
import { EuiFlyoutBody, EuiForm } from '@elastic/eui';
12+
13+
import { ApiTokenTypes } from '../constants';
14+
import { defaultApiToken } from '../credentials_logic';
1015

16+
import {
17+
FormKeyName,
18+
FormKeyType,
19+
FormKeyReadWriteAccess,
20+
FormKeyEngineAccess,
21+
FormKeyUpdateWarning,
22+
} from './form_components';
1123
import { CredentialsFlyoutBody } from './body';
1224

1325
describe('CredentialsFlyoutBody', () => {
26+
const values = {
27+
activeApiToken: defaultApiToken,
28+
activeApiTokenExists: false,
29+
};
30+
const actions = {
31+
onApiTokenChange: jest.fn(),
32+
};
33+
34+
beforeEach(() => {
35+
jest.clearAllMocks();
36+
setMockValues(values);
37+
setMockActions(actions);
38+
});
39+
1440
it('renders', () => {
1541
const wrapper = shallow(<CredentialsFlyoutBody />);
42+
1643
expect(wrapper.find(EuiFlyoutBody)).toHaveLength(1);
44+
expect(wrapper.find(EuiForm)).toHaveLength(1);
45+
});
46+
47+
it('shows the expected form components on default private key creation', () => {
48+
const wrapper = shallow(<CredentialsFlyoutBody />);
49+
50+
expect(wrapper.find(FormKeyName)).toHaveLength(1);
51+
expect(wrapper.find(FormKeyType)).toHaveLength(1);
52+
expect(wrapper.find(FormKeyReadWriteAccess)).toHaveLength(1);
53+
expect(wrapper.find(FormKeyEngineAccess)).toHaveLength(1);
54+
expect(wrapper.find(FormKeyUpdateWarning)).toHaveLength(0);
55+
});
56+
57+
it('does not show read-write access options for search keys', () => {
58+
setMockValues({
59+
...values,
60+
activeApiToken: {
61+
...defaultApiToken,
62+
type: ApiTokenTypes.Search,
63+
},
64+
});
65+
const wrapper = shallow(<CredentialsFlyoutBody />);
66+
67+
expect(wrapper.find(FormKeyReadWriteAccess)).toHaveLength(0);
68+
expect(wrapper.find(FormKeyEngineAccess)).toHaveLength(1);
69+
});
70+
71+
it('does not show read-write or engine access options for admin keys', () => {
72+
setMockValues({
73+
...values,
74+
activeApiToken: {
75+
...defaultApiToken,
76+
type: ApiTokenTypes.Admin,
77+
},
78+
});
79+
const wrapper = shallow(<CredentialsFlyoutBody />);
80+
81+
expect(wrapper.find(FormKeyReadWriteAccess)).toHaveLength(0);
82+
expect(wrapper.find(FormKeyEngineAccess)).toHaveLength(0);
83+
});
84+
85+
it('shows a warning if updating an existing key', () => {
86+
setMockValues({ ...values, activeApiTokenExists: true });
87+
const wrapper = shallow(<CredentialsFlyoutBody />);
88+
89+
expect(wrapper.find(FormKeyUpdateWarning)).toHaveLength(1);
90+
});
91+
92+
it('calls onApiTokenChange on form submit', () => {
93+
const wrapper = shallow(<CredentialsFlyoutBody />);
94+
95+
const preventDefault = jest.fn();
96+
wrapper.find(EuiForm).simulate('submit', { preventDefault });
97+
98+
expect(preventDefault).toHaveBeenCalled();
99+
expect(actions.onApiTokenChange).toHaveBeenCalled();
17100
});
18101
});

x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_flyout/body.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,41 @@
55
*/
66

77
import React from 'react';
8-
import { EuiFlyoutBody } from '@elastic/eui';
8+
import { useValues, useActions } from 'kea';
9+
import { EuiFlyoutBody, EuiForm } from '@elastic/eui';
910

1011
import { FlashMessages } from '../../../../shared/flash_messages';
12+
import { CredentialsLogic } from '../credentials_logic';
13+
import { ApiTokenTypes } from '../constants';
14+
15+
import {
16+
FormKeyName,
17+
FormKeyType,
18+
FormKeyReadWriteAccess,
19+
FormKeyEngineAccess,
20+
FormKeyUpdateWarning,
21+
} from './form_components';
1122

1223
export const CredentialsFlyoutBody: React.FC = () => {
24+
const { onApiTokenChange } = useActions(CredentialsLogic);
25+
const { activeApiToken, activeApiTokenExists } = useValues(CredentialsLogic);
26+
1327
return (
1428
<EuiFlyoutBody>
1529
<FlashMessages />
16-
Details go here
30+
<EuiForm
31+
onSubmit={(e) => {
32+
e.preventDefault();
33+
onApiTokenChange();
34+
}}
35+
component="form"
36+
>
37+
<FormKeyName />
38+
<FormKeyType />
39+
{activeApiToken.type === ApiTokenTypes.Private && <FormKeyReadWriteAccess />}
40+
{activeApiToken.type !== ApiTokenTypes.Admin && <FormKeyEngineAccess />}
41+
</EuiForm>
42+
{activeApiTokenExists && <FormKeyUpdateWarning />}
1743
</EuiFlyoutBody>
1844
);
1945
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export { FormKeyName } from './key_name';
8+
export { FormKeyType } from './key_type';
9+
export { FormKeyReadWriteAccess } from './key_read_write_access';
10+
export { FormKeyEngineAccess } from './key_engine_access';
11+
export { FormKeyUpdateWarning } from './key_update_warning';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { setMockValues, setMockActions } from '../../../../../__mocks__/kea.mock';
8+
9+
import React from 'react';
10+
import { shallow } from 'enzyme';
11+
import { EuiRadio, EuiCheckbox } from '@elastic/eui';
12+
13+
import { FormKeyEngineAccess, EngineSelection } from './key_engine_access';
14+
15+
describe('FormKeyEngineAccess', () => {
16+
const values = {
17+
myRole: { canAccessAllEngines: true },
18+
fullEngineAccessChecked: true,
19+
};
20+
const actions = {
21+
setAccessAllEngines: jest.fn(),
22+
};
23+
24+
beforeEach(() => {
25+
jest.clearAllMocks();
26+
setMockValues(values);
27+
setMockActions(actions);
28+
});
29+
30+
it('renders', () => {
31+
const wrapper = shallow(<FormKeyEngineAccess />);
32+
33+
expect(wrapper.find(EuiRadio)).toHaveLength(2);
34+
expect(wrapper.find(EngineSelection)).toHaveLength(0);
35+
});
36+
37+
it('hides the full access radio option if the user does not have access to all engines', () => {
38+
setMockValues({
39+
...values,
40+
myRole: { canAccessAllEngines: false },
41+
});
42+
const wrapper = shallow(<FormKeyEngineAccess />);
43+
44+
expect(wrapper.find('#all_engines').prop('hidden')).toEqual(true);
45+
});
46+
47+
it('controls the checked values for access radios', () => {
48+
setMockValues({
49+
...values,
50+
fullEngineAccessChecked: true,
51+
});
52+
const wrapper = shallow(<FormKeyEngineAccess />);
53+
54+
expect(wrapper.find('#all_engines').prop('checked')).toEqual(true);
55+
expect(wrapper.find('#all_engines').prop('value')).toEqual('true');
56+
expect(wrapper.find('#specific_engines').prop('checked')).toEqual(false);
57+
expect(wrapper.find('#specific_engines').prop('value')).toEqual('false');
58+
59+
setMockValues({
60+
...values,
61+
fullEngineAccessChecked: false,
62+
});
63+
wrapper.setProps({}); // Re-render
64+
65+
expect(wrapper.find('#all_engines').prop('checked')).toEqual(false);
66+
expect(wrapper.find('#all_engines').prop('value')).toEqual('false');
67+
expect(wrapper.find('#specific_engines').prop('checked')).toEqual(true);
68+
expect(wrapper.find('#specific_engines').prop('value')).toEqual('true');
69+
});
70+
71+
it('calls setAccessAllEngines when the radios are changed', () => {
72+
const wrapper = shallow(<FormKeyEngineAccess />);
73+
74+
wrapper.find('#all_engines').simulate('change');
75+
expect(actions.setAccessAllEngines).toHaveBeenCalledWith(true);
76+
77+
wrapper.find('#specific_engines').simulate('change');
78+
expect(actions.setAccessAllEngines).toHaveBeenCalledWith(false);
79+
});
80+
81+
it('displays the engine selection panel if the limited access radio is selected', () => {
82+
setMockValues({
83+
...values,
84+
fullEngineAccessChecked: false,
85+
});
86+
const wrapper = shallow(<FormKeyEngineAccess />);
87+
88+
expect(wrapper.find(EngineSelection)).toHaveLength(1);
89+
});
90+
});
91+
92+
describe('EngineSelection', () => {
93+
const values = {
94+
activeApiToken: { engines: [] },
95+
engines: [{ name: 'engine1' }, { name: 'engine2' }, { name: 'engine3' }],
96+
};
97+
const actions = {
98+
onEngineSelect: jest.fn(),
99+
};
100+
101+
beforeEach(() => {
102+
jest.clearAllMocks();
103+
setMockValues(values);
104+
setMockActions(actions);
105+
});
106+
107+
it('renders', () => {
108+
const wrapper = shallow(<EngineSelection />);
109+
110+
expect(wrapper.find('h4').text()).toEqual('Select Engines');
111+
expect(wrapper.find(EuiCheckbox)).toHaveLength(3);
112+
expect(wrapper.find(EuiCheckbox).first().prop('label')).toEqual('engine1');
113+
});
114+
115+
it('controls the engines checked state', () => {
116+
setMockValues({
117+
...values,
118+
activeApiToken: { engines: ['engine3'] },
119+
});
120+
const wrapper = shallow(<EngineSelection />);
121+
122+
expect(wrapper.find(EuiCheckbox).first().prop('checked')).toEqual(false);
123+
expect(wrapper.find(EuiCheckbox).last().prop('checked')).toEqual(true);
124+
});
125+
126+
it('calls onEngineSelect when the checkboxes are changed', () => {
127+
const wrapper = shallow(<EngineSelection />);
128+
129+
wrapper.find(EuiCheckbox).first().simulate('change');
130+
expect(actions.onEngineSelect).toHaveBeenCalledWith('engine1');
131+
132+
wrapper.find(EuiCheckbox).last().simulate('change');
133+
expect(actions.onEngineSelect).toHaveBeenCalledWith('engine3');
134+
});
135+
});

0 commit comments

Comments
 (0)