Skip to content

Commit

Permalink
Merge branch 'main' of github.com:elastic/kibana into feat/enable-esl…
Browse files Browse the repository at this point in the history
…int-rule-for-ai-assistant
  • Loading branch information
CoenWarmer committed Sep 5, 2023
2 parents fd7a1eb + 528884c commit c0cea1c
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 12 deletions.
3 changes: 2 additions & 1 deletion x-pack/plugins/actions/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"usageCollection",
"spaces",
"security",
"monitoringCollection"
"monitoringCollection",
"serverless"
],
"extraPublicDirs": [
"common"
Expand Down
17 changes: 11 additions & 6 deletions x-pack/plugins/actions/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,23 @@ const connectorTypeSchema = schema.object({
maxAttempts: schema.maybe(schema.number({ min: MIN_MAX_ATTEMPTS, max: MAX_MAX_ATTEMPTS })),
});

// We leverage enabledActionTypes list by allowing the other plugins to overwrite it by using "setEnabledConnectorTypes" in the plugin setup.
// The list can be overwritten only if it's not already been set in the config.
const enabledConnectorTypesSchema = schema.arrayOf(
schema.oneOf([schema.string(), schema.literal(EnabledActionTypes.Any)]),
{
defaultValue: [AllowedHosts.Any],
}
);

export const configSchema = schema.object({
allowedHosts: schema.arrayOf(
schema.oneOf([schema.string({ hostname: true }), schema.literal(AllowedHosts.Any)]),
{
defaultValue: [AllowedHosts.Any],
}
),
enabledActionTypes: schema.arrayOf(
schema.oneOf([schema.string(), schema.literal(EnabledActionTypes.Any)]),
{
defaultValue: [AllowedHosts.Any],
}
),
enabledActionTypes: enabledConnectorTypesSchema,
preconfiguredAlertHistoryEsIndex: schema.boolean({ defaultValue: false }),
preconfigured: schema.recordOf(schema.string(), preconfiguredActionSchema, {
defaultValue: {},
Expand Down Expand Up @@ -129,6 +133,7 @@ export const configSchema = schema.object({
});

export type ActionsConfig = TypeOf<typeof configSchema>;
export type EnabledConnectorTypes = TypeOf<typeof enabledConnectorTypesSchema>;

// It would be nicer to add the proxyBypassHosts / proxyOnlyHosts restriction on
// simultaneous usage in the config validator directly, but there's no good way to express
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const createSetupMock = () => {
getCaseConnectorClass: jest.fn(),
getActionsHealth: jest.fn(),
getActionsConfigurationUtilities: jest.fn(),
setEnabledConnectorTypes: jest.fn(),
};
return mock;
};
Expand Down
189 changes: 189 additions & 0 deletions x-pack/plugins/actions/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,163 @@ describe('Actions Plugin', () => {
expect(pluginSetup.isPreconfiguredConnector('anotherConnectorId')).toEqual(false);
});
});

describe('setEnabledConnectorTypes (works only on serverless)', () => {
function setup(config: ActionsConfig) {
context = coreMock.createPluginInitializerContext<ActionsConfig>(config);
plugin = new ActionsPlugin(context);
coreSetup = coreMock.createSetup();
pluginsSetup = {
taskManager: taskManagerMock.createSetup(),
encryptedSavedObjects: encryptedSavedObjectsMock.createSetup(),
licensing: licensingMock.createSetup(),
eventLog: eventLogMock.createSetup(),
usageCollection: usageCollectionPluginMock.createSetupContract(),
features: featuresPluginMock.createSetup(),
serverless: {},
};
}

it('should set connector type enabled', async () => {
setup(getConfig());
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);
const coreStart = coreMock.createStart();
const pluginsStart = {
licensing: licensingMock.createStart(),
taskManager: taskManagerMock.createStart(),
encryptedSavedObjects: encryptedSavedObjectsMock.createStart(),
eventLog: eventLogMock.createStart(),
};
const pluginStart = plugin.start(coreStart, pluginsStart);

pluginSetup.registerType({
id: '.server-log',
name: 'Server log',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});
pluginSetup.registerType({
id: '.slack',
name: 'Slack',
minimumLicenseRequired: 'gold',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});
pluginSetup.setEnabledConnectorTypes(['.server-log']);
expect(pluginStart.isActionTypeEnabled('.server-log')).toBeTruthy();
expect(pluginStart.isActionTypeEnabled('.slack')).toBeFalsy();
});

it('should set all the connector types enabled when null or ["*"] passed', async () => {
setup(getConfig());
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);
const coreStart = coreMock.createStart();
const pluginsStart = {
licensing: licensingMock.createStart(),
taskManager: taskManagerMock.createStart(),
encryptedSavedObjects: encryptedSavedObjectsMock.createStart(),
eventLog: eventLogMock.createStart(),
};
const pluginStart = plugin.start(coreStart, pluginsStart);

pluginSetup.registerType({
id: '.server-log',
name: 'Server log',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});
pluginSetup.registerType({
id: '.index',
name: 'Index',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});
pluginSetup.setEnabledConnectorTypes(['*']);
expect(pluginStart.isActionTypeEnabled('.server-log')).toBeTruthy();
expect(pluginStart.isActionTypeEnabled('.index')).toBeTruthy();
});

it('should set all the connector types disabled when [] passed', async () => {
setup(getConfig());
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);
const coreStart = coreMock.createStart();
const pluginsStart = {
licensing: licensingMock.createStart(),
taskManager: taskManagerMock.createStart(),
encryptedSavedObjects: encryptedSavedObjectsMock.createStart(),
eventLog: eventLogMock.createStart(),
};
const pluginStart = plugin.start(coreStart, pluginsStart);

pluginSetup.registerType({
id: '.server-log',
name: 'Server log',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});
pluginSetup.registerType({
id: '.index',
name: 'Index',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});
pluginSetup.setEnabledConnectorTypes([]);
expect(pluginStart.isActionTypeEnabled('.server-log')).toBeFalsy();
expect(pluginStart.isActionTypeEnabled('.index')).toBeFalsy();
});

it('should throw if the enabledActionTypes is already set by the config', async () => {
setup({ ...getConfig(), enabledActionTypes: ['.email'] });
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);

expect(() => pluginSetup.setEnabledConnectorTypes(['.index'])).toThrow(
"Enabled connector types can be set only if they haven't already been set in the config"
);
});
});
});

