Skip to content

Commit

Permalink
Merge branch 'master' into find-plugins-circular-deps
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Nov 26, 2020
2 parents 027ba37 + 51f75a5 commit 7d42882
Show file tree
Hide file tree
Showing 31 changed files with 356 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -902,8 +902,9 @@ The most significant changes on the Kibana side for the consumers are the follow
===== User client accessor
Internal /current user client accessors has been renamed and are now
properties instead of functions:
** `callAsInternalUser('ping')` -> `asInternalUser.ping()`
** `callAsCurrentUser('ping')` -> `asCurrentUser.ping()`

* `callAsInternalUser('ping')` -> `asInternalUser.ping()`
* `callAsCurrentUser('ping')` -> `asCurrentUser.ping()`
* the API now reflects the `Client`’s instead of leveraging the
string-based endpoint names the `LegacyAPICaller` was using.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ kibana_vars=(
xpack.code.security.gitProtocolWhitelist
xpack.encryptedSavedObjects.encryptionKey
xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys
xpack.fleet.agents.elasticsearch.host
xpack.fleet.agents.kibana.host
xpack.fleet.agents.tlsCheckDisabled
xpack.graph.enabled
xpack.graph.canEditDrillDownUrls
xpack.graph.savePolicy
Expand Down
6 changes: 3 additions & 3 deletions src/plugins/discover/public/application/angular/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise
if (!_.isEqual(newStatePartial, oldStatePartial)) {
$scope.$evalAsync(async () => {
if (oldStatePartial.index !== newStatePartial.index) {
//in case of index switch the route has currently to be reloaded, legacy
//in case of index pattern switch the route has currently to be reloaded, legacy
$route.reload();
return;
}

Expand Down Expand Up @@ -289,8 +290,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise
$scope.state.sort,
config.get(MODIFY_COLUMNS_ON_SWITCH)
);
await replaceUrlAppState(nextAppState);
$route.reload();
await setAppState(nextAppState);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const applicationUsageSchema = {
logs: commonSchema,
metrics: commonSchema,
infra: commonSchema, // It's a forward app so we'll likely never report it
ingestManager: commonSchema,
fleet: commonSchema,
lens: commonSchema,
maps: commonSchema,
ml: commonSchema,
Expand Down
45 changes: 24 additions & 21 deletions src/plugins/share/server/routes/lib/short_url_assert_valid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,39 @@

import { shortUrlAssertValid } from './short_url_assert_valid';

const PROTOCOL_ERROR = /^Short url targets cannot have a protocol/;
const HOSTNAME_ERROR = /^Short url targets cannot have a hostname/;
const PATH_ERROR = /^Short url target path must be in the format/;

describe('shortUrlAssertValid()', () => {
const invalid = [
['protocol', 'http://localhost:5601/app/kibana'],
['protocol', 'https://localhost:5601/app/kibana'],
['protocol', 'mailto:foo@bar.net'],
['protocol', 'javascript:alert("hi")'], // eslint-disable-line no-script-url
['hostname', 'localhost/app/kibana'],
['hostname and port', 'local.host:5601/app/kibana'],
['hostname and auth', 'user:pass@localhost.net/app/kibana'],
['path traversal', '/app/../../not-kibana'],
['deep path', '/app/kibana/foo'],
['deep path', '/app/kibana/foo/bar'],
['base path', '/base/app/kibana'],
['protocol', 'http://localhost:5601/app/kibana', PROTOCOL_ERROR],
['protocol', 'https://localhost:5601/app/kibana', PROTOCOL_ERROR],
['protocol', 'mailto:foo@bar.net', PROTOCOL_ERROR],
['protocol', 'javascript:alert("hi")', PROTOCOL_ERROR], // eslint-disable-line no-script-url
['hostname', 'localhost/app/kibana', PATH_ERROR], // according to spec, this is not a valid URL -- you cannot specify a hostname without a protocol
['hostname and port', 'local.host:5601/app/kibana', PROTOCOL_ERROR], // parser detects 'local.host' as the protocol
['hostname and auth', 'user:pass@localhost.net/app/kibana', PROTOCOL_ERROR], // parser detects 'user' as the protocol
['path traversal', '/app/../../not-kibana', PATH_ERROR], // fails because there are >2 path parts
['path traversal', '/../not-kibana', PATH_ERROR], // fails because first path part is not 'app'
['deep path', '/app/kibana/foo', PATH_ERROR], // fails because there are >2 path parts
['deeper path', '/app/kibana/foo/bar', PATH_ERROR], // fails because there are >2 path parts
['base path', '/base/app/kibana', PATH_ERROR], // fails because there are >2 path parts
['path with an extra leading slash', '//foo/app/kibana', HOSTNAME_ERROR], // parser detects 'foo' as the hostname
['path with an extra leading slash', '///app/kibana', HOSTNAME_ERROR], // parser detects '' as the hostname
['path without app', '/foo/kibana', PATH_ERROR], // fails because first path part is not 'app'
['path without appId', '/app/', PATH_ERROR], // fails because there is only one path part (leading and trailing slashes are trimmed)
];

invalid.forEach(([desc, url]) => {
it(`fails when url has ${desc}`, () => {
try {
shortUrlAssertValid(url);
throw new Error(`expected assertion to throw`);
} catch (err) {
if (!err || !err.isBoom) {
throw err;
}
}
invalid.forEach(([desc, url, error]) => {
it(`fails when url has ${desc as string}`, () => {
expect(() => shortUrlAssertValid(url as string)).toThrowError(error);
});
});

const valid = [
'/app/kibana',
'/app/kibana/', // leading and trailing slashes are trimmed
'/app/monitoring#angular/route',
'/app/text#document-id',
'/app/some?with=query',
Expand Down
12 changes: 8 additions & 4 deletions src/plugins/share/server/routes/lib/short_url_assert_valid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ import { trim } from 'lodash';
import Boom from '@hapi/boom';

export function shortUrlAssertValid(url: string) {
const { protocol, hostname, pathname } = parse(url);
const { protocol, hostname, pathname } = parse(
url,
false /* parseQueryString */,
true /* slashesDenoteHost */
);

if (protocol) {
if (protocol !== null) {
throw Boom.notAcceptable(`Short url targets cannot have a protocol, found "${protocol}"`);
}

if (hostname) {
if (hostname !== null) {
throw Boom.notAcceptable(`Short url targets cannot have a hostname, found "${hostname}"`);
}

const pathnameParts = trim(pathname === null ? undefined : pathname, '/').split('/');
if (pathnameParts.length !== 2) {
if (pathnameParts.length !== 2 || pathnameParts[0] !== 'app' || !pathnameParts[1]) {
throw Boom.notAcceptable(
`Short url target path must be in the format "/app/{{appId}}", found "${pathname}"`
);
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/telemetry/schema/oss_plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@
}
}
},
"ingestManager": {
"fleet": {
"properties": {
"clicks_total": {
"type": "long"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import _ from 'lodash';
import { startsWith } from 'lodash';
import moment from 'moment';

export function timeShift(resp, panel, series) {
Expand All @@ -26,13 +26,15 @@ export function timeShift(resp, panel, series) {
const matches = series.offset_time.match(/^([+-]?[\d]+)([shmdwMy]|ms)$/);

if (matches) {
const offsetValue = Number(matches[1]);
const offsetValue = matches[1];
const offsetUnit = matches[2];
const offset = moment.duration(offsetValue, offsetUnit).valueOf();

results.forEach((item) => {
if (_.startsWith(item.id, series.id)) {
item.data = item.data.map(([time, value]) => [time + offset, value]);
if (startsWith(item.id, series.id)) {
item.data = item.data.map((row) => [
moment.utc(row[0]).add(offsetValue, offsetUnit).valueOf(),
row[1],
]);
}
});
}
Expand Down
17 changes: 17 additions & 0 deletions test/functional/apps/discover/_indexpattern_without_timefield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import { FtrProviderContext } from '../../ftr_provider_context';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const browser = getService('browser');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const security = getService('security');
Expand Down Expand Up @@ -50,5 +51,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
throw new Error('Expected timepicker to exist');
}
});
it('should switch between with and without timefield using the browser back button', async () => {
await PageObjects.discover.selectIndexPattern('without-timefield');
if (await PageObjects.timePicker.timePickerExists()) {
throw new Error('Expected timepicker not to exist');
}

await PageObjects.discover.selectIndexPattern('with-timefield');
if (!(await PageObjects.timePicker.timePickerExists())) {
throw new Error('Expected timepicker to exist');
}
// Navigating back to discover
await browser.goBack();
if (await PageObjects.timePicker.timePickerExists()) {
throw new Error('Expected timepicker not to exist');
}
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export type TestSubjects =
| 'createTemplateButton'
| 'dataStreamsEmptyPromptTemplateLink'
| 'dataStreamTable'
| 'deleteDataStreamsButton'
| 'deleteDataStreamButton'
| 'deleteSystemTemplateCallOut'
| 'deleteTemplateButton'
| 'deleteTemplatesConfirmation'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface DataStreamsTabTestBed extends TestBed<TestSubjects> {
clickNameAt: (index: number) => void;
clickIndicesAt: (index: number) => void;
clickDeleteActionAt: (index: number) => void;
selectDataStream: (name: string, selected: boolean) => void;
clickConfirmDelete: () => void;
clickDeleteDataStreamButton: () => void;
clickDetailPanelIndexTemplateLink: () => void;
Expand Down Expand Up @@ -125,6 +126,13 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
findDeleteActionAt(index).simulate('click');
};

const selectDataStream = (name: string, selected: boolean) => {
const {
form: { selectCheckBox },
} = testBed;
selectCheckBox(`checkboxSelectRow-${name}`, selected);
};

const findDeleteConfirmationModal = () => {
const { find } = testBed;
return find('deleteDataStreamsConfirmation');
Expand Down Expand Up @@ -194,6 +202,7 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
clickNameAt,
clickIndicesAt,
clickDeleteActionAt,
selectDataStream,
clickConfirmDelete,
clickDeleteDataStreamButton,
clickDetailPanelIndexTemplateLink,
Expand Down Expand Up @@ -223,6 +232,9 @@ export const createDataStreamPayload = (dataStream: Partial<DataStream>): DataSt
indexTemplateName: 'indexTemplate',
storageSize: '1b',
maxTimeStamp: 420,
privileges: {
delete_index: true,
},
...dataStream,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,4 +449,77 @@ describe('Data Streams tab', () => {
expect(tableCellsValues).toEqual([['', 'non-managed-data-stream', 'green', '1', 'Delete']]);
});
});

describe('data stream privileges', () => {
describe('delete', () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamWithDelete = createDataStreamPayload({
name: 'dataStreamWithDelete',
privileges: { delete_index: true },
});
const dataStreamNoDelete = createDataStreamPayload({
name: 'dataStreamNoDelete',
privileges: { delete_index: false },
});

beforeEach(async () => {
setLoadDataStreamsResponse([dataStreamWithDelete, dataStreamNoDelete]);

testBed = await setup({ history: createMemoryHistory() });
await act(async () => {
testBed.actions.goToDataStreamsList();
});
testBed.component.update();
});

test('displays/hides delete button depending on data streams privileges', async () => {
const { table } = testBed;
const { tableCellsValues } = table.getMetaData('dataStreamTable');

expect(tableCellsValues).toEqual([
['', 'dataStreamNoDelete', 'green', '1', ''],
['', 'dataStreamWithDelete', 'green', '1', 'Delete'],
]);
});

test('displays/hides delete action depending on data streams privileges', async () => {
const {
actions: { selectDataStream },
find,
} = testBed;

selectDataStream('dataStreamNoDelete', true);
expect(find('deleteDataStreamsButton').exists()).toBeFalsy();

selectDataStream('dataStreamWithDelete', true);
expect(find('deleteDataStreamsButton').exists()).toBeFalsy();

selectDataStream('dataStreamNoDelete', false);
expect(find('deleteDataStreamsButton').exists()).toBeTruthy();
});

test('displays delete button in detail panel', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamWithDelete);
await clickNameAt(1);

expect(find('deleteDataStreamButton').exists()).toBeTruthy();
});

test('hides delete button in detail panel', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamNoDelete);
await clickNameAt(0);

expect(find('deleteDataStreamButton').exists()).toBeFalsy();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS
store_size: storageSize,
maximum_timestamp: maxTimeStamp,
_meta,
privileges,
} = dataStreamFromEs;

return {
Expand All @@ -37,6 +38,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS
storageSize,
maxTimeStamp,
_meta,
privileges,
};
}

Expand Down
16 changes: 12 additions & 4 deletions x-pack/plugins/index_management/common/types/data_streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ interface TimestampFieldFromEs {

type TimestampField = TimestampFieldFromEs;

interface MetaFieldFromEs {
interface MetaFromEs {
managed_by: string;
package: any;
managed: boolean;
}

type MetaField = MetaFieldFromEs;
type Meta = MetaFromEs;

interface PrivilegesFromEs {
delete_index: boolean;
}

type Privileges = PrivilegesFromEs;

export type HealthFromEs = 'GREEN' | 'YELLOW' | 'RED';

Expand All @@ -25,12 +31,13 @@ export interface DataStreamFromEs {
timestamp_field: TimestampFieldFromEs;
indices: DataStreamIndexFromEs[];
generation: number;
_meta?: MetaFieldFromEs;
_meta?: MetaFromEs;
status: HealthFromEs;
template: string;
ilm_policy?: string;
store_size?: string;
maximum_timestamp?: number;
privileges: PrivilegesFromEs;
}

export interface DataStreamIndexFromEs {
Expand All @@ -50,7 +57,8 @@ export interface DataStream {
ilmPolicyName?: string;
storageSize?: string;
maxTimeStamp?: number;
_meta?: MetaField;
_meta?: Meta;
privileges: Privileges;
}

export interface DataStreamIndex {
Expand Down
Loading

0 comments on commit 7d42882

Please sign in to comment.