mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Require config, secrets and params validation for connector types (#153969)
Resolves https://github.com/elastic/kibana/issues/153769 In this PR, I'm making `config`, `secrets` and `params` require a validation in their connector type definition. Even in scenarios no values are used, an empty object validation is required. This prevents arbitrary data from being passed through. Only `ES Index`, `Server Log` and `Teams` had some empty validators. Adding them won't be a breaking change because the HTTP API defaults missing `config` and `secrets` to `{}` ([see here](https://github.com/elastic/kibana/blob/main/x-pack/plugins/actions/server/routes/create.ts#L19-L24)). --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
99db75512a
commit
046d97c357
15 changed files with 331 additions and 41 deletions
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { ActionTypeRegistry, ActionTypeRegistryOpts } from './action_type_registry';
|
||||
import { ActionType, ExecutorType } from './types';
|
||||
import { ActionExecutor, ILicenseState, TaskRunnerFactory } from './lib';
|
||||
|
@ -56,11 +57,16 @@ describe('actionTypeRegistry', () => {
|
|||
describe('register()', () => {
|
||||
test('able to register action types', () => {
|
||||
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
|
||||
actionTypeRegistry.register({
|
||||
actionTypeRegistry.register<{}, {}, {}, void>({
|
||||
id: 'my-action-type',
|
||||
name: 'My action type',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
expect(actionTypeRegistry.has('my-action-type')).toEqual(true);
|
||||
|
@ -88,6 +94,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
};
|
||||
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
|
||||
|
@ -103,6 +114,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
expect(() =>
|
||||
|
@ -111,6 +127,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -126,6 +147,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: [],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -141,6 +167,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['foo'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -155,6 +186,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
expect(actionTypeRegistryParams.licensing.featureUsage.register).toHaveBeenCalledWith(
|
||||
|
@ -170,6 +206,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
expect(actionTypeRegistryParams.licensing.featureUsage.register).not.toHaveBeenCalled();
|
||||
|
@ -184,10 +225,16 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
const actionType = actionTypeRegistry.get('my-action-type');
|
||||
expect(actionType).toMatchInlineSnapshot(`
|
||||
const { validate, ...rest } = actionTypeRegistry.get('my-action-type');
|
||||
expect(validate).toBeDefined();
|
||||
expect(rest).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"executor": [Function],
|
||||
"id": "my-action-type",
|
||||
|
@ -217,6 +264,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
const actionTypes = actionTypeRegistry.list();
|
||||
|
@ -243,6 +295,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
actionTypeRegistry.register({
|
||||
|
@ -250,6 +307,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['cases'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
const actionTypes = actionTypeRegistry.list('alerting');
|
||||
|
@ -282,6 +344,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
expect(actionTypeRegistry.has('my-action-type'));
|
||||
|
@ -295,6 +362,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
@ -363,6 +435,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
@ -409,6 +486,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
@ -451,6 +533,11 @@ describe('actionTypeRegistry', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
|
|
@ -180,6 +180,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
|
||||
|
@ -213,6 +218,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
|
||||
|
@ -254,6 +264,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
|
||||
|
@ -293,6 +308,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
|
||||
|
@ -346,6 +366,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
|
||||
|
@ -391,6 +416,8 @@ describe('create()', () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
config: {
|
||||
schema: schema.object({
|
||||
param1: schema.string(),
|
||||
|
@ -414,11 +441,8 @@ describe('create()', () => {
|
|||
});
|
||||
|
||||
test('validates connector: config and secrets', async () => {
|
||||
const connectorValidator = ({}, secrets: { param1: '1' }) => {
|
||||
if (secrets.param1 == null) {
|
||||
return '[param1] is required';
|
||||
}
|
||||
return null;
|
||||
const connectorValidator = () => {
|
||||
return '[param1] is required';
|
||||
};
|
||||
actionTypeRegistry.register({
|
||||
id: 'my-action-type',
|
||||
|
@ -426,6 +450,9 @@ describe('create()', () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({ param1: schema.string() }) },
|
||||
params: { schema: schema.object({}) },
|
||||
connector: connectorValidator,
|
||||
},
|
||||
executor,
|
||||
|
@ -436,7 +463,7 @@ describe('create()', () => {
|
|||
name: 'my name',
|
||||
actionTypeId: 'my-action-type',
|
||||
config: {},
|
||||
secrets: {},
|
||||
secrets: { param1: '1' },
|
||||
},
|
||||
})
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -465,6 +492,13 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: {
|
||||
schema: schema.object({ a: schema.boolean(), b: schema.boolean(), c: schema.boolean() }),
|
||||
},
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
|
||||
|
@ -594,6 +628,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
|
||||
|
@ -629,6 +668,11 @@ describe('create()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
mockedLicenseState.ensureLicenseForActionType.mockImplementation(() => {
|
||||
|
@ -1725,6 +1769,11 @@ describe('update()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
|
@ -1829,6 +1878,11 @@ describe('update()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
|
@ -1902,6 +1956,11 @@ describe('update()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
|
@ -1969,6 +2028,8 @@ describe('update()', () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
config: {
|
||||
schema: schema.object({
|
||||
param1: schema.string(),
|
||||
|
@ -2006,6 +2067,9 @@ describe('update()', () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
connector: () => {
|
||||
return '[param1] is required';
|
||||
},
|
||||
|
@ -2040,6 +2104,13 @@ describe('update()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: {
|
||||
schema: schema.object({ a: schema.boolean(), b: schema.boolean(), c: schema.boolean() }),
|
||||
},
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
|
@ -2121,6 +2192,11 @@ describe('update()', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
mockedLicenseState.ensureLicenseForActionType.mockImplementation(() => {
|
||||
|
@ -2511,6 +2587,11 @@ describe('isActionTypeEnabled()', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -77,6 +77,11 @@ test('successfully executes', async () => {
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.boolean() }) },
|
||||
secrets: { schema: schema.object({ baz: schema.boolean() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -210,6 +215,11 @@ test('successfully executes when http_request source is specified', async () =>
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.boolean() }) },
|
||||
secrets: { schema: schema.object({ baz: schema.boolean() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -348,6 +358,11 @@ test('successfully executes when saved_object source is specified', async () =>
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.boolean() }) },
|
||||
secrets: { schema: schema.object({ baz: schema.boolean() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -489,6 +504,11 @@ test('successfully executes with preconfigured connector', async () => {
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.string() }) },
|
||||
secrets: { schema: schema.object({ apiKey: schema.string() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
|
||||
|
@ -605,6 +625,11 @@ test('successfully executes as a task', async () => {
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -649,6 +674,11 @@ test('provides empty config when config and / or secrets is empty', async () =>
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -666,7 +696,7 @@ test('provides empty config when config and / or secrets is empty', async () =>
|
|||
|
||||
expect(actionType.executor).toHaveBeenCalledTimes(1);
|
||||
const executorCall = actionType.executor.mock.calls[0][0];
|
||||
expect(executorCall.config).toMatchInlineSnapshot(`undefined`);
|
||||
expect(executorCall.config).toMatchInlineSnapshot(`Object {}`);
|
||||
});
|
||||
|
||||
test('throws an error when config is invalid', async () => {
|
||||
|
@ -676,6 +706,8 @@ test('throws an error when config is invalid', async () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
config: {
|
||||
schema: schema.object({
|
||||
param1: schema.string(),
|
||||
|
@ -712,6 +744,9 @@ test('throws an error when connector is invalid', async () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
secrets: { schema: schema.object({}) },
|
||||
config: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
connector: () => {
|
||||
return 'error';
|
||||
},
|
||||
|
@ -746,6 +781,8 @@ test('throws an error when params is invalid', async () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: {
|
||||
schema: schema.object({
|
||||
param1: schema.string(),
|
||||
|
@ -790,6 +827,11 @@ test('throws an error if actionType is not enabled', async () => {
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -819,6 +861,11 @@ test('should not throws an error if actionType is preconfigured', async () => {
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.boolean() }) },
|
||||
secrets: { schema: schema.object({ baz: schema.boolean() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
@ -907,6 +954,11 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.string() }) },
|
||||
secrets: { schema: schema.object({ apiKey: schema.string() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
|
||||
|
@ -1233,6 +1285,11 @@ function setupActionExecutorMock() {
|
|||
name: 'Test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ bar: schema.boolean() }) },
|
||||
secrets: { schema: schema.object({ baz: schema.boolean() }) },
|
||||
params: { schema: schema.object({ foo: schema.boolean() }) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
const actionSavedObject = {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { ActionType } from '../types';
|
||||
import { ensureSufficientLicense } from './ensure_sufficient_license';
|
||||
|
||||
|
@ -13,6 +14,11 @@ const sampleActionType: ActionType = {
|
|||
name: 'test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
async executor({ actionId }) {
|
||||
return { status: 'ok', actionId };
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { ActionType } from '../types';
|
||||
import { Subject } from 'rxjs';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { LicenseState, ILicenseState } from './license_state';
|
||||
import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
|
||||
import { ILicense } from '@kbn/licensing-plugin/server';
|
||||
|
@ -62,6 +63,11 @@ describe('isLicenseValidForActionType', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
@ -158,6 +164,11 @@ describe('ensureLicenseForActionType()', () => {
|
|||
name: 'Foo',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
|
|
@ -35,6 +35,11 @@ test('should validate when there are no validators', () => {
|
|||
name: 'bar',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({ any: schema.arrayOf(schema.string()) }) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
};
|
||||
const testValue = { any: ['old', 'thing'] };
|
||||
|
@ -43,32 +48,6 @@ test('should validate when there are no validators', () => {
|
|||
expect(result).toEqual(testValue);
|
||||
});
|
||||
|
||||
test('should validate when there are no individual validators', () => {
|
||||
const actionType: ActionType = {
|
||||
id: 'foo',
|
||||
name: 'bar',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
executor,
|
||||
validate: {},
|
||||
};
|
||||
|
||||
let result;
|
||||
const testValue = { any: ['old', 'thing'] };
|
||||
|
||||
result = validateParams(actionType, testValue, { configurationUtilities });
|
||||
expect(result).toEqual(testValue);
|
||||
|
||||
result = validateConfig(actionType, testValue, { configurationUtilities });
|
||||
expect(result).toEqual(testValue);
|
||||
|
||||
result = validateSecrets(actionType, testValue, { configurationUtilities });
|
||||
expect(result).toEqual(testValue);
|
||||
|
||||
result = validateConnector(actionType, { config: testValue });
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
test('should validate when validators return incoming value', () => {
|
||||
const selfValidator = { validate: (value: Record<string, unknown>) => value };
|
||||
const actionType: ActionType = {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { schema, ByteSizeValue } from '@kbn/config-schema';
|
||||
import { PluginInitializerContext, RequestHandlerContext } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks';
|
||||
|
@ -144,6 +144,11 @@ describe('Actions Plugin', () => {
|
|||
name: 'test',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
async executor(options) {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
},
|
||||
|
@ -371,6 +376,11 @@ describe('Actions Plugin', () => {
|
|||
name: 'Server log',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
|
||||
|
@ -390,6 +400,11 @@ describe('Actions Plugin', () => {
|
|||
name: 'ES Index',
|
||||
minimumLicenseRequired: 'basic',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor,
|
||||
});
|
||||
|
||||
|
@ -432,6 +447,11 @@ describe('Actions Plugin', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
|
||||
|
@ -455,6 +475,11 @@ describe('Actions Plugin', () => {
|
|||
name: 'My action type',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
secrets: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
},
|
||||
executor: jest.fn(),
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { transformConnectorsForExport } from './transform_connectors_for_export';
|
||||
import { actionTypeRegistryMock } from '../action_type_registry.mock';
|
||||
import { ActionType, ActionTypeRegistryContract, ActionTypeSecrets } from '../types';
|
||||
|
@ -17,6 +18,8 @@ describe('transform connector for export', () => {
|
|||
supportedFeatureIds: ['alerting'],
|
||||
executor: jest.fn(),
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
secrets: {
|
||||
schema: {
|
||||
validate: (value: unknown) => value as ActionTypeSecrets,
|
||||
|
@ -245,6 +248,8 @@ describe('transform connector for export', () => {
|
|||
actionTypeRegistry.get.mockReturnValue({
|
||||
...connectorType,
|
||||
validate: {
|
||||
config: { schema: schema.object({}) },
|
||||
params: { schema: schema.object({}) },
|
||||
secrets: {
|
||||
schema: {
|
||||
validate: (value: unknown) => {
|
||||
|
|
|
@ -127,10 +127,10 @@ export interface ActionType<
|
|||
maxAttempts?: number;
|
||||
minimumLicenseRequired: LicenseType;
|
||||
supportedFeatureIds: string[];
|
||||
validate?: {
|
||||
params?: ValidatorType<Params>;
|
||||
config?: ValidatorType<Config>;
|
||||
secrets?: ValidatorType<Secrets>;
|
||||
validate: {
|
||||
params: ValidatorType<Params>;
|
||||
config: ValidatorType<Config>;
|
||||
secrets: ValidatorType<Secrets>;
|
||||
connector?: (config: Config, secrets: Secrets) => string | null;
|
||||
};
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ export function getConnectorType(): ESIndexConnectorType {
|
|||
SecurityConnectorFeatureId,
|
||||
],
|
||||
validate: {
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
config: {
|
||||
schema: ConfigSchema,
|
||||
},
|
||||
|
|
|
@ -57,6 +57,8 @@ export function getConnectorType(): ServerLogConnectorType {
|
|||
}),
|
||||
supportedFeatureIds: [AlertingConnectorFeatureId, UptimeConnectorFeatureId],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: {
|
||||
schema: ParamsSchema,
|
||||
},
|
||||
|
|
|
@ -79,6 +79,7 @@ export function getConnectorType({
|
|||
SecurityConnectorFeatureId,
|
||||
],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: {
|
||||
schema: SecretsSchema,
|
||||
customValidator: validateConnectorTypeConfig,
|
||||
|
|
|
@ -71,6 +71,7 @@ export function getConnectorType(): TeamsConnectorType {
|
|||
SecurityConnectorFeatureId,
|
||||
],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: {
|
||||
schema: SecretsSchema,
|
||||
customValidator: validateConnectorTypeConfig,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { Plugin, CoreSetup } from '@kbn/core/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { EncryptedSavedObjectsPluginStart } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import {
|
||||
|
@ -105,6 +106,11 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
name: 'Test: Not Enabled',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
},
|
||||
async executor() {
|
||||
return { status: 'ok', actionId: '' };
|
||||
},
|
||||
|
|
|
@ -24,6 +24,11 @@ export function defineActionTypes(
|
|||
name: 'Test: Noop',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
},
|
||||
async executor() {
|
||||
return { status: 'ok', actionId: '' };
|
||||
},
|
||||
|
@ -34,6 +39,11 @@ export function defineActionTypes(
|
|||
name: 'Test: Throw',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
},
|
||||
async executor() {
|
||||
throw new Error('this action is intended to fail');
|
||||
},
|
||||
|
@ -44,6 +54,11 @@ export function defineActionTypes(
|
|||
name: 'Test: Capped',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
},
|
||||
async executor() {
|
||||
return { status: 'ok', actionId: '' };
|
||||
},
|
||||
|
@ -168,6 +183,8 @@ function getFailingActionType() {
|
|||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: {
|
||||
schema: paramsSchema,
|
||||
},
|
||||
|
@ -204,6 +221,8 @@ function getRateLimitedActionType() {
|
|||
supportedFeatureIds: ['alerting'],
|
||||
maxAttempts: 2,
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: {
|
||||
schema: paramsSchema,
|
||||
},
|
||||
|
@ -243,6 +262,8 @@ function getNoAttemptsRateLimitedActionType() {
|
|||
supportedFeatureIds: ['alerting'],
|
||||
maxAttempts: 0,
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: {
|
||||
schema: paramsSchema,
|
||||
},
|
||||
|
@ -284,6 +305,8 @@ function getAuthorizationActionType(core: CoreSetup<FixtureStartDeps>) {
|
|||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: {
|
||||
schema: paramsSchema,
|
||||
},
|
||||
|
@ -365,6 +388,11 @@ function getExcludedActionType() {
|
|||
name: 'Test: Excluded',
|
||||
minimumLicenseRequired: 'gold',
|
||||
supportedFeatureIds: ['alerting'],
|
||||
validate: {
|
||||
config: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
secrets: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
params: { schema: schema.object({}, { defaultValue: {} }) },
|
||||
},
|
||||
async executor({ actionId }) {
|
||||
return { status: 'ok', actionId };
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue