Skip to content

Commit

Permalink
feat(webhook-whitelisting): extracted the schema for action specific …
Browse files Browse the repository at this point in the history
…Kiabana configs making them an action specific concern
  • Loading branch information
gmmorris committed Aug 28, 2019
1 parent 5be9aa4 commit 91cc23b
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 33 deletions.
18 changes: 9 additions & 9 deletions x-pack/legacy/plugins/actions/server/action_type_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
GetServicesFunction,
SpaceIdToNamespaceFunction,
} from './types';
import { ActionKibanaConfig } from './actions_config';
import { ActionsConfigType } from './actions_config';

interface ConstructorOptions {
isSecurityEnabled: boolean;
Expand All @@ -26,30 +26,30 @@ interface ConstructorOptions {
encryptedSavedObjectsPlugin: EncryptedSavedObjectsPlugin;
spaceIdToNamespace: SpaceIdToNamespaceFunction;
getBasePath: GetBasePathFunction;
actionKibanaConfigurations: Option<Record<string, ActionKibanaConfig>>;
actionKibanaConfigurations: Option<ActionsConfigType>;
}

function getKibanaConfigurationByActionType(
actionType: string,
configs: Option<Record<string, ActionKibanaConfig>>
): Option<ActionKibanaConfig> {
configs: Option<Record<string, any>>
): Option<any> {
return configs.mapNullable(allConfigs => allConfigs[actionType]);
}

function isConfigurable(
actionType: ActionType | ConfigureableActionType
): actionType is ConfigureableActionType {
actionType: ActionType | ConfigureableActionType<any>
): actionType is ConfigureableActionType<any> {
return (
actionType.hasOwnProperty('configure') &&
typeof (actionType as ConfigureableActionType).configure === 'function'
typeof (actionType as ConfigureableActionType<any>).configure === 'function'
);
}

export class ActionTypeRegistry {
private readonly taskRunCreatorFunction: TaskRunCreatorFunction;
private readonly taskManager: TaskManager;
private readonly actionTypes: Map<string, ActionType> = new Map();
private readonly actionKibanaConfigurations: Option<Record<string, ActionKibanaConfig>> = none;
private readonly actionKibanaConfigurations: Option<ActionsConfigType> = none;

constructor({
getServices,
Expand Down Expand Up @@ -82,7 +82,7 @@ export class ActionTypeRegistry {
/**
* Registers an action type to the action type registry
*/
public register(providedActionType: ActionType | ConfigureableActionType) {
public register(providedActionType: ActionType | ConfigureableActionType<any>) {
const actionType = isConfigurable(providedActionType)
? providedActionType.configure(
getKibanaConfigurationByActionType(
Expand Down
9 changes: 1 addition & 8 deletions x-pack/legacy/plugins/actions/server/actions_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,4 @@
import { schema, TypeOf } from '@kbn/config-schema';

export type ActionsConfigType = TypeOf<typeof config>;

export type ActionKibanaConfig = TypeOf<typeof actionConfig>;

export const actionConfig = schema.object({
whitelistedEndpoints: schema.oneOf([schema.arrayOf(schema.string()), schema.literal('any')]),
});

export const config = schema.mapOf(schema.string(), actionConfig);
export const config = schema.mapOf(schema.string(), schema.object({}, { allowUnknowns: true }));
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
*/

import { Option, none, some } from 'fp-ts/lib/Option';
import { actionType, validateConfig as validateWebhookConfig } from './webhook';
import {
actionType,
validateConfig as validateWebhookConfig,
WebhookActionKibanaConfig,
} from './webhook';
import { validateConfig, validateSecrets, validateParams } from '../lib';
import { ActionKibanaConfig } from '../actions_config';

function actionTypeWithDefaults() {
return actionType.configure(
Expand Down Expand Up @@ -140,7 +143,7 @@ describe('config validation', () => {
};

expect(
validateWebhookConfig(kiabanaActionConfig as Option<ActionKibanaConfig>)(config)
validateWebhookConfig(kiabanaActionConfig as Option<WebhookActionKibanaConfig>)(config)
).toMatchInlineSnapshot(
`"an error occurred configuring webhook with unwhitelisted target url \\"http://mylisteningserver:9200/endpoint\\""`
);
Expand All @@ -158,7 +161,7 @@ describe('config validation', () => {
};

expect(
validateWebhookConfig(kiabanaActionConfig as Option<ActionKibanaConfig>)(config)
validateWebhookConfig(kiabanaActionConfig as Option<WebhookActionKibanaConfig>)(config)
).toBeUndefined();
});

Expand All @@ -174,7 +177,7 @@ describe('config validation', () => {
};

expect(
validateWebhookConfig(kiabanaActionConfig as Option<ActionKibanaConfig>)(config)
validateWebhookConfig(kiabanaActionConfig as Option<WebhookActionKibanaConfig>)(config)
).toBeUndefined();
});

Expand All @@ -190,7 +193,7 @@ describe('config validation', () => {
};

expect(
validateWebhookConfig(kiabanaActionConfig as Option<ActionKibanaConfig>)(config)
validateWebhookConfig(kiabanaActionConfig as Option<WebhookActionKibanaConfig>)(config)
).toBeUndefined();
});

Expand All @@ -206,7 +209,7 @@ describe('config validation', () => {
};

expect(
validateWebhookConfig(kiabanaActionConfig as Option<ActionKibanaConfig>)(config)
validateWebhookConfig(kiabanaActionConfig as Option<WebhookActionKibanaConfig>)(config)
).toBeUndefined();
});

Expand All @@ -222,7 +225,7 @@ describe('config validation', () => {
};

expect(
validateWebhookConfig(kiabanaActionConfig as Option<ActionKibanaConfig>)(config)
validateWebhookConfig(kiabanaActionConfig as Option<WebhookActionKibanaConfig>)(config)
).toMatchInlineSnapshot(
`"an error occurred configuring webhook with unwhitelisted target url \\"http://mylisteningserver:9200/endpoint\\""`
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
import { URL } from 'url';
import Boom from 'boom';
import { Option, fromNullable } from 'fp-ts/lib/Option';
import axios, { AxiosError, AxiosResponse } from 'axios';
import { schema, TypeOf } from '@kbn/config-schema';
Expand All @@ -17,7 +18,6 @@ import {
ActionTypeExecutorResult,
ActionType,
} from '../types';
import { ActionKibanaConfig } from '../actions_config';

// config definition
enum WebhookMethods {
Expand All @@ -26,23 +26,37 @@ enum WebhookMethods {
}

const HeadersSchema = schema.recordOf(schema.string(), schema.string());
export type ActionTypeConfigType = TypeOf<typeof UnvalidatedConfigSchema>;
const configSchemaProps = {
url: schema.string(),
method: schema.oneOf([schema.literal(WebhookMethods.POST), schema.literal(WebhookMethods.PUT)], {
defaultValue: WebhookMethods.POST,
}),
headers: nullableType(HeadersSchema),
};
export type ActionTypeConfigType = TypeOf<typeof UnvalidatedConfigSchema>;
const UnvalidatedConfigSchema = schema.object(configSchemaProps);

export type WebhookActionKibanaConfig = TypeOf<typeof ActionTypeConfigSchema>;
const ActionTypeConfigSchema = schema.object({
whitelistedEndpoints: schema.oneOf([schema.arrayOf(schema.string()), schema.literal('any')]),
});

function asArray(list: string | string[]): string[] {
return Array.isArray(list) ? list : [list];
}

export function validateConfig(
kibanaConfig: Option<ActionKibanaConfig>
kibanaConfig: Option<WebhookActionKibanaConfig>
): (configObject: any) => string | void {
try {
kibanaConfig.map(config => ActionTypeConfigSchema.validate(config));
} catch (err) {
throw Boom.badRequest(
`error validating the Webhook Action Kiaban configuration: ${err.message} ${JSON.stringify(
kibanaConfig
)}`
);
}
return configObject => {
const { url }: ActionTypeConfigType = configObject;

Expand Down Expand Up @@ -101,13 +115,13 @@ const ParamsSchema = schema.object({
});

// action type definition
export const actionType: ConfigureableActionType = {
export const actionType: ConfigureableActionType<WebhookActionKibanaConfig> = {
id: '.webhook',
name: 'webhook',
configure: configureActionType,
};

function configureActionType(kibanaConfig: Option<ActionKibanaConfig>): ActionType {
function configureActionType(kibanaConfig: Option<WebhookActionKibanaConfig>): ActionType {
const ConfigSchema = schema.object(configSchemaProps, {
validate: validateConfig(kibanaConfig),
});
Expand Down
5 changes: 2 additions & 3 deletions x-pack/legacy/plugins/actions/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Option } from 'fp-ts/lib/Option';
import { SavedObjectsClientContract, SavedObjectAttributes } from 'src/core/server';
import { ActionTypeRegistry } from './action_type_registry';
import { ExecuteOptions } from './create_execute_function';
import { ActionKibanaConfig } from './actions_config';

export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
export type GetServicesFunction = (request: any) => Services;
Expand Down Expand Up @@ -90,8 +89,8 @@ export interface ActionTypeBehavior {

export interface ActionType extends ActionTypeBehavior, ActionTypeIdentifiers {}

export interface ConfigureableActionType extends ActionTypeIdentifiers {
configure: (config: Option<ActionKibanaConfig>) => ActionType;
export interface ConfigureableActionType<T> extends ActionTypeIdentifiers {
configure: (config: Option<T>) => ActionType;
}

export interface RawAction extends SavedObjectAttributes {
Expand Down

0 comments on commit 91cc23b

Please sign in to comment.