Skip to content

Commit

Permalink
Merge branch 'master' into move-exceptions-part1
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine committed Mar 9, 2021
2 parents 14f8738 + add02f1 commit ac43909
Show file tree
Hide file tree
Showing 47 changed files with 982 additions and 413 deletions.
6 changes: 3 additions & 3 deletions api_docs/file_upload.json
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@
"type": "Function",
"label": "import",
"signature": [
"(id: string, index: string, pipelineId: string, setImportProgress: (progress: number) => void) => Promise<",
"(id: string, index: string, pipelineId: string | undefined, setImportProgress: (progress: number) => void) => Promise<",
{
"pluginId": "fileUpload",
"scope": "public",
Expand Down Expand Up @@ -413,9 +413,9 @@
{
"type": "string",
"label": "pipelineId",
"isRequired": true,
"isRequired": false,
"signature": [
"string"
"string | undefined"
],
"description": [],
"source": {
Expand Down
12 changes: 12 additions & 0 deletions docs/api/saved-objects/import.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@

experimental[] Create sets of {kib} saved objects from a file created by the export API.

==== Compatibility across versions
Saved objects can only be imported into the same version, a newer minor on the same major or the next major. Exported saved objects are not backwards compatible and cannot be imported into an older version of {kib}. See the table below for compatibility examples:

|=======
| Exporting version | Importing version | Compatible?
| 6.7.0 | 6.8.1 | Yes
| 6.8.1 | 7.3.0 | Yes
| 7.3.0 | 7.11.1 | Yes
| 7.11.1 | 7.6.0 | No
| 6.8.1 | 8.0.0 | No
|=======

[[saved-objects-api-import-request]]
==== Request

Expand Down
15 changes: 15 additions & 0 deletions docs/management/managing-saved-objects.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ have multiple environments for development and production.
Import and export also work well when you have a large number
of objects to update and want to batch the process.

[float]
==== Compatibility across versions

With each release, {kib} introduces changes to the way saved objects are stored. When importing a saved object, {kib} will run the necessary migrations to ensure that the imported saved objects are compatible with the current version.

However, saved objects can only be imported into the same version, a newer minor on the same major or the next major. Exported saved objects are not backwards compatible and cannot be imported into an older version of {kib}. See the table below for compatibility examples:

|=======
| Exporting version | Importing version | Compatible?
| 6.7.0 | 6.8.1 | Yes
| 6.8.1 | 7.3.0 | Yes
| 7.3.0 | 7.11.1 | Yes
| 7.11.1 | 7.6.0 | No
| 6.8.1 | 8.0.0 | No
|=======

[float]
==== Import
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"author": "Rashid Khan <rashid.khan@elastic.co>",
"scripts": {
"bazel": "bazel",
"preinstall": "node ./preinstall_check",
"kbn": "node scripts/kbn",
"es": "node scripts/es",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export function Tabs({
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill onClick={() => openFieldEditor()}>
<EuiButton fill onClick={() => openFieldEditor()} data-test-subj="addField">
{addFieldButtonLabel}
</EuiButton>
</EuiFlexItem>
Expand Down
52 changes: 52 additions & 0 deletions test/functional/apps/management/_runtime_fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import expect from '@kbn/expect';

export default function ({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const log = getService('log');
const browser = getService('browser');
const retry = getService('retry');
const PageObjects = getPageObjects(['settings']);

describe('runtime fields', function () {
this.tags(['skipFirefox']);

before(async function () {
await browser.setWindowSize(1200, 800);
await esArchiver.load('discover');
// delete .kibana index and then wait for Kibana to re-create it
await kibanaServer.uiSettings.replace({});
await kibanaServer.uiSettings.update({});
});

after(async function afterAll() {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.removeLogstashIndexPatternIfExist();
});

describe('create runtime field', function describeIndexTests() {
const fieldName = 'atest';

it('should create runtime field', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
const startingCount = parseInt(await PageObjects.settings.getFieldsTabCount());
await log.debug('add runtime field');
await PageObjects.settings.addRuntimeField(fieldName, 'Keyword', "emit('hello world')");
await retry.try(async function () {
expect(parseInt(await PageObjects.settings.getFieldsTabCount())).to.be(startingCount + 1);
});
});
});
});
}
1 change: 1 addition & 0 deletions test/functional/apps/management/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_mgmt_import_saved_objects'));
loadTestFile(require.resolve('./_index_patterns_empty'));
loadTestFile(require.resolve('./_scripted_fields'));
loadTestFile(require.resolve('./_runtime_fields'));
});

describe('', function () {
Expand Down
52 changes: 52 additions & 0 deletions test/functional/page_objects/settings_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,58 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
await this.clickSaveScriptedField();
}

async addRuntimeField(name: string, type: string, script: string) {
await this.clickAddField();
await this.setFieldName(name);
await this.setFieldType(type);
if (script) {
await this.setFieldScript(script);
}
await this.clickSaveField();
await this.closeIndexPatternFieldEditor();
}

async closeIndexPatternFieldEditor() {
await retry.waitFor('field editor flyout to close', async () => {
return !(await testSubjects.exists('euiFlyoutCloseButton'));
});
}

async clickAddField() {
log.debug('click Add Field');
await testSubjects.click('addField');
}

async clickSaveField() {
log.debug('click Save');
await testSubjects.click('fieldSaveButton');
}

async setFieldName(name: string) {
log.debug('set field name = ' + name);
await testSubjects.setValue('nameField', name);
}

async setFieldType(type: string) {
log.debug('set type = ' + type);
await testSubjects.setValue('typeField', type);
}

async setFieldScript(script: string) {
log.debug('set script = ' + script);
const formatRow = await testSubjects.find('valueRow');
const formatRowToggle = (
await formatRow.findAllByCssSelector('[data-test-subj="toggle"]')
)[0];

await formatRowToggle.click();
const getMonacoTextArea = async () => (await formatRow.findAllByCssSelector('textarea'))[0];
retry.waitFor('monaco editor is ready', async () => !!(await getMonacoTextArea()));
const monacoTextArea = await getMonacoTextArea();
await monacoTextArea.focus();
browser.pressKeys(script);
}

async clickAddScriptedField() {
log.debug('click Add Scripted Field');
await testSubjects.click('addScriptedFieldLink');
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/enterprise_search/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,5 @@ export const JSON_HEADER = {
};

export const READ_ONLY_MODE_HEADER = 'x-ent-search-read-only-mode';

export const ENTERPRISE_SEARCH_KIBANA_COOKIE = '_enterprise_search';
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ describe('RoleMappingsTable', () => {
expect(wrapper.find(EuiTableRow)).toHaveLength(0);
});

it('handles input change with special chars', () => {
const wrapper = shallow(<RoleMappingsTable {...props} />);
const input = wrapper.find(EuiFieldSearch);
const value = '*//username';
input.simulate('change', { target: { value } });

expect(wrapper.find(EuiTableRow)).toHaveLength(1);
});

it('shows default message when "accessAllEngines" is true', () => {
const wrapper = shallow(
<RoleMappingsTable {...props} roleMappings={[asRoleMapping as any]} accessItemKey="engines" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ export const RoleMappingsTable: React.FC<Props> = ({
});

const filterResults = (result: SharedRoleMapping) => {
// Filter out non-alphanumeric characters, except for underscores, hyphens, and spaces
const sanitizedValue = filterValue.replace(/[^\w\s-]/g, '');
const values = Object.values(result);
const regexp = new RegExp(filterValue, 'i');
const regexp = new RegExp(sanitizedValue, 'i');
return values.filter((x) => regexp.test(x)).length > 0;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

import { mockConfig, mockLogger } from '../__mocks__';

import { JSON_HEADER, READ_ONLY_MODE_HEADER } from '../../common/constants';
import {
ENTERPRISE_SEARCH_KIBANA_COOKIE,
JSON_HEADER,
READ_ONLY_MODE_HEADER,
} from '../../common/constants';

import { EnterpriseSearchRequestHandler } from './enterprise_search_request_handler';

Expand Down Expand Up @@ -171,6 +175,28 @@ describe('EnterpriseSearchRequestHandler', () => {
headers: mockExpectedResponseHeaders,
});
});

it('filters out any _sessionData passed back from Enterprise Search', async () => {
const jsonWithSessionData = {
_sessionData: {
secrets: 'no peeking',
},
regular: 'data',
};

EnterpriseSearchAPI.mockReturn(jsonWithSessionData, { headers: JSON_HEADER });

const requestHandler = enterpriseSearchRequestHandler.createRequest({ path: '/api/prep' });
await makeAPICall(requestHandler);

expect(responseMock.custom).toHaveBeenCalledWith({
statusCode: 200,
body: {
regular: 'data',
},
headers: mockExpectedResponseHeaders,
});
});
});
});

Expand Down Expand Up @@ -378,6 +404,33 @@ describe('EnterpriseSearchRequestHandler', () => {
});
});

describe('setSessionData', () => {
it('sets the value of wsOAuthTokenPackage in a cookie', async () => {
const tokenPackage = 'some_encrypted_secrets';

const mockNow = 'Thu, 04 Mar 2021 22:40:32 GMT';
const mockInAnHour = 'Thu, 04 Mar 2021 23:40:32 GMT';
jest.spyOn(global.Date, 'now').mockImplementationOnce(() => {
return new Date(mockNow).valueOf();
});

const sessionDataBody = {
_sessionData: { wsOAuthTokenPackage: tokenPackage },
regular: 'data',
};

EnterpriseSearchAPI.mockReturn(sessionDataBody, { headers: JSON_HEADER });

const requestHandler = enterpriseSearchRequestHandler.createRequest({ path: '/' });
await makeAPICall(requestHandler);

expect(enterpriseSearchRequestHandler.headers).toEqual({
['set-cookie']: `${ENTERPRISE_SEARCH_KIBANA_COOKIE}=${tokenPackage}; Path=/; Expires=${mockInAnHour}; SameSite=Lax; HttpOnly`,
...mockExpectedResponseHeaders,
});
});
});

it('isEmptyObj', async () => {
expect(enterpriseSearchRequestHandler.isEmptyObj({})).toEqual(true);
expect(enterpriseSearchRequestHandler.isEmptyObj({ empty: false })).toEqual(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import {
Logger,
} from 'src/core/server';

import { JSON_HEADER, READ_ONLY_MODE_HEADER } from '../../common/constants';
import {
ENTERPRISE_SEARCH_KIBANA_COOKIE,
JSON_HEADER,
READ_ONLY_MODE_HEADER,
} from '../../common/constants';

import { ConfigType } from '../index';

interface ConstructorDependencies {
Expand Down Expand Up @@ -113,11 +118,17 @@ export class EnterpriseSearchRequestHandler {
return this.handleInvalidDataError(response, url, json);
}

// Intercept data that is meant for the server side session
const { _sessionData, ...responseJson } = json;
if (_sessionData) {
this.setSessionData(_sessionData);
}

// Pass successful responses back to the front-end
return response.custom({
statusCode: status,
headers: this.headers,
body: json,
body: _sessionData ? responseJson : json,
});
} catch (e) {
// Catch connection/auth errors
Expand Down Expand Up @@ -270,6 +281,27 @@ export class EnterpriseSearchRequestHandler {
this.headers[READ_ONLY_MODE_HEADER] = readOnlyMode as 'true' | 'false';
}

/**
* Extract Session Data
*
* In the future, this will set the keys passed back from Enterprise Search
* into the Kibana login session.
* For now we'll explicity look for the Workplace Search OAuth token package
* and stuff it into a cookie so it can be picked up later when we proxy the
* OAuth callback.
*/
setSessionData(sessionData: { [key: string]: string }) {
if (sessionData.wsOAuthTokenPackage) {
const anHourFromNow = new Date(Date.now());
anHourFromNow.setHours(anHourFromNow.getHours() + 1);

const cookiePayload = `${ENTERPRISE_SEARCH_KIBANA_COOKIE}=${sessionData.wsOAuthTokenPackage};`;
const cookieRestrictions = `Path=/; Expires=${anHourFromNow.toUTCString()}; SameSite=Lax; HttpOnly`;

this.headers['set-cookie'] = `${cookiePayload} ${cookieRestrictions}`;
}
}

/**
* Misc helpers
*/
Expand Down
Loading

0 comments on commit ac43909

Please sign in to comment.