Skip to content

Commit

Permalink
Merge branch 'master' of github.com:elastic/kibana into feat/hosts-kp…
Browse files Browse the repository at this point in the history
…i-search-strategy
  • Loading branch information
patrykkopycinski committed Sep 18, 2020
2 parents f32f8bb + 217276e commit 420ac24
Show file tree
Hide file tree
Showing 136 changed files with 3,287 additions and 1,140 deletions.
1 change: 1 addition & 0 deletions docs/user/dashboard/drilldowns.asciidoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[role="xpack"]
[[drilldowns]]
== Use drilldowns for dashboard actions

Expand Down
2 changes: 2 additions & 0 deletions docs/user/dashboard/url-drilldown.asciidoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[[url-drilldown]]
=== URL drilldown

beta[]

The URL drilldown allows you to navigate from a dashboard to an internal or external URL.
The destination URL can be dynamic, depending on the dashboard context or user’s interaction with a visualization.

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"color": "1.0.3",
"commander": "3.0.2",
"core-js": "^3.6.4",
"cypress-promise": "^1.1.0",
"deep-freeze-strict": "^1.1.1",
"del": "^5.1.0",
"elastic-apm-node": "^3.7.0",
Expand Down
1 change: 1 addition & 0 deletions x-pack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
"cronstrue": "^1.51.0",
"cypress": "5.0.0",
"cypress-multi-reporters": "^1.2.3",
"cypress-promise": "^1.1.0",
"d3": "3.5.17",
"d3-scale": "1.0.7",
"dragselect": "1.13.1",
Expand Down
28 changes: 18 additions & 10 deletions x-pack/plugins/actions/server/actions_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ describe('update()', () => {
},
references: [],
});
unsecuredSavedObjectsClient.update.mockResolvedValueOnce({
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
id: 'my-action',
type: 'action',
attributes: {
Expand Down Expand Up @@ -946,7 +946,7 @@ describe('update()', () => {
},
references: [],
});
unsecuredSavedObjectsClient.update.mockResolvedValueOnce({
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
id: 'my-action',
type: 'action',
attributes: {
Expand All @@ -972,17 +972,21 @@ describe('update()', () => {
name: 'my name',
config: {},
});
expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1);
expect(unsecuredSavedObjectsClient.update.mock.calls[0]).toMatchInlineSnapshot(`
expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1);
expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"action",
"my-action",
Object {
"actionTypeId": "my-action-type",
"config": Object {},
"name": "my name",
"secrets": Object {},
},
Object {
"id": "my-action",
"overwrite": true,
"references": Array [],
},
]
`);
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -1043,7 +1047,7 @@ describe('update()', () => {
},
references: [],
});
unsecuredSavedObjectsClient.update.mockResolvedValueOnce({
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
id: 'my-action',
type: 'action',
attributes: {
Expand Down Expand Up @@ -1081,11 +1085,10 @@ describe('update()', () => {
c: true,
},
});
expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1);
expect(unsecuredSavedObjectsClient.update.mock.calls[0]).toMatchInlineSnapshot(`
expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1);
expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"action",
"my-action",
Object {
"actionTypeId": "my-action-type",
"config": Object {
Expand All @@ -1096,6 +1099,11 @@ describe('update()', () => {
"name": "my name",
"secrets": Object {},
},
Object {
"id": "my-action",
"overwrite": true,
"references": Array [],
},
]
`);
});
Expand All @@ -1118,7 +1126,7 @@ describe('update()', () => {
},
references: [],
});
unsecuredSavedObjectsClient.update.mockResolvedValueOnce({
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
id: 'my-action',
type: 'action',
attributes: {
Expand Down
47 changes: 36 additions & 11 deletions x-pack/plugins/actions/server/actions_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from 'src/core/server';

import { i18n } from '@kbn/i18n';
import { omitBy, isUndefined } from 'lodash';
import { ActionTypeRegistry } from './action_type_registry';
import { validateConfig, validateSecrets, ActionExecutorContract } from './lib';
import {
Expand All @@ -30,7 +31,10 @@ import {
} from './create_execute_function';
import { ActionsAuthorization } from './authorization/actions_authorization';
import { ActionType } from '../common';
import { shouldLegacyRbacApplyBySource } from './authorization/should_legacy_rbac_apply_by_source';
import {
getAuthorizationModeBySource,
AuthorizationMode,
} from './authorization/get_authorization_mode_by_source';

// We are assuming there won't be many actions. This is why we will load
// all the actions in advance and assume the total count to not go over 10000.
Expand Down Expand Up @@ -151,21 +155,36 @@ export class ActionsClient {
'update'
);
}
const existingObject = await this.unsecuredSavedObjectsClient.get<RawAction>('action', id);
const { actionTypeId } = existingObject.attributes;
const { attributes, references, version } = await this.unsecuredSavedObjectsClient.get<
RawAction
>('action', id);
const { actionTypeId } = attributes;
const { name, config, secrets } = action;
const actionType = this.actionTypeRegistry.get(actionTypeId);
const validatedActionTypeConfig = validateConfig(actionType, config);
const validatedActionTypeSecrets = validateSecrets(actionType, secrets);

this.actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);

const result = await this.unsecuredSavedObjectsClient.update<RawAction>('action', id, {
actionTypeId,
name,
config: validatedActionTypeConfig as SavedObjectAttributes,
secrets: validatedActionTypeSecrets as SavedObjectAttributes,
});
const result = await this.unsecuredSavedObjectsClient.create<RawAction>(
'action',
{
...attributes,
actionTypeId,
name,
config: validatedActionTypeConfig as SavedObjectAttributes,
secrets: validatedActionTypeSecrets as SavedObjectAttributes,
},
omitBy(
{
id,
overwrite: true,
references,
version,
},
isUndefined
)
);

return {
id,
Expand Down Expand Up @@ -301,15 +320,21 @@ export class ActionsClient {
params,
source,
}: Omit<ExecuteOptions, 'request'>): Promise<ActionTypeExecutorResult<unknown>> {
if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) {
if (
(await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) ===
AuthorizationMode.RBAC
) {
await this.authorization.ensureAuthorized('execute');
}
return this.actionExecutor.execute({ actionId, params, source, request: this.request });
}

public async enqueueExecution(options: EnqueueExecutionOptions): Promise<void> {
const { source } = options;
if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) {
if (
(await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) ===
AuthorizationMode.RBAC
) {
await this.authorization.ensureAuthorized('execute');
}
return this.executionEnqueuer(this.unsecuredSavedObjectsClient, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { actionsAuthorizationAuditLoggerMock } from './audit_logger.mock';
import { ActionsAuthorizationAuditLogger, AuthorizationResult } from './audit_logger';
import { ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../saved_objects';
import { AuthenticatedUser } from '../../../security/server';
import { AuthorizationMode } from './get_authorization_mode_by_source';

const request = {} as KibanaRequest;

Expand Down Expand Up @@ -195,7 +196,7 @@ describe('ensureAuthorized', () => {
`);
});

test('exempts users from requiring privileges to execute actions when shouldUseLegacyRbac is true', async () => {
test('exempts users from requiring privileges to execute actions when authorizationMode is Legacy', async () => {
const { authorization, authentication } = mockSecurity();
const checkPrivileges: jest.MockedFunction<ReturnType<
typeof authorization.checkPrivilegesDynamicallyWithRequest
Expand All @@ -206,7 +207,7 @@ describe('ensureAuthorized', () => {
authorization,
authentication,
auditLogger,
shouldUseLegacyRbac: true,
authorizationMode: AuthorizationMode.Legacy,
});

authentication.getCurrentUser.mockReturnValueOnce(({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { KibanaRequest } from 'src/core/server';
import { SecurityPluginSetup } from '../../../security/server';
import { ActionsAuthorizationAuditLogger } from './audit_logger';
import { ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../saved_objects';
import { AuthorizationMode } from './get_authorization_mode_by_source';

export interface ConstructorOptions {
request: KibanaRequest;
Expand All @@ -22,7 +23,7 @@ export interface ConstructorOptions {
// actions to continue to execute - which requires that we exempt auth on
// `get` for Connectors and `execute` for Action execution when used by
// these legacy alerts
shouldUseLegacyRbac?: boolean;
authorizationMode?: AuthorizationMode;
}

const operationAlias: Record<
Expand All @@ -43,20 +44,19 @@ export class ActionsAuthorization {
private readonly authorization?: SecurityPluginSetup['authz'];
private readonly authentication?: SecurityPluginSetup['authc'];
private readonly auditLogger: ActionsAuthorizationAuditLogger;
private readonly shouldUseLegacyRbac: boolean;

private readonly authorizationMode: AuthorizationMode;
constructor({
request,
authorization,
authentication,
auditLogger,
shouldUseLegacyRbac = false,
authorizationMode = AuthorizationMode.RBAC,
}: ConstructorOptions) {
this.request = request;
this.authorization = authorization;
this.authentication = authentication;
this.auditLogger = auditLogger;
this.shouldUseLegacyRbac = shouldUseLegacyRbac;
this.authorizationMode = authorizationMode;
}

public async ensureAuthorized(operation: string, actionTypeId?: string) {
Expand Down Expand Up @@ -87,6 +87,9 @@ export class ActionsAuthorization {
}

private isOperationExemptDueToLegacyRbac(operation: string) {
return this.shouldUseLegacyRbac && LEGACY_RBAC_EXEMPT_OPERATIONS.has(operation);
return (
this.authorizationMode === AuthorizationMode.Legacy &&
LEGACY_RBAC_EXEMPT_OPERATIONS.has(operation)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,93 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { shouldLegacyRbacApplyBySource } from './should_legacy_rbac_apply_by_source';
import {
getAuthorizationModeBySource,
AuthorizationMode,
} from './get_authorization_mode_by_source';
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
import uuid from 'uuid';
import { asSavedObjectExecutionSource } from '../lib';

const unsecuredSavedObjectsClient = savedObjectsClientMock.create();

describe(`#shouldLegacyRbacApplyBySource`, () => {
test('should return false if no source is provided', async () => {
expect(await shouldLegacyRbacApplyBySource(unsecuredSavedObjectsClient)).toEqual(false);
describe(`#getAuthorizationModeBySource`, () => {
test('should return RBAC if no source is provided', async () => {
expect(await getAuthorizationModeBySource(unsecuredSavedObjectsClient)).toEqual(
AuthorizationMode.RBAC
);
});

test('should return false if source is not an alert', async () => {
test('should return RBAC if source is not an alert', async () => {
expect(
await shouldLegacyRbacApplyBySource(
await getAuthorizationModeBySource(
unsecuredSavedObjectsClient,
asSavedObjectExecutionSource({
type: 'action',
id: uuid.v4(),
})
)
).toEqual(false);
).toEqual(AuthorizationMode.RBAC);
});

test('should return false if source alert is not marked as legacy', async () => {
test('should return RBAC if source alert is not marked as legacy', async () => {
const id = uuid.v4();
unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id }));
expect(
await shouldLegacyRbacApplyBySource(
await getAuthorizationModeBySource(
unsecuredSavedObjectsClient,
asSavedObjectExecutionSource({
type: 'alert',
id,
})
)
).toEqual(false);
).toEqual(AuthorizationMode.RBAC);
});

test('should return true if source alert is marked as legacy', async () => {
test('should return Legacy if source alert is marked as legacy', async () => {
const id = uuid.v4();
unsecuredSavedObjectsClient.get.mockResolvedValue(
mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } })
);
expect(
await shouldLegacyRbacApplyBySource(
await getAuthorizationModeBySource(
unsecuredSavedObjectsClient,
asSavedObjectExecutionSource({
type: 'alert',
id,
})
)
).toEqual(true);
).toEqual(AuthorizationMode.Legacy);
});

test('should return false if source alert is marked as modern', async () => {
test('should return RBAC if source alert is marked as modern', async () => {
const id = uuid.v4();
unsecuredSavedObjectsClient.get.mockResolvedValue(
mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: '7.10.0' } } })
);
expect(
await shouldLegacyRbacApplyBySource(
await getAuthorizationModeBySource(
unsecuredSavedObjectsClient,
asSavedObjectExecutionSource({
type: 'alert',
id,
})
)
).toEqual(false);
).toEqual(AuthorizationMode.RBAC);
});

test('should return false if source alert is marked with a last modified version', async () => {
test('should return RBAC if source alert doesnt have a last modified version', async () => {
const id = uuid.v4();
unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id, attributes: { meta: {} } }));
expect(
await shouldLegacyRbacApplyBySource(
await getAuthorizationModeBySource(
unsecuredSavedObjectsClient,
asSavedObjectExecutionSource({
type: 'alert',
id,
})
)
).toEqual(false);
).toEqual(AuthorizationMode.RBAC);
});
});

Expand Down
Loading

0 comments on commit 420ac24

Please sign in to comment.