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:
Mike Côté 2023-04-17 08:22:13 -04:00 committed by GitHub
parent 99db75512a
commit 046d97c357
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 331 additions and 41 deletions

View file

@ -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 };
},

View file

@ -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(() => {

View file

@ -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 = {

View file

@ -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 };
},

View file

@ -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 };
},

View file

@ -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 = {

View file

@ -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(),
};

View file

@ -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) => {

View file

@ -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;
};

View file

@ -83,6 +83,7 @@ export function getConnectorType(): ESIndexConnectorType {
SecurityConnectorFeatureId,
],
validate: {
secrets: { schema: schema.object({}, { defaultValue: {} }) },
config: {
schema: ConfigSchema,
},

View file

@ -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,
},

View file

@ -79,6 +79,7 @@ export function getConnectorType({
SecurityConnectorFeatureId,
],
validate: {
config: { schema: schema.object({}, { defaultValue: {} }) },
secrets: {
schema: SecretsSchema,
customValidator: validateConnectorTypeConfig,

View file

@ -71,6 +71,7 @@ export function getConnectorType(): TeamsConnectorType {
SecurityConnectorFeatureId,
],
validate: {
config: { schema: schema.object({}, { defaultValue: {} }) },
secrets: {
schema: SecretsSchema,
customValidator: validateConnectorTypeConfig,

View file

@ -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: '' };
},

View file

@ -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 };
},