describe('start()', () => {
Expand Down Expand Up @@ -396,6 +553,38 @@ describe('Actions Plugin', () => {
};
});

it('should throw when there is an invalid connector type in enabledActionTypes', async () => {
const pluginSetup = await plugin.setup(coreSetup, {
...pluginsSetup,
encryptedSavedObjects: {
...pluginsSetup.encryptedSavedObjects,
canEncrypt: true,
},
serverless: {},
});

pluginSetup.registerType({
id: '.server-log',
name: 'Server log',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});

pluginSetup.setEnabledConnectorTypes(['.server-log', 'non-existing']);

await expect(async () =>
plugin.start(coreStart, { ...pluginsStart, serverless: {} })
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Action type \\"non-existing\\" is not registered."`
);
});

describe('getActionsClientWithRequest()', () => {
it('should not throw error when ESO plugin has encryption key', async () => {
await plugin.setup(coreSetup, {
Expand Down
41 changes: 36 additions & 5 deletions x-pack/plugins/actions/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ import {
} from '@kbn/event-log-plugin/server';
import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server';

import { ActionsConfig, getValidatedConfig } from './config';
import { ServerlessPluginSetup } from '@kbn/serverless/server';
import { ActionsConfig, AllowedHosts, EnabledConnectorTypes, getValidatedConfig } from './config';
import { resolveCustomHosts } from './lib/custom_host_settings';
import { ActionsClient } from './actions_client/actions_client';
import { ActionTypeRegistry } from './action_type_registry';
Expand Down Expand Up @@ -100,10 +101,8 @@ import { createSubActionConnectorFramework } from './sub_action_framework';
import { IServiceAbstract, SubActionConnectorType } from './sub_action_framework/types';
import { SubActionConnector } from './sub_action_framework/sub_action_connector';
import { CaseConnector } from './sub_action_framework/case';
import {
type IUnsecuredActionsClient,
UnsecuredActionsClient,
} from './unsecured_actions_client/unsecured_actions_client';
import type { IUnsecuredActionsClient } from './unsecured_actions_client/unsecured_actions_client';
import { UnsecuredActionsClient } from './unsecured_actions_client/unsecured_actions_client';
import { createBulkUnsecuredExecutionEnqueuerFunction } from './create_unsecured_execute_function';
import { createSystemConnectors } from './create_system_actions';

Expand All @@ -130,6 +129,7 @@ export interface PluginSetupContract {
getCaseConnectorClass: <Config, Secrets>() => IServiceAbstract<Config, Secrets>;
getActionsHealth: () => { hasPermanentEncryptionKey: boolean };
getActionsConfigurationUtilities: () => ActionsConfigurationUtilities;
setEnabledConnectorTypes: (connectorTypes: EnabledConnectorTypes) => void;
}

export interface PluginStartContract {
Expand Down Expand Up @@ -169,6 +169,7 @@ export interface ActionsPluginsSetup {
features: FeaturesPluginSetup;
spaces?: SpacesPluginSetup;
monitoringCollection?: MonitoringCollectionSetup;
serverless?: ServerlessPluginSetup;
}

export interface ActionsPluginsStart {
Expand All @@ -178,6 +179,7 @@ export interface ActionsPluginsStart {
eventLog: IEventLogClientService;
spaces?: SpacesPluginStart;
security?: SecurityPluginStart;
serverless?: ServerlessPluginSetup;
}

const includedHiddenTypes = [
Expand Down Expand Up @@ -375,6 +377,20 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
};
},
getActionsConfigurationUtilities: () => actionsConfigUtils,
setEnabledConnectorTypes: (connectorTypes) => {
if (
!!plugins.serverless &&
this.actionsConfig.enabledActionTypes.length === 1 &&
this.actionsConfig.enabledActionTypes[0] === AllowedHosts.Any
) {
this.actionsConfig.enabledActionTypes.pop();
this.actionsConfig.enabledActionTypes.push(...connectorTypes);
} else {
throw new Error(
"Enabled connector types can be set only if they haven't already been set in the config"
);
}
},
};
}

Expand Down Expand Up @@ -542,6 +558,8 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
});
}

this.validateEnabledConnectorTypes(plugins);

return {
isActionTypeEnabled: (id, options = { notifyUsage: false }) => {
return this.actionTypeRegistry!.isActionTypeEnabled(id, options);
Expand Down Expand Up @@ -695,6 +713,19 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
};
};

private validateEnabledConnectorTypes = (plugins: ActionsPluginsStart) => {
if (
!!plugins.serverless &&
this.actionsConfig.enabledActionTypes.length > 0 &&
this.actionsConfig.enabledActionTypes[0] !== AllowedHosts.Any
) {
this.actionsConfig.enabledActionTypes.forEach((connectorType) => {
// Throws error if action type doesn't exist
this.actionTypeRegistry?.get(connectorType);
});
}
};

public stop() {
if (this.licenseState) {
this.licenseState.clean();
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@kbn/core-saved-objects-api-server-mocks",
"@kbn/core-elasticsearch-server-mocks",
"@kbn/core-logging-server-mocks",
"@kbn/serverless"
],
"exclude": [
"target/**/*",
Expand Down

0 comments on commit c0cea1c

Please sign in to comment.