Skip to content

Commit

Permalink
Define minimum license required for each action type (#58668)
Browse files Browse the repository at this point in the history
* Add minimum required license

* Require at least gold license as a minimum license required on third party action types

* Use strings for license references

* Ensure license type is valid

* Fix some tests

* Add servicenow to gold

* Add tests

* Set license requirements on other built in action types

* Use jest.Mocked<ActionType> instead

* Change servicenow to platinum

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
mikecote and elasticmachine authored Mar 2, 2020
1 parent a3be4e2 commit a28e6db
Show file tree
Hide file tree
Showing 16 changed files with 127 additions and 11 deletions.
8 changes: 8 additions & 0 deletions x-pack/plugins/actions/server/action_type_registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('register()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(actionTypeRegistry.has('my-action-type')).toEqual(true);
Expand All @@ -55,12 +56,14 @@ describe('register()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(() =>
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
})
).toThrowErrorMatchingInlineSnapshot(
Expand All @@ -73,6 +76,7 @@ describe('register()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1);
Expand All @@ -94,13 +98,15 @@ describe('get()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
const actionType = actionTypeRegistry.get('my-action-type');
expect(actionType).toMatchInlineSnapshot(`
Object {
"executor": [Function],
"id": "my-action-type",
"minimumLicenseRequired": "basic",
"name": "My action type",
}
`);
Expand All @@ -120,6 +126,7 @@ describe('list()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
const actionTypes = actionTypeRegistry.list();
Expand All @@ -144,6 +151,7 @@ describe('has()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(actionTypeRegistry.has('my-action-type'));
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/actions/server/actions_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
Expand Down Expand Up @@ -100,6 +101,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
validate: {
config: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -140,6 +142,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.create.mockResolvedValueOnce({
Expand Down Expand Up @@ -233,6 +236,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
Expand Down Expand Up @@ -346,6 +350,7 @@ describe('update()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.get.mockResolvedValueOnce({
Expand Down Expand Up @@ -407,6 +412,7 @@ describe('update()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
validate: {
config: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -440,6 +446,7 @@ describe('update()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.get.mockResolvedValueOnce({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function getActionType(params: GetActionTypeParams): ActionType {
const { logger, configurationUtilities } = params;
return {
id: '.email',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.emailTitle', {
defaultMessage: 'Email',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const ParamsSchema = schema.object({
export function getActionType({ logger }: { logger: Logger }): ActionType {
return {
id: '.index',
minimumLicenseRequired: 'basic',
name: i18n.translate('xpack.actions.builtin.esIndexTitle', {
defaultMessage: 'Index',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export function getActionType({
}): ActionType {
return {
id: '.pagerduty',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.pagerdutyTitle', {
defaultMessage: 'PagerDuty',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const ParamsSchema = schema.object({
export function getActionType({ logger }: { logger: Logger }): ActionType {
return {
id: '.server-log',
minimumLicenseRequired: 'basic',
name: i18n.translate('xpack.actions.builtin.serverLogTitle', {
defaultMessage: 'Server log',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export function getActionType({
}): ActionType {
return {
id: '.servicenow',
minimumLicenseRequired: 'platinum',
name: i18n.translate('xpack.actions.builtin.servicenowTitle', {
defaultMessage: 'ServiceNow',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function getActionType({
}): ActionType {
return {
id: '.slack',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.slackTitle', {
defaultMessage: 'Slack',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export function getActionType({
}): ActionType {
return {
id: '.webhook',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.webhookTitle', {
defaultMessage: 'Webhook',
}),
Expand Down
16 changes: 11 additions & 5 deletions x-pack/plugins/actions/server/lib/action_executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/serv
import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
import { eventLoggerMock } from '../../../event_log/server/mocks';
import { spacesServiceMock } from '../../../spaces/server/spaces_service/spaces_service.mock';
import { ActionType } from '../types';

const actionExecutor = new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false });
const savedObjectsClient = savedObjectsClientMock.create();
Expand Down Expand Up @@ -50,9 +51,10 @@ beforeEach(() => {
});

test('successfully executes', async () => {
const actionType = {
const actionType: jest.Mocked<ActionType> = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic',
executor: jest.fn(),
};
const actionSavedObject = {
Expand Down Expand Up @@ -96,9 +98,10 @@ test('successfully executes', async () => {
});

test('provides empty config when config and / or secrets is empty', async () => {
const actionType = {
const actionType: jest.Mocked<ActionType> = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic',
executor: jest.fn(),
};
const actionSavedObject = {
Expand All @@ -120,9 +123,10 @@ test('provides empty config when config and / or secrets is empty', async () =>
});

test('throws an error when config is invalid', async () => {
const actionType = {
const actionType: jest.Mocked<ActionType> = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic',
validate: {
config: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -152,9 +156,10 @@ test('throws an error when config is invalid', async () => {
});

test('throws an error when params is invalid', async () => {
const actionType = {
const actionType: jest.Mocked<ActionType> = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic',
validate: {
params: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -191,9 +196,10 @@ test('throws an error when failing to load action through savedObjectsClient', a
});

test('returns an error if actionType is not enabled', async () => {
const actionType = {
const actionType: jest.Mocked<ActionType> = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic',
executor: jest.fn(),
};
const actionSavedObject = {
Expand Down
19 changes: 17 additions & 2 deletions x-pack/plugins/actions/server/lib/validate_with_schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,26 @@ const executor: ExecutorType = async options => {
};

test('should validate when there are no validators', () => {
const actionType: ActionType = { id: 'foo', name: 'bar', executor };
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
};
const testValue = { any: ['old', 'thing'] };

const result = validateConfig(actionType, testValue);
expect(result).toEqual(testValue);
});

test('should validate when there are no individual validators', () => {
const actionType: ActionType = { id: 'foo', name: 'bar', executor, validate: {} };
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {},
};

let result;
const testValue = { any: ['old', 'thing'] };
Expand All @@ -42,6 +53,7 @@ test('should validate when validators return incoming value', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: selfValidator,
Expand Down Expand Up @@ -69,6 +81,7 @@ test('should validate when validators return different values', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: selfValidator,
Expand Down Expand Up @@ -99,6 +112,7 @@ test('should throw with expected error when validators fail', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: erroringValidator,
Expand Down Expand Up @@ -127,6 +141,7 @@ test('should work with @kbn/config-schema', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: testSchema,
Expand Down
56 changes: 55 additions & 1 deletion x-pack/plugins/actions/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ActionsPlugin, ActionsPluginsSetup, ActionsPluginsStart } from './plugin';
import { PluginInitializerContext } from '../../../../src/core/server';
import { coreMock, httpServerMock } from '../../../../src/core/server/mocks';
import { licensingMock } from '../../licensing/server/mocks';
import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks';
import { taskManagerMock } from '../../task_manager/server/mocks';
import { eventLogMock } from '../../event_log/server/mocks';
import { ActionType } from './types';
import {
ActionsPlugin,
ActionsPluginsSetup,
ActionsPluginsStart,
PluginSetupContract,
} from './plugin';

describe('Actions Plugin', () => {
describe('setup()', () => {
Expand Down Expand Up @@ -90,6 +96,54 @@ describe('Actions Plugin', () => {
);
});
});

describe('registerType()', () => {
let setup: PluginSetupContract;
const sampleActionType: ActionType = {
id: 'test',
name: 'test',
minimumLicenseRequired: 'basic',
async executor() {},
};

beforeEach(async () => {
setup = await plugin.setup(coreSetup, pluginsSetup);
});

it('should throw error when license type is invalid', async () => {
expect(() =>
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'foo' as any,
})
).toThrowErrorMatchingInlineSnapshot(`"\\"foo\\" is not a valid license type"`);
});

it('should throw error when license type is less than gold', async () => {
expect(() =>
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'basic',
})
).toThrowErrorMatchingInlineSnapshot(
`"Third party action type \\"test\\" can only set minimumLicenseRequired to a gold license or higher"`
);
});

it('should not throw when license type is gold', async () => {
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'gold',
});
});

it('should not throw when license type is higher than gold', async () => {
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'platinum',
});
});
});
});
describe('start()', () => {
let plugin: ActionsPlugin;
Expand Down
Loading

0 comments on commit a28e6db

Please sign in to comment.