Make xpack.actions.rejectUnauthorized
setting work (#88690)
* Remove ActionsConfigType due to being a duplicate * Fix rejectUnauthorized not being configured * Move proxySettings to configurationUtilities * Fix isAxiosError check to code * Add functional test * Remove comment * Close webhook server Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
074003d4b4
commit
da8ce374cf
|
@ -402,6 +402,9 @@ describe('create()', () => {
|
|||
enabled: true,
|
||||
enabledActionTypes: ['some-not-ignored-action-type'],
|
||||
allowedHosts: ['*'],
|
||||
preconfigured: {},
|
||||
proxyRejectUnauthorizedCertificates: true,
|
||||
rejectUnauthorized: true,
|
||||
});
|
||||
|
||||
const localActionTypeRegistryParams = {
|
||||
|
|
|
@ -14,6 +14,8 @@ const createActionsConfigMock = () => {
|
|||
ensureHostnameAllowed: jest.fn().mockReturnValue({}),
|
||||
ensureUriAllowed: jest.fn().mockReturnValue({}),
|
||||
ensureActionTypeEnabled: jest.fn().mockReturnValue({}),
|
||||
isRejectUnauthorizedCertificatesEnabled: jest.fn().mockReturnValue(true),
|
||||
getProxySettings: jest.fn().mockReturnValue(undefined),
|
||||
};
|
||||
return mocked;
|
||||
};
|
||||
|
|
|
@ -4,22 +4,26 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ActionsConfigType } from './types';
|
||||
import { ActionsConfig } from './config';
|
||||
import {
|
||||
getActionsConfigurationUtilities,
|
||||
AllowedHosts,
|
||||
EnabledActionTypes,
|
||||
} from './actions_config';
|
||||
|
||||
const DefaultActionsConfig: ActionsConfigType = {
|
||||
const defaultActionsConfig: ActionsConfig = {
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: [],
|
||||
preconfigured: {},
|
||||
proxyRejectUnauthorizedCertificates: true,
|
||||
rejectUnauthorized: true,
|
||||
};
|
||||
|
||||
describe('ensureUriAllowed', () => {
|
||||
test('returns true when "any" hostnames are allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [AllowedHosts.Any],
|
||||
enabledActionTypes: [],
|
||||
|
@ -30,7 +34,7 @@ describe('ensureUriAllowed', () => {
|
|||
});
|
||||
|
||||
test('throws when the hostname in the requested uri is not in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(() =>
|
||||
getActionsConfigurationUtilities(config).ensureUriAllowed('https://github.com/elastic/kibana')
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -39,7 +43,7 @@ describe('ensureUriAllowed', () => {
|
|||
});
|
||||
|
||||
test('throws when the uri cannot be parsed as a valid URI', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(() =>
|
||||
getActionsConfigurationUtilities(config).ensureUriAllowed('github.com/elastic')
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -48,7 +52,8 @@ describe('ensureUriAllowed', () => {
|
|||
});
|
||||
|
||||
test('returns true when the hostname in the requested uri is in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: ['github.com'],
|
||||
enabledActionTypes: [],
|
||||
|
@ -61,7 +66,8 @@ describe('ensureUriAllowed', () => {
|
|||
|
||||
describe('ensureHostnameAllowed', () => {
|
||||
test('returns true when "any" hostnames are allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [AllowedHosts.Any],
|
||||
enabledActionTypes: [],
|
||||
|
@ -72,7 +78,7 @@ describe('ensureHostnameAllowed', () => {
|
|||
});
|
||||
|
||||
test('throws when the hostname in the requested uri is not in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(() =>
|
||||
getActionsConfigurationUtilities(config).ensureHostnameAllowed('github.com')
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -81,7 +87,8 @@ describe('ensureHostnameAllowed', () => {
|
|||
});
|
||||
|
||||
test('returns true when the hostname in the requested uri is in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: ['github.com'],
|
||||
enabledActionTypes: [],
|
||||
|
@ -94,7 +101,8 @@ describe('ensureHostnameAllowed', () => {
|
|||
|
||||
describe('isUriAllowed', () => {
|
||||
test('returns true when "any" hostnames are allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [AllowedHosts.Any],
|
||||
enabledActionTypes: [],
|
||||
|
@ -105,21 +113,22 @@ describe('isUriAllowed', () => {
|
|||
});
|
||||
|
||||
test('throws when the hostname in the requested uri is not in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(
|
||||
getActionsConfigurationUtilities(config).isUriAllowed('https://github.com/elastic/kibana')
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('throws when the uri cannot be parsed as a valid URI', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(getActionsConfigurationUtilities(config).isUriAllowed('github.com/elastic')).toEqual(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test('returns true when the hostname in the requested uri is in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: ['github.com'],
|
||||
enabledActionTypes: [],
|
||||
|
@ -132,7 +141,8 @@ describe('isUriAllowed', () => {
|
|||
|
||||
describe('isHostnameAllowed', () => {
|
||||
test('returns true when "any" hostnames are allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [AllowedHosts.Any],
|
||||
enabledActionTypes: [],
|
||||
|
@ -141,12 +151,13 @@ describe('isHostnameAllowed', () => {
|
|||
});
|
||||
|
||||
test('throws when the hostname in the requested uri is not in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(getActionsConfigurationUtilities(config).isHostnameAllowed('github.com')).toEqual(false);
|
||||
});
|
||||
|
||||
test('returns true when the hostname in the requested uri is in the allowedHosts', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: ['github.com'],
|
||||
enabledActionTypes: [],
|
||||
|
@ -157,7 +168,8 @@ describe('isHostnameAllowed', () => {
|
|||
|
||||
describe('isActionTypeEnabled', () => {
|
||||
test('returns true when "any" actionTypes are allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: ['ignore', EnabledActionTypes.Any],
|
||||
|
@ -166,7 +178,8 @@ describe('isActionTypeEnabled', () => {
|
|||
});
|
||||
|
||||
test('returns false when no actionType is allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: [],
|
||||
|
@ -175,7 +188,8 @@ describe('isActionTypeEnabled', () => {
|
|||
});
|
||||
|
||||
test('returns false when the actionType is not in the enabled list', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: ['foo'],
|
||||
|
@ -184,7 +198,8 @@ describe('isActionTypeEnabled', () => {
|
|||
});
|
||||
|
||||
test('returns true when the actionType is in the enabled list', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: ['ignore', 'foo'],
|
||||
|
@ -195,7 +210,8 @@ describe('isActionTypeEnabled', () => {
|
|||
|
||||
describe('ensureActionTypeEnabled', () => {
|
||||
test('does not throw when any actionType is allowed', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: ['ignore', EnabledActionTypes.Any],
|
||||
|
@ -204,7 +220,7 @@ describe('ensureActionTypeEnabled', () => {
|
|||
});
|
||||
|
||||
test('throws when no actionType is not allowed', () => {
|
||||
const config: ActionsConfigType = DefaultActionsConfig;
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
expect(() =>
|
||||
getActionsConfigurationUtilities(config).ensureActionTypeEnabled('foo')
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -213,7 +229,8 @@ describe('ensureActionTypeEnabled', () => {
|
|||
});
|
||||
|
||||
test('throws when actionType is not enabled', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: ['ignore'],
|
||||
|
@ -226,7 +243,8 @@ describe('ensureActionTypeEnabled', () => {
|
|||
});
|
||||
|
||||
test('does not throw when actionType is enabled', () => {
|
||||
const config: ActionsConfigType = {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
enabled: false,
|
||||
allowedHosts: [],
|
||||
enabledActionTypes: ['ignore', 'foo'],
|
||||
|
|
|
@ -10,8 +10,9 @@ import url from 'url';
|
|||
import { curry } from 'lodash';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
import { ActionsConfigType } from './types';
|
||||
import { ActionsConfig } from './config';
|
||||
import { ActionTypeDisabledError } from './lib';
|
||||
import { ProxySettings } from './types';
|
||||
|
||||
export enum AllowedHosts {
|
||||
Any = '*',
|
||||
|
@ -33,6 +34,8 @@ export interface ActionsConfigurationUtilities {
|
|||
ensureHostnameAllowed: (hostname: string) => void;
|
||||
ensureUriAllowed: (uri: string) => void;
|
||||
ensureActionTypeEnabled: (actionType: string) => void;
|
||||
isRejectUnauthorizedCertificatesEnabled: () => boolean;
|
||||
getProxySettings: () => undefined | ProxySettings;
|
||||
}
|
||||
|
||||
function allowListErrorMessage(field: AllowListingField, value: string) {
|
||||
|
@ -56,14 +59,14 @@ function disabledActionTypeErrorMessage(actionType: string) {
|
|||
});
|
||||
}
|
||||
|
||||
function isAllowed({ allowedHosts }: ActionsConfigType, hostname: string | null): boolean {
|
||||
function isAllowed({ allowedHosts }: ActionsConfig, hostname: string | null): boolean {
|
||||
const allowed = new Set(allowedHosts);
|
||||
if (allowed.has(AllowedHosts.Any)) return true;
|
||||
if (hostname && allowed.has(hostname)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function isHostnameAllowedInUri(config: ActionsConfigType, uri: string): boolean {
|
||||
function isHostnameAllowedInUri(config: ActionsConfig, uri: string): boolean {
|
||||
return pipe(
|
||||
tryCatch(() => url.parse(uri)),
|
||||
map((parsedUrl) => parsedUrl.hostname),
|
||||
|
@ -73,7 +76,7 @@ function isHostnameAllowedInUri(config: ActionsConfigType, uri: string): boolean
|
|||
}
|
||||
|
||||
function isActionTypeEnabledInConfig(
|
||||
{ enabledActionTypes }: ActionsConfigType,
|
||||
{ enabledActionTypes }: ActionsConfig,
|
||||
actionType: string
|
||||
): boolean {
|
||||
const enabled = new Set(enabledActionTypes);
|
||||
|
@ -82,8 +85,20 @@ function isActionTypeEnabledInConfig(
|
|||
return false;
|
||||
}
|
||||
|
||||
function getProxySettingsFromConfig(config: ActionsConfig): undefined | ProxySettings {
|
||||
if (!config.proxyUrl) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
proxyUrl: config.proxyUrl,
|
||||
proxyHeaders: config.proxyHeaders,
|
||||
proxyRejectUnauthorizedCertificates: config.proxyRejectUnauthorizedCertificates,
|
||||
};
|
||||
}
|
||||
|
||||
export function getActionsConfigurationUtilities(
|
||||
config: ActionsConfigType
|
||||
config: ActionsConfig
|
||||
): ActionsConfigurationUtilities {
|
||||
const isHostnameAllowed = curry(isAllowed)(config);
|
||||
const isUriAllowed = curry(isHostnameAllowedInUri)(config);
|
||||
|
@ -92,6 +107,8 @@ export function getActionsConfigurationUtilities(
|
|||
isHostnameAllowed,
|
||||
isUriAllowed,
|
||||
isActionTypeEnabled,
|
||||
getProxySettings: () => getProxySettingsFromConfig(config),
|
||||
isRejectUnauthorizedCertificatesEnabled: () => config.rejectUnauthorized,
|
||||
ensureUriAllowed(uri: string) {
|
||||
if (!isUriAllowed(uri)) {
|
||||
throw new Error(allowListErrorMessage(AllowListingField.URL, uri));
|
||||
|
|
|
@ -277,6 +277,16 @@ describe('execute()', () => {
|
|||
`);
|
||||
expect(sendEmailMock.mock.calls[0][1]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"configurationUtilities": Object {
|
||||
"ensureActionTypeEnabled": [MockFunction],
|
||||
"ensureHostnameAllowed": [MockFunction],
|
||||
"ensureUriAllowed": [MockFunction],
|
||||
"getProxySettings": [MockFunction],
|
||||
"isActionTypeEnabled": [MockFunction],
|
||||
"isHostnameAllowed": [MockFunction],
|
||||
"isRejectUnauthorizedCertificatesEnabled": [MockFunction],
|
||||
"isUriAllowed": [MockFunction],
|
||||
},
|
||||
"content": Object {
|
||||
"message": "a message to you
|
||||
|
||||
|
@ -286,7 +296,6 @@ describe('execute()', () => {
|
|||
"subject": "the subject",
|
||||
},
|
||||
"hasAuth": true,
|
||||
"proxySettings": undefined,
|
||||
"routing": Object {
|
||||
"bcc": Array [
|
||||
"jimmy@example.com",
|
||||
|
@ -327,6 +336,16 @@ describe('execute()', () => {
|
|||
await actionType.executor(customExecutorOptions);
|
||||
expect(sendEmailMock.mock.calls[0][1]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"configurationUtilities": Object {
|
||||
"ensureActionTypeEnabled": [MockFunction],
|
||||
"ensureHostnameAllowed": [MockFunction],
|
||||
"ensureUriAllowed": [MockFunction],
|
||||
"getProxySettings": [MockFunction],
|
||||
"isActionTypeEnabled": [MockFunction],
|
||||
"isHostnameAllowed": [MockFunction],
|
||||
"isRejectUnauthorizedCertificatesEnabled": [MockFunction],
|
||||
"isUriAllowed": [MockFunction],
|
||||
},
|
||||
"content": Object {
|
||||
"message": "a message to you
|
||||
|
||||
|
@ -336,7 +355,6 @@ describe('execute()', () => {
|
|||
"subject": "the subject",
|
||||
},
|
||||
"hasAuth": false,
|
||||
"proxySettings": undefined,
|
||||
"routing": Object {
|
||||
"bcc": Array [
|
||||
"jimmy@example.com",
|
||||
|
|
|
@ -156,7 +156,7 @@ export function getActionType(params: GetActionTypeParams): EmailActionType {
|
|||
params: ParamsSchema,
|
||||
},
|
||||
renderParameterTemplates,
|
||||
executor: curry(executor)({ logger, publicBaseUrl }),
|
||||
executor: curry(executor)({ logger, publicBaseUrl, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,12 @@ async function executor(
|
|||
{
|
||||
logger,
|
||||
publicBaseUrl,
|
||||
}: { logger: GetActionTypeParams['logger']; publicBaseUrl: GetActionTypeParams['publicBaseUrl'] },
|
||||
configurationUtilities,
|
||||
}: {
|
||||
logger: GetActionTypeParams['logger'];
|
||||
publicBaseUrl: GetActionTypeParams['publicBaseUrl'];
|
||||
configurationUtilities: ActionsConfigurationUtilities;
|
||||
},
|
||||
execOptions: EmailActionTypeExecutorOptions
|
||||
): Promise<ActionTypeExecutorResult<unknown>> {
|
||||
const actionId = execOptions.actionId;
|
||||
|
@ -221,8 +226,8 @@ async function executor(
|
|||
subject: params.subject,
|
||||
message: `${params.message}${EMAIL_FOOTER_DIVIDER}${footerMessage}`,
|
||||
},
|
||||
proxySettings: execOptions.proxySettings,
|
||||
hasAuth: config.hasAuth,
|
||||
configurationUtilities,
|
||||
};
|
||||
|
||||
let result;
|
||||
|
|
|
@ -72,13 +72,16 @@ export function getActionType(
|
|||
}),
|
||||
params: ExecutorParamsSchema,
|
||||
},
|
||||
executor: curry(executor)({ logger }),
|
||||
executor: curry(executor)({ logger, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
// action executor
|
||||
async function executor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: ActionTypeExecutorOptions<
|
||||
JiraPublicConfigurationType,
|
||||
JiraSecretConfigurationType,
|
||||
|
@ -95,7 +98,7 @@ async function executor(
|
|||
secrets,
|
||||
},
|
||||
logger,
|
||||
execOptions.proxySettings
|
||||
configurationUtilities
|
||||
);
|
||||
|
||||
if (!api[subAction]) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as utils from '../lib/axios_utils';
|
|||
import { ExternalService } from './types';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
interface ResponseError extends Error {
|
||||
|
@ -28,6 +29,7 @@ jest.mock('../lib/axios_utils', () => {
|
|||
|
||||
axios.create = jest.fn(() => axios);
|
||||
const requestMock = utils.request as jest.Mock;
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
|
||||
const issueTypesResponse = {
|
||||
data: {
|
||||
|
@ -116,7 +118,8 @@ describe('Jira service', () => {
|
|||
config: { apiUrl: 'https://siem-kibana.atlassian.net/', projectKey: 'CK' },
|
||||
secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -132,7 +135,8 @@ describe('Jira service', () => {
|
|||
config: { apiUrl: null, projectKey: 'CK' },
|
||||
secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -144,7 +148,8 @@ describe('Jira service', () => {
|
|||
config: { apiUrl: 'test.com', projectKey: null },
|
||||
secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -156,7 +161,8 @@ describe('Jira service', () => {
|
|||
config: { apiUrl: 'test.com' },
|
||||
secrets: { apiToken: '', email: 'elastic@elastic.com' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -168,7 +174,8 @@ describe('Jira service', () => {
|
|||
config: { apiUrl: 'test.com' },
|
||||
secrets: { apiToken: '', email: undefined },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -193,6 +200,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1',
|
||||
logger,
|
||||
configurationUtilities,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -293,6 +301,7 @@ describe('Jira service', () => {
|
|||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue',
|
||||
logger,
|
||||
method: 'post',
|
||||
configurationUtilities,
|
||||
data: {
|
||||
fields: {
|
||||
summary: 'title',
|
||||
|
@ -331,6 +340,7 @@ describe('Jira service', () => {
|
|||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue',
|
||||
logger,
|
||||
method: 'post',
|
||||
configurationUtilities,
|
||||
data: {
|
||||
fields: {
|
||||
summary: 'title',
|
||||
|
@ -424,6 +434,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'put',
|
||||
configurationUtilities,
|
||||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1',
|
||||
data: {
|
||||
fields: {
|
||||
|
@ -510,6 +521,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'post',
|
||||
configurationUtilities,
|
||||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1/comment',
|
||||
data: { body: 'comment' },
|
||||
});
|
||||
|
@ -568,6 +580,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url: 'https://siem-kibana.atlassian.net/rest/capabilities',
|
||||
});
|
||||
});
|
||||
|
@ -642,6 +655,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url:
|
||||
'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta?projectKeys=CK&expand=projects.issuetypes.fields',
|
||||
});
|
||||
|
@ -724,6 +738,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta/CK/issuetypes',
|
||||
});
|
||||
});
|
||||
|
@ -807,6 +822,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url:
|
||||
'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta?projectKeys=CK&issuetypeIds=10006&expand=projects.issuetypes.fields',
|
||||
});
|
||||
|
@ -928,6 +944,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/createmeta/CK/issuetypes/10006',
|
||||
});
|
||||
});
|
||||
|
@ -988,6 +1005,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url: `https://siem-kibana.atlassian.net/rest/api/2/search?jql=project%3D%22CK%22%20and%20summary%20~%22Test%20title%22`,
|
||||
});
|
||||
});
|
||||
|
@ -1032,6 +1050,7 @@ describe('Jira service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'get',
|
||||
configurationUtilities,
|
||||
url: `https://siem-kibana.atlassian.net/rest/api/2/issue/RJ-107`,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ import {
|
|||
|
||||
import * as i18n from './translations';
|
||||
import { request, getErrorMessage } from '../lib/axios_utils';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
const VERSION = '2';
|
||||
const BASE_URL = `rest/api/${VERSION}`;
|
||||
|
@ -39,7 +39,7 @@ const createMetaCapabilities = ['list-project-issuetypes', 'list-issuetype-field
|
|||
export const createExternalService = (
|
||||
{ config, secrets }: ExternalServiceCredentials,
|
||||
logger: Logger,
|
||||
proxySettings?: ProxySettings
|
||||
configurationUtilities: ActionsConfigurationUtilities
|
||||
): ExternalService => {
|
||||
const { apiUrl: url, projectKey } = config as JiraPublicConfigurationType;
|
||||
const { apiToken, email } = secrets as JiraSecretConfigurationType;
|
||||
|
@ -173,7 +173,7 @@ export const createExternalService = (
|
|||
axios: axiosInstance,
|
||||
url: `${incidentUrl}/${id}`,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const { fields, ...rest } = res.data;
|
||||
|
@ -222,7 +222,7 @@ export const createExternalService = (
|
|||
data: {
|
||||
fields,
|
||||
},
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const updatedIncident = await getIncident(res.data.id);
|
||||
|
@ -263,7 +263,7 @@ export const createExternalService = (
|
|||
url: `${incidentUrl}/${incidentId}`,
|
||||
logger,
|
||||
data: { fields },
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const updatedIncident = await getIncident(incidentId as string);
|
||||
|
@ -297,7 +297,7 @@ export const createExternalService = (
|
|||
url: getCommentsURL(incidentId),
|
||||
logger,
|
||||
data: { body: comment.comment },
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -324,7 +324,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: capabilitiesUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return { ...res.data };
|
||||
|
@ -350,7 +350,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: getIssueTypesOldAPIURL,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const issueTypes = res.data.projects[0]?.issuetypes ?? [];
|
||||
|
@ -361,7 +361,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: getIssueTypesUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const issueTypes = res.data.values;
|
||||
|
@ -389,7 +389,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: createGetIssueTypeFieldsUrl(getIssueTypeFieldsOldAPIURL, issueTypeId),
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const fields = res.data.projects[0]?.issuetypes[0]?.fields || {};
|
||||
|
@ -400,7 +400,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: createGetIssueTypeFieldsUrl(getIssueTypeFieldsUrl, issueTypeId),
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const fields = res.data.values.reduce(
|
||||
|
@ -459,7 +459,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: query,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return normalizeSearchResults(res.data?.issues ?? []);
|
||||
|
@ -483,7 +483,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: getIssueUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return normalizeIssue(res.data ?? {});
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { addTimeZoneToDate, request, patch, getErrorMessage } from './axios_utils';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
import { getProxyAgents } from './get_proxy_agents';
|
||||
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
jest.mock('axios');
|
||||
const axiosMock = (axios as unknown) as jest.Mock;
|
||||
|
||||
|
@ -41,13 +44,14 @@ describe('request', () => {
|
|||
axios,
|
||||
url: '/test',
|
||||
logger,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
expect(axiosMock).toHaveBeenCalledWith('/test', {
|
||||
method: 'get',
|
||||
data: {},
|
||||
httpAgent: undefined,
|
||||
httpsAgent: undefined,
|
||||
httpsAgent: expect.any(HttpsAgent),
|
||||
proxy: false,
|
||||
});
|
||||
expect(res).toEqual({
|
||||
|
@ -58,20 +62,17 @@ describe('request', () => {
|
|||
});
|
||||
|
||||
test('it have been called with proper proxy agent for a valid url', async () => {
|
||||
const proxySettings = {
|
||||
configurationUtilities.getProxySettings.mockReturnValue({
|
||||
proxyRejectUnauthorizedCertificates: true,
|
||||
proxyUrl: 'https://localhost:1212',
|
||||
};
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(proxySettings, logger);
|
||||
});
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(configurationUtilities, logger);
|
||||
|
||||
const res = await request({
|
||||
axios,
|
||||
url: 'http://testProxy',
|
||||
logger,
|
||||
proxySettings: {
|
||||
proxyUrl: 'https://localhost:1212',
|
||||
proxyRejectUnauthorizedCertificates: true,
|
||||
},
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
expect(axiosMock).toHaveBeenCalledWith('http://testProxy', {
|
||||
|
@ -89,21 +90,22 @@ describe('request', () => {
|
|||
});
|
||||
|
||||
test('it have been called with proper proxy agent for an invalid url', async () => {
|
||||
configurationUtilities.getProxySettings.mockReturnValue({
|
||||
proxyUrl: ':nope:',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
});
|
||||
const res = await request({
|
||||
axios,
|
||||
url: 'https://testProxy',
|
||||
logger,
|
||||
proxySettings: {
|
||||
proxyUrl: ':nope:',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
},
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
expect(axiosMock).toHaveBeenCalledWith('https://testProxy', {
|
||||
method: 'get',
|
||||
data: {},
|
||||
httpAgent: undefined,
|
||||
httpsAgent: undefined,
|
||||
httpsAgent: expect.any(HttpsAgent),
|
||||
proxy: false,
|
||||
});
|
||||
expect(res).toEqual({
|
||||
|
@ -114,13 +116,20 @@ describe('request', () => {
|
|||
});
|
||||
|
||||
test('it fetch correctly', async () => {
|
||||
const res = await request({ axios, url: '/test', method: 'post', logger, data: { id: '123' } });
|
||||
const res = await request({
|
||||
axios,
|
||||
url: '/test',
|
||||
method: 'post',
|
||||
logger,
|
||||
data: { id: '123' },
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
expect(axiosMock).toHaveBeenCalledWith('/test', {
|
||||
method: 'post',
|
||||
data: { id: '123' },
|
||||
httpAgent: undefined,
|
||||
httpsAgent: undefined,
|
||||
httpsAgent: expect.any(HttpsAgent),
|
||||
proxy: false,
|
||||
});
|
||||
expect(res).toEqual({
|
||||
|
@ -140,12 +149,12 @@ describe('patch', () => {
|
|||
});
|
||||
|
||||
test('it fetch correctly', async () => {
|
||||
await patch({ axios, url: '/test', data: { id: '123' }, logger });
|
||||
await patch({ axios, url: '/test', data: { id: '123' }, logger, configurationUtilities });
|
||||
expect(axiosMock).toHaveBeenCalledWith('/test', {
|
||||
method: 'patch',
|
||||
data: { id: '123' },
|
||||
httpAgent: undefined,
|
||||
httpsAgent: undefined,
|
||||
httpsAgent: expect.any(HttpsAgent),
|
||||
proxy: false,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { AxiosInstance, Method, AxiosResponse, AxiosBasicCredentials } from 'axios';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { getProxyAgents } from './get_proxy_agents';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
export const request = async <T = unknown>({
|
||||
axios,
|
||||
|
@ -15,7 +15,7 @@ export const request = async <T = unknown>({
|
|||
logger,
|
||||
method = 'get',
|
||||
data,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
...rest
|
||||
}: {
|
||||
axios: AxiosInstance;
|
||||
|
@ -24,12 +24,12 @@ export const request = async <T = unknown>({
|
|||
method?: Method;
|
||||
data?: T;
|
||||
params?: unknown;
|
||||
proxySettings?: ProxySettings;
|
||||
configurationUtilities: ActionsConfigurationUtilities;
|
||||
headers?: Record<string, string> | null;
|
||||
validateStatus?: (status: number) => boolean;
|
||||
auth?: AxiosBasicCredentials;
|
||||
}): Promise<AxiosResponse> => {
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(proxySettings, logger);
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(configurationUtilities, logger);
|
||||
|
||||
return await axios(url, {
|
||||
...rest,
|
||||
|
@ -47,13 +47,13 @@ export const patch = async <T = unknown>({
|
|||
url,
|
||||
data,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
}: {
|
||||
axios: AxiosInstance;
|
||||
url: string;
|
||||
data: T;
|
||||
logger: Logger;
|
||||
proxySettings?: ProxySettings;
|
||||
configurationUtilities: ActionsConfigurationUtilities;
|
||||
}): Promise<AxiosResponse> => {
|
||||
return request({
|
||||
axios,
|
||||
|
@ -61,7 +61,7 @@ export const patch = async <T = unknown>({
|
|||
logger,
|
||||
method: 'patch',
|
||||
data,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -4,41 +4,41 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
import HttpProxyAgent from 'http-proxy-agent';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { getProxyAgents } from './get_proxy_agents';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
describe('getProxyAgents', () => {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
|
||||
test('get agents for valid proxy URL', () => {
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(
|
||||
{ proxyUrl: 'https://someproxyhost', proxyRejectUnauthorizedCertificates: false },
|
||||
logger
|
||||
);
|
||||
configurationUtilities.getProxySettings.mockReturnValue({
|
||||
proxyUrl: 'https://someproxyhost',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
});
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(configurationUtilities, logger);
|
||||
expect(httpAgent instanceof HttpProxyAgent).toBeTruthy();
|
||||
expect(httpsAgent instanceof HttpsProxyAgent).toBeTruthy();
|
||||
});
|
||||
|
||||
test('return undefined agents for invalid proxy URL', () => {
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(
|
||||
{ proxyUrl: ':nope: not a valid URL', proxyRejectUnauthorizedCertificates: false },
|
||||
logger
|
||||
);
|
||||
test('return default agents for invalid proxy URL', () => {
|
||||
configurationUtilities.getProxySettings.mockReturnValue({
|
||||
proxyUrl: ':nope: not a valid URL',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
});
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(configurationUtilities, logger);
|
||||
expect(httpAgent).toBe(undefined);
|
||||
expect(httpsAgent).toBe(undefined);
|
||||
expect(httpsAgent instanceof HttpsAgent).toBeTruthy();
|
||||
});
|
||||
|
||||
test('return undefined agents for null proxy options', () => {
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(null, logger);
|
||||
test('return default agents for undefined proxy options', () => {
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(configurationUtilities, logger);
|
||||
expect(httpAgent).toBe(undefined);
|
||||
expect(httpsAgent).toBe(undefined);
|
||||
});
|
||||
|
||||
test('return undefined agents for undefined proxy options', () => {
|
||||
const { httpAgent, httpsAgent } = getProxyAgents(undefined, logger);
|
||||
expect(httpAgent).toBe(undefined);
|
||||
expect(httpsAgent).toBe(undefined);
|
||||
expect(httpsAgent instanceof HttpsAgent).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,28 +4,32 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Agent } from 'http';
|
||||
import { Agent as HttpAgent } from 'http';
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
import HttpProxyAgent from 'http-proxy-agent';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
interface GetProxyAgentsResponse {
|
||||
httpAgent: Agent | undefined;
|
||||
httpsAgent: Agent | undefined;
|
||||
httpAgent: HttpAgent | undefined;
|
||||
httpsAgent: HttpsAgent | undefined;
|
||||
}
|
||||
|
||||
export function getProxyAgents(
|
||||
proxySettings: ProxySettings | undefined | null,
|
||||
configurationUtilities: ActionsConfigurationUtilities,
|
||||
logger: Logger
|
||||
): GetProxyAgentsResponse {
|
||||
const undefinedResponse = {
|
||||
const proxySettings = configurationUtilities.getProxySettings();
|
||||
const defaultResponse = {
|
||||
httpAgent: undefined,
|
||||
httpsAgent: undefined,
|
||||
httpsAgent: new HttpsAgent({
|
||||
rejectUnauthorized: configurationUtilities.isRejectUnauthorizedCertificatesEnabled(),
|
||||
}),
|
||||
};
|
||||
|
||||
if (!proxySettings) {
|
||||
return undefinedResponse;
|
||||
return defaultResponse;
|
||||
}
|
||||
|
||||
logger.debug(`Creating proxy agents for proxy: ${proxySettings.proxyUrl}`);
|
||||
|
@ -34,7 +38,7 @@ export function getProxyAgents(
|
|||
proxyUrl = new URL(proxySettings.proxyUrl);
|
||||
} catch (err) {
|
||||
logger.warn(`invalid proxy URL "${proxySettings.proxyUrl}" ignored`);
|
||||
return undefinedResponse;
|
||||
return defaultResponse;
|
||||
}
|
||||
|
||||
const httpAgent = new HttpProxyAgent(proxySettings.proxyUrl);
|
||||
|
@ -45,8 +49,8 @@ export function getProxyAgents(
|
|||
headers: proxySettings.proxyHeaders,
|
||||
// do not fail on invalid certs if value is false
|
||||
rejectUnauthorized: proxySettings.proxyRejectUnauthorizedCertificates,
|
||||
}) as unknown) as Agent;
|
||||
// vsCode wasn't convinced HttpsProxyAgent is an http.Agent, so we convinced it
|
||||
}) as unknown) as HttpsAgent;
|
||||
// vsCode wasn't convinced HttpsProxyAgent is an https.Agent, so we convinced it
|
||||
|
||||
return { httpAgent, httpsAgent };
|
||||
}
|
||||
|
|
|
@ -6,23 +6,24 @@
|
|||
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { Services, ProxySettings } from '../../types';
|
||||
import { Services } from '../../types';
|
||||
import { request } from './axios_utils';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
interface PostPagerdutyOptions {
|
||||
apiUrl: string;
|
||||
data: unknown;
|
||||
headers: Record<string, string>;
|
||||
services: Services;
|
||||
proxySettings?: ProxySettings;
|
||||
}
|
||||
|
||||
// post an event to pagerduty
|
||||
export async function postPagerduty(
|
||||
options: PostPagerdutyOptions,
|
||||
logger: Logger
|
||||
logger: Logger,
|
||||
configurationUtilities: ActionsConfigurationUtilities
|
||||
): Promise<AxiosResponse> {
|
||||
const { apiUrl, data, headers, proxySettings } = options;
|
||||
const { apiUrl, data, headers } = options;
|
||||
const axiosInstance = axios.create();
|
||||
|
||||
return await request({
|
||||
|
@ -31,8 +32,8 @@ export async function postPagerduty(
|
|||
method: 'post',
|
||||
logger,
|
||||
data,
|
||||
proxySettings,
|
||||
headers,
|
||||
configurationUtilities,
|
||||
validateStatus: () => true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { sendEmail } from './send_email';
|
|||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
|
||||
const createTransportMock = nodemailer.createTransport as jest.Mock;
|
||||
const sendMailMockResult = { result: 'does not matter' };
|
||||
|
@ -136,7 +137,7 @@ describe('send_email module', () => {
|
|||
"port": 1025,
|
||||
"secure": false,
|
||||
"tls": Object {
|
||||
"rejectUnauthorized": undefined,
|
||||
"rejectUnauthorized": true,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -223,6 +224,10 @@ function getSendEmailOptions(
|
|||
{ content = {}, routing = {}, transport = {} } = {},
|
||||
proxySettings?: ProxySettings
|
||||
) {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
if (proxySettings) {
|
||||
configurationUtilities.getProxySettings.mockReturnValue(proxySettings);
|
||||
}
|
||||
return {
|
||||
content: {
|
||||
...content,
|
||||
|
@ -242,8 +247,8 @@ function getSendEmailOptions(
|
|||
user: 'elastic',
|
||||
password: 'changeme',
|
||||
},
|
||||
proxySettings,
|
||||
hasAuth: true,
|
||||
configurationUtilities,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -251,6 +256,10 @@ function getSendEmailOptionsNoAuth(
|
|||
{ content = {}, routing = {}, transport = {} } = {},
|
||||
proxySettings?: ProxySettings
|
||||
) {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
if (proxySettings) {
|
||||
configurationUtilities.getProxySettings.mockReturnValue(proxySettings);
|
||||
}
|
||||
return {
|
||||
content: {
|
||||
...content,
|
||||
|
@ -267,7 +276,7 @@ function getSendEmailOptionsNoAuth(
|
|||
transport: {
|
||||
...transport,
|
||||
},
|
||||
proxySettings,
|
||||
hasAuth: false,
|
||||
configurationUtilities,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import nodemailer from 'nodemailer';
|
|||
import { default as MarkdownIt } from 'markdown-it';
|
||||
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
// an email "service" which doesn't actually send, just returns what it would send
|
||||
export const JSON_TRANSPORT_SERVICE = '__json';
|
||||
|
@ -18,9 +18,8 @@ export interface SendEmailOptions {
|
|||
transport: Transport;
|
||||
routing: Routing;
|
||||
content: Content;
|
||||
proxySettings?: ProxySettings;
|
||||
rejectUnauthorized?: boolean;
|
||||
hasAuth: boolean;
|
||||
configurationUtilities: ActionsConfigurationUtilities;
|
||||
}
|
||||
|
||||
// config validation ensures either service is set or host/port are set
|
||||
|
@ -47,12 +46,14 @@ export interface Content {
|
|||
|
||||
// send an email
|
||||
export async function sendEmail(logger: Logger, options: SendEmailOptions): Promise<unknown> {
|
||||
const { transport, routing, content, proxySettings, rejectUnauthorized, hasAuth } = options;
|
||||
const { transport, routing, content, configurationUtilities, hasAuth } = options;
|
||||
const { service, host, port, secure, user, password } = transport;
|
||||
const { from, to, cc, bcc } = routing;
|
||||
const { subject, message } = content;
|
||||
|
||||
const transportConfig: Record<string, unknown> = {};
|
||||
const proxySettings = configurationUtilities.getProxySettings();
|
||||
const rejectUnauthorized = configurationUtilities.isRejectUnauthorizedCertificatesEnabled();
|
||||
|
||||
if (hasAuth && user != null && password != null) {
|
||||
transportConfig.auth = {
|
||||
|
|
|
@ -139,7 +139,7 @@ export function getActionType({
|
|||
secrets: SecretsSchema,
|
||||
params: ParamsSchema,
|
||||
},
|
||||
executor: curry(executor)({ logger }),
|
||||
executor: curry(executor)({ logger, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,10 @@ function getPagerDutyApiUrl(config: ActionTypeConfigType): string {
|
|||
// action executor
|
||||
|
||||
async function executor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: PagerDutyActionTypeExecutorOptions
|
||||
): Promise<ActionTypeExecutorResult<unknown>> {
|
||||
const actionId = execOptions.actionId;
|
||||
|
@ -174,7 +177,6 @@ async function executor(
|
|||
const secrets = execOptions.secrets;
|
||||
const params = execOptions.params;
|
||||
const services = execOptions.services;
|
||||
const proxySettings = execOptions.proxySettings;
|
||||
|
||||
const apiUrl = getPagerDutyApiUrl(config);
|
||||
const headers = {
|
||||
|
@ -185,7 +187,11 @@ async function executor(
|
|||
|
||||
let response;
|
||||
try {
|
||||
response = await postPagerduty({ apiUrl, data, headers, services, proxySettings }, logger);
|
||||
response = await postPagerduty(
|
||||
{ apiUrl, data, headers, services },
|
||||
logger,
|
||||
configurationUtilities
|
||||
);
|
||||
} catch (err) {
|
||||
const message = i18n.translate('xpack.actions.builtin.pagerduty.postingErrorMessage', {
|
||||
defaultMessage: 'error posting pagerduty event',
|
||||
|
|
|
@ -63,13 +63,16 @@ export function getActionType(
|
|||
}),
|
||||
params: ExecutorParamsSchema,
|
||||
},
|
||||
executor: curry(executor)({ logger }),
|
||||
executor: curry(executor)({ logger, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
// action executor
|
||||
async function executor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: ActionTypeExecutorOptions<
|
||||
ResilientPublicConfigurationType,
|
||||
ResilientSecretConfigurationType,
|
||||
|
@ -86,7 +89,7 @@ async function executor(
|
|||
secrets,
|
||||
},
|
||||
logger,
|
||||
execOptions.proxySettings
|
||||
configurationUtilities
|
||||
);
|
||||
|
||||
if (!api[subAction]) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import { ExternalService } from './types';
|
|||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { incidentTypes, resilientFields, severity } from './mocks';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
|
@ -28,6 +29,7 @@ axios.create = jest.fn(() => axios);
|
|||
const requestMock = utils.request as jest.Mock;
|
||||
const now = Date.now;
|
||||
const TIMESTAMP = 1589391874472;
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
|
||||
// Incident update makes three calls to the API.
|
||||
// The function below mocks this calls.
|
||||
|
@ -86,7 +88,8 @@ describe('IBM Resilient service', () => {
|
|||
config: { apiUrl: 'https://resilient.elastic.co/', orgId: '201' },
|
||||
secrets: { apiKeyId: 'keyId', apiKeySecret: 'secret' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -155,7 +158,8 @@ describe('IBM Resilient service', () => {
|
|||
config: { apiUrl: null, orgId: '201' },
|
||||
secrets: { apiKeyId: 'token', apiKeySecret: 'secret' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -167,7 +171,8 @@ describe('IBM Resilient service', () => {
|
|||
config: { apiUrl: 'test.com', orgId: null },
|
||||
secrets: { apiKeyId: 'token', apiKeySecret: 'secret' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -179,7 +184,8 @@ describe('IBM Resilient service', () => {
|
|||
config: { apiUrl: 'test.com', orgId: '201' },
|
||||
secrets: { apiKeyId: '', apiKeySecret: 'secret' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -191,7 +197,8 @@ describe('IBM Resilient service', () => {
|
|||
config: { apiUrl: 'test.com', orgId: '201' },
|
||||
secrets: { apiKeyId: '', apiKeySecret: undefined },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -226,6 +233,7 @@ describe('IBM Resilient service', () => {
|
|||
params: {
|
||||
text_content_output_format: 'objects_convert',
|
||||
},
|
||||
configurationUtilities,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -294,6 +302,7 @@ describe('IBM Resilient service', () => {
|
|||
'https://resilient.elastic.co/rest/orgs/201/incidents?text_content_output_format=objects_convert',
|
||||
logger,
|
||||
method: 'post',
|
||||
configurationUtilities,
|
||||
data: {
|
||||
name: 'title',
|
||||
description: {
|
||||
|
@ -367,6 +376,7 @@ describe('IBM Resilient service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'patch',
|
||||
configurationUtilities,
|
||||
url: 'https://resilient.elastic.co/rest/orgs/201/incidents/1',
|
||||
data: {
|
||||
changes: [
|
||||
|
@ -480,7 +490,7 @@ describe('IBM Resilient service', () => {
|
|||
axios,
|
||||
logger,
|
||||
method: 'post',
|
||||
proxySettings: undefined,
|
||||
configurationUtilities,
|
||||
url: 'https://resilient.elastic.co/rest/orgs/201/incidents/1/comments',
|
||||
data: {
|
||||
text: {
|
||||
|
@ -584,6 +594,7 @@ describe('IBM Resilient service', () => {
|
|||
expect(requestMock).toHaveBeenCalledWith({
|
||||
axios,
|
||||
logger,
|
||||
configurationUtilities,
|
||||
url: 'https://resilient.elastic.co/rest/orgs/201/types/incident/fields',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
|
||||
import * as i18n from './translations';
|
||||
import { getErrorMessage, request } from '../lib/axios_utils';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
const VIEW_INCIDENT_URL = `#incidents`;
|
||||
|
||||
|
@ -93,7 +93,7 @@ export const formatUpdateRequest = ({
|
|||
export const createExternalService = (
|
||||
{ config, secrets }: ExternalServiceCredentials,
|
||||
logger: Logger,
|
||||
proxySettings?: ProxySettings
|
||||
configurationUtilities: ActionsConfigurationUtilities
|
||||
): ExternalService => {
|
||||
const { apiUrl: url, orgId } = config as ResilientPublicConfigurationType;
|
||||
const { apiKeyId, apiKeySecret } = secrets as ResilientSecretConfigurationType;
|
||||
|
@ -130,7 +130,7 @@ export const createExternalService = (
|
|||
params: {
|
||||
text_content_output_format: 'objects_convert',
|
||||
},
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return { ...res.data, description: res.data.description?.content ?? '' };
|
||||
|
@ -178,7 +178,7 @@ export const createExternalService = (
|
|||
method: 'post',
|
||||
logger,
|
||||
data,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -208,7 +208,7 @@ export const createExternalService = (
|
|||
url: `${incidentUrl}/${incidentId}`,
|
||||
logger,
|
||||
data,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
if (!res.data.success) {
|
||||
|
@ -241,7 +241,7 @@ export const createExternalService = (
|
|||
url: getCommentsURL(incidentId),
|
||||
logger,
|
||||
data: { text: { format: 'text', content: comment.comment } },
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -266,7 +266,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: incidentTypesUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const incidentTypes = res.data?.values ?? [];
|
||||
|
@ -288,7 +288,7 @@ export const createExternalService = (
|
|||
method: 'get',
|
||||
url: severityUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
|
||||
const incidentTypes = res.data?.values ?? [];
|
||||
|
@ -309,7 +309,7 @@ export const createExternalService = (
|
|||
axios: axiosInstance,
|
||||
url: incidentFieldsUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
return res.data ?? [];
|
||||
} catch (error) {
|
||||
|
|
|
@ -60,14 +60,17 @@ export function getActionType(
|
|||
}),
|
||||
params: ExecutorParamsSchema,
|
||||
},
|
||||
executor: curry(executor)({ logger }),
|
||||
executor: curry(executor)({ logger, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
// action executor
|
||||
const supportedSubActions: string[] = ['getFields', 'pushToService'];
|
||||
async function executor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: ActionTypeExecutorOptions<
|
||||
ServiceNowPublicConfigurationType,
|
||||
ServiceNowSecretConfigurationType,
|
||||
|
@ -84,7 +87,7 @@ async function executor(
|
|||
secrets,
|
||||
},
|
||||
logger,
|
||||
execOptions.proxySettings
|
||||
configurationUtilities
|
||||
);
|
||||
|
||||
if (!api[subAction]) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as utils from '../lib/axios_utils';
|
|||
import { ExternalService } from './types';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
import { serviceNowCommonFields } from './mocks';
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
|
@ -27,6 +28,7 @@ jest.mock('../lib/axios_utils', () => {
|
|||
axios.create = jest.fn(() => axios);
|
||||
const requestMock = utils.request as jest.Mock;
|
||||
const patchMock = utils.patch as jest.Mock;
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
|
||||
describe('ServiceNow service', () => {
|
||||
let service: ExternalService;
|
||||
|
@ -39,7 +41,8 @@ describe('ServiceNow service', () => {
|
|||
config: { apiUrl: 'https://dev102283.service-now.com/' },
|
||||
secrets: { username: 'admin', password: 'admin' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -55,7 +58,8 @@ describe('ServiceNow service', () => {
|
|||
config: { apiUrl: null },
|
||||
secrets: { username: 'admin', password: 'admin' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -67,7 +71,8 @@ describe('ServiceNow service', () => {
|
|||
config: { apiUrl: 'test.com' },
|
||||
secrets: { username: '', password: 'admin' },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -79,7 +84,8 @@ describe('ServiceNow service', () => {
|
|||
config: { apiUrl: 'test.com' },
|
||||
secrets: { username: '', password: undefined },
|
||||
},
|
||||
logger
|
||||
logger,
|
||||
configurationUtilities
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
|
@ -103,6 +109,7 @@ describe('ServiceNow service', () => {
|
|||
expect(requestMock).toHaveBeenCalledWith({
|
||||
axios,
|
||||
logger,
|
||||
configurationUtilities,
|
||||
url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1',
|
||||
});
|
||||
});
|
||||
|
@ -147,6 +154,7 @@ describe('ServiceNow service', () => {
|
|||
expect(requestMock).toHaveBeenCalledWith({
|
||||
axios,
|
||||
logger,
|
||||
configurationUtilities,
|
||||
url: 'https://dev102283.service-now.com/api/now/v2/table/incident',
|
||||
method: 'post',
|
||||
data: { short_description: 'title', description: 'desc' },
|
||||
|
@ -200,6 +208,7 @@ describe('ServiceNow service', () => {
|
|||
expect(patchMock).toHaveBeenCalledWith({
|
||||
axios,
|
||||
logger,
|
||||
configurationUtilities,
|
||||
url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1',
|
||||
data: { short_description: 'title', description: 'desc' },
|
||||
});
|
||||
|
@ -248,6 +257,7 @@ describe('ServiceNow service', () => {
|
|||
expect(requestMock).toHaveBeenCalledWith({
|
||||
axios,
|
||||
logger,
|
||||
configurationUtilities,
|
||||
url:
|
||||
'https://dev102283.service-now.com/api/now/v2/table/sys_dictionary?sysparm_query=name=task^internal_type=string&active=true&array=false&read_only=false&sysparm_fields=max_length,element,column_label,mandatory',
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import * as i18n from './translations';
|
|||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { ServiceNowPublicConfigurationType, ServiceNowSecretConfigurationType } from './types';
|
||||
import { request, getErrorMessage, addTimeZoneToDate, patch } from '../lib/axios_utils';
|
||||
import { ProxySettings } from '../../types';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
|
||||
const API_VERSION = 'v2';
|
||||
const INCIDENT_URL = `api/now/${API_VERSION}/table/incident`;
|
||||
|
@ -24,7 +24,7 @@ const VIEW_INCIDENT_URL = `nav_to.do?uri=incident.do?sys_id=`;
|
|||
export const createExternalService = (
|
||||
{ config, secrets }: ExternalServiceCredentials,
|
||||
logger: Logger,
|
||||
proxySettings?: ProxySettings
|
||||
configurationUtilities: ActionsConfigurationUtilities
|
||||
): ExternalService => {
|
||||
const { apiUrl: url } = config as ServiceNowPublicConfigurationType;
|
||||
const { username, password } = secrets as ServiceNowSecretConfigurationType;
|
||||
|
@ -58,7 +58,7 @@ export const createExternalService = (
|
|||
axios: axiosInstance,
|
||||
url: `${incidentUrl}/${id}`,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
checkInstance(res);
|
||||
return { ...res.data.result };
|
||||
|
@ -75,8 +75,8 @@ export const createExternalService = (
|
|||
axios: axiosInstance,
|
||||
url: incidentUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
params,
|
||||
configurationUtilities,
|
||||
});
|
||||
checkInstance(res);
|
||||
return res.data.result.length > 0 ? { ...res.data.result } : undefined;
|
||||
|
@ -93,9 +93,9 @@ export const createExternalService = (
|
|||
axios: axiosInstance,
|
||||
url: `${incidentUrl}`,
|
||||
logger,
|
||||
proxySettings,
|
||||
method: 'post',
|
||||
data: { ...(incident as Record<string, unknown>) },
|
||||
configurationUtilities,
|
||||
});
|
||||
checkInstance(res);
|
||||
return {
|
||||
|
@ -118,7 +118,7 @@ export const createExternalService = (
|
|||
url: `${incidentUrl}/${incidentId}`,
|
||||
logger,
|
||||
data: { ...(incident as Record<string, unknown>) },
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
checkInstance(res);
|
||||
return {
|
||||
|
@ -143,7 +143,7 @@ export const createExternalService = (
|
|||
axios: axiosInstance,
|
||||
url: fieldsUrl,
|
||||
logger,
|
||||
proxySettings,
|
||||
configurationUtilities,
|
||||
});
|
||||
checkInstance(res);
|
||||
return res.data.result.length > 0 ? res.data.result : [];
|
||||
|
|
|
@ -165,10 +165,6 @@ describe('execute()', () => {
|
|||
config: {},
|
||||
secrets: { webhookUrl: 'http://example.com' },
|
||||
params: { message: 'this invocation should succeed' },
|
||||
proxySettings: {
|
||||
proxyUrl: 'https://someproxyhost',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
},
|
||||
});
|
||||
expect(response).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -194,9 +190,14 @@ describe('execute()', () => {
|
|||
});
|
||||
|
||||
test('calls the mock executor with success proxy', async () => {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
configurationUtilities.getProxySettings.mockReturnValue({
|
||||
proxyUrl: 'https://someproxyhost',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
});
|
||||
const actionTypeProxy = getActionType({
|
||||
logger: mockedLogger,
|
||||
configurationUtilities: actionsConfigMock.create(),
|
||||
configurationUtilities,
|
||||
});
|
||||
await actionTypeProxy.executor({
|
||||
actionId: 'some-id',
|
||||
|
@ -204,10 +205,6 @@ describe('execute()', () => {
|
|||
config: {},
|
||||
secrets: { webhookUrl: 'http://example.com' },
|
||||
params: { message: 'this invocation should succeed' },
|
||||
proxySettings: {
|
||||
proxyUrl: 'https://someproxyhost',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
},
|
||||
});
|
||||
expect(mockedLogger.debug).toHaveBeenCalledWith(
|
||||
'IncomingWebhook was called with proxyUrl https://someproxyhost'
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { URL } from 'url';
|
||||
import { curry } from 'lodash';
|
||||
import { Agent } from 'http';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { IncomingWebhook, IncomingWebhookResult } from '@slack/webhook';
|
||||
|
@ -56,7 +55,7 @@ export const ActionTypeId = '.slack';
|
|||
export function getActionType({
|
||||
logger,
|
||||
configurationUtilities,
|
||||
executor = curry(slackExecutor)({ logger }),
|
||||
executor = curry(slackExecutor)({ logger, configurationUtilities }),
|
||||
}: {
|
||||
logger: Logger;
|
||||
configurationUtilities: ActionsConfigurationUtilities;
|
||||
|
@ -116,7 +115,10 @@ function validateActionTypeConfig(
|
|||
// action executor
|
||||
|
||||
async function slackExecutor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: SlackActionTypeExecutorOptions
|
||||
): Promise<ActionTypeExecutorResult<unknown>> {
|
||||
const actionId = execOptions.actionId;
|
||||
|
@ -126,15 +128,15 @@ async function slackExecutor(
|
|||
let result: IncomingWebhookResult;
|
||||
const { webhookUrl } = secrets;
|
||||
const { message } = params;
|
||||
const proxySettings = configurationUtilities.getProxySettings();
|
||||
|
||||
let httpProxyAgent: Agent | undefined;
|
||||
if (execOptions.proxySettings) {
|
||||
const httpProxyAgents = getProxyAgents(execOptions.proxySettings, logger);
|
||||
httpProxyAgent = webhookUrl.toLowerCase().startsWith('https')
|
||||
? httpProxyAgents.httpsAgent
|
||||
: httpProxyAgents.httpAgent;
|
||||
const proxyAgents = getProxyAgents(configurationUtilities, logger);
|
||||
const httpProxyAgent = webhookUrl.toLowerCase().startsWith('https')
|
||||
? proxyAgents.httpsAgent
|
||||
: proxyAgents.httpAgent;
|
||||
|
||||
logger.debug(`IncomingWebhook was called with proxyUrl ${execOptions.proxySettings.proxyUrl}`);
|
||||
if (proxySettings) {
|
||||
logger.debug(`IncomingWebhook was called with proxyUrl ${proxySettings.proxyUrl}`);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -160,38 +160,47 @@ describe('execute()', () => {
|
|||
params: { message: 'this invocation should succeed' },
|
||||
});
|
||||
expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"axios": undefined,
|
||||
"data": Object {
|
||||
"text": "this invocation should succeed",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from teams action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"axios": undefined,
|
||||
"configurationUtilities": Object {
|
||||
"ensureActionTypeEnabled": [MockFunction],
|
||||
"ensureHostnameAllowed": [MockFunction],
|
||||
"ensureUriAllowed": [MockFunction],
|
||||
"getProxySettings": [MockFunction],
|
||||
"isActionTypeEnabled": [MockFunction],
|
||||
"isHostnameAllowed": [MockFunction],
|
||||
"isRejectUnauthorizedCertificatesEnabled": [MockFunction],
|
||||
"isUriAllowed": [MockFunction],
|
||||
},
|
||||
"data": Object {
|
||||
"text": "this invocation should succeed",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from teams action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"proxySettings": undefined,
|
||||
"url": "http://example.com",
|
||||
}
|
||||
],
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"url": "http://example.com",
|
||||
}
|
||||
`);
|
||||
expect(response).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -211,47 +220,49 @@ describe('execute()', () => {
|
|||
config: {},
|
||||
secrets: { webhookUrl: 'http://example.com' },
|
||||
params: { message: 'this invocation should succeed' },
|
||||
proxySettings: {
|
||||
proxyUrl: 'https://someproxyhost',
|
||||
proxyRejectUnauthorizedCertificates: false,
|
||||
},
|
||||
});
|
||||
expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"axios": undefined,
|
||||
"data": Object {
|
||||
"text": "this invocation should succeed",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from teams action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"axios": undefined,
|
||||
"configurationUtilities": Object {
|
||||
"ensureActionTypeEnabled": [MockFunction],
|
||||
"ensureHostnameAllowed": [MockFunction],
|
||||
"ensureUriAllowed": [MockFunction],
|
||||
"getProxySettings": [MockFunction],
|
||||
"isActionTypeEnabled": [MockFunction],
|
||||
"isHostnameAllowed": [MockFunction],
|
||||
"isRejectUnauthorizedCertificatesEnabled": [MockFunction],
|
||||
"isUriAllowed": [MockFunction],
|
||||
},
|
||||
"data": Object {
|
||||
"text": "this invocation should succeed",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from teams action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"proxySettings": Object {
|
||||
"proxyRejectUnauthorizedCertificates": false,
|
||||
"proxyUrl": "https://someproxyhost",
|
||||
},
|
||||
"url": "http://example.com",
|
||||
}
|
||||
],
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"url": "http://example.com",
|
||||
}
|
||||
`);
|
||||
expect(response).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
|
|
@ -63,7 +63,7 @@ export function getActionType({
|
|||
}),
|
||||
params: ParamsSchema,
|
||||
},
|
||||
executor: curry(teamsExecutor)({ logger }),
|
||||
executor: curry(teamsExecutor)({ logger, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,10 @@ function validateActionTypeConfig(
|
|||
// action executor
|
||||
|
||||
async function teamsExecutor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: TeamsActionTypeExecutorOptions
|
||||
): Promise<ActionTypeExecutorResult<unknown>> {
|
||||
const actionId = execOptions.actionId;
|
||||
|
@ -114,7 +117,7 @@ async function teamsExecutor(
|
|||
url: webhookUrl,
|
||||
logger,
|
||||
data,
|
||||
proxySettings: execOptions.proxySettings,
|
||||
configurationUtilities,
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -279,43 +279,52 @@ describe('execute()', () => {
|
|||
});
|
||||
|
||||
expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"auth": Object {
|
||||
"password": "123",
|
||||
"username": "abc",
|
||||
},
|
||||
"axios": undefined,
|
||||
"data": "some data",
|
||||
"headers": Object {
|
||||
"aheader": "a value",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from webhook action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"auth": Object {
|
||||
"password": "123",
|
||||
"username": "abc",
|
||||
},
|
||||
"axios": undefined,
|
||||
"configurationUtilities": Object {
|
||||
"ensureActionTypeEnabled": [MockFunction],
|
||||
"ensureHostnameAllowed": [MockFunction],
|
||||
"ensureUriAllowed": [MockFunction],
|
||||
"getProxySettings": [MockFunction],
|
||||
"isActionTypeEnabled": [MockFunction],
|
||||
"isHostnameAllowed": [MockFunction],
|
||||
"isRejectUnauthorizedCertificatesEnabled": [MockFunction],
|
||||
"isUriAllowed": [MockFunction],
|
||||
},
|
||||
"data": "some data",
|
||||
"headers": Object {
|
||||
"aheader": "a value",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from webhook action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"proxySettings": undefined,
|
||||
"url": "https://abc.def/my-webhook",
|
||||
}
|
||||
],
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"url": "https://abc.def/my-webhook",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -338,39 +347,48 @@ describe('execute()', () => {
|
|||
});
|
||||
|
||||
expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"axios": undefined,
|
||||
"data": "some data",
|
||||
"headers": Object {
|
||||
"aheader": "a value",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from webhook action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
Object {
|
||||
"axios": undefined,
|
||||
"configurationUtilities": Object {
|
||||
"ensureActionTypeEnabled": [MockFunction],
|
||||
"ensureHostnameAllowed": [MockFunction],
|
||||
"ensureUriAllowed": [MockFunction],
|
||||
"getProxySettings": [MockFunction],
|
||||
"isActionTypeEnabled": [MockFunction],
|
||||
"isHostnameAllowed": [MockFunction],
|
||||
"isRejectUnauthorizedCertificatesEnabled": [MockFunction],
|
||||
"isUriAllowed": [MockFunction],
|
||||
},
|
||||
"data": "some data",
|
||||
"headers": Object {
|
||||
"aheader": "a value",
|
||||
},
|
||||
"logger": Object {
|
||||
"context": Array [],
|
||||
"debug": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"response from webhook action \\"some-id\\": [HTTP 200] ",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"proxySettings": undefined,
|
||||
"url": "https://abc.def/my-webhook",
|
||||
}
|
||||
],
|
||||
},
|
||||
"error": [MockFunction],
|
||||
"fatal": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"info": [MockFunction],
|
||||
"log": [MockFunction],
|
||||
"trace": [MockFunction],
|
||||
"warn": [MockFunction],
|
||||
},
|
||||
"method": "post",
|
||||
"url": "https://abc.def/my-webhook",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ export function getActionType({
|
|||
params: ParamsSchema,
|
||||
},
|
||||
renderParameterTemplates,
|
||||
executor: curry(executor)({ logger }),
|
||||
executor: curry(executor)({ logger, configurationUtilities }),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,10 @@ function validateActionTypeConfig(
|
|||
|
||||
// action executor
|
||||
export async function executor(
|
||||
{ logger }: { logger: Logger },
|
||||
{
|
||||
logger,
|
||||
configurationUtilities,
|
||||
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
|
||||
execOptions: WebhookActionTypeExecutorOptions
|
||||
): Promise<ActionTypeExecutorResult<unknown>> {
|
||||
const actionId = execOptions.actionId;
|
||||
|
@ -162,7 +165,7 @@ export async function executor(
|
|||
...basicAuth,
|
||||
headers,
|
||||
data,
|
||||
proxySettings: execOptions.proxySettings,
|
||||
configurationUtilities,
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -202,7 +205,7 @@ export async function executor(
|
|||
);
|
||||
}
|
||||
return errorResultInvalid(actionId, message);
|
||||
} else if (error.isAxiosError) {
|
||||
} else if (error.code) {
|
||||
const message = `[${error.code}] ${error.message}`;
|
||||
logger.error(`error on ${actionId} webhook event: ${message}`);
|
||||
return errorResultRequestFailed(actionId, message);
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server';
|
||||
import { ActionsPlugin } from './plugin';
|
||||
import { configSchema } from './config';
|
||||
import { configSchema, ActionsConfig } from './config';
|
||||
import { ActionsClient as ActionsClientClass } from './actions_client';
|
||||
import { ActionsAuthorization as ActionsAuthorizationClass } from './authorization/actions_authorization';
|
||||
import { ActionsConfigType } from './types';
|
||||
|
||||
export type ActionsClient = PublicMethodsOf<ActionsClientClass>;
|
||||
export type ActionsAuthorization = PublicMethodsOf<ActionsAuthorizationClass>;
|
||||
|
@ -52,7 +51,7 @@ export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './li
|
|||
|
||||
export const plugin = (initContext: PluginInitializerContext) => new ActionsPlugin(initContext);
|
||||
|
||||
export const config: PluginConfigDescriptor<ActionsConfigType> = {
|
||||
export const config: PluginConfigDescriptor<ActionsConfig> = {
|
||||
schema: configSchema,
|
||||
deprecations: ({ renameFromRoot }) => [
|
||||
renameFromRoot('xpack.actions.whitelistedHosts', 'xpack.actions.allowedHosts'),
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
GetServicesFunction,
|
||||
RawAction,
|
||||
PreConfiguredAction,
|
||||
ProxySettings,
|
||||
} from '../types';
|
||||
import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server';
|
||||
import { SpacesServiceStart } from '../../../spaces/server';
|
||||
|
@ -33,7 +32,6 @@ export interface ActionExecutorContext {
|
|||
actionTypeRegistry: ActionTypeRegistryContract;
|
||||
eventLogger: IEventLogger;
|
||||
preconfiguredActions: PreConfiguredAction[];
|
||||
proxySettings?: ProxySettings;
|
||||
}
|
||||
|
||||
export interface ExecuteOptions<Source = unknown> {
|
||||
|
@ -87,7 +85,6 @@ export class ActionExecutor {
|
|||
eventLogger,
|
||||
preconfiguredActions,
|
||||
getActionsClientWithRequest,
|
||||
proxySettings,
|
||||
} = this.actionExecutorContext!;
|
||||
|
||||
const services = getServices(request);
|
||||
|
@ -145,7 +142,6 @@ export class ActionExecutor {
|
|||
params: validatedParams,
|
||||
config: validatedConfig,
|
||||
secrets: validatedSecrets,
|
||||
proxySettings,
|
||||
});
|
||||
} catch (err) {
|
||||
rawResult = {
|
||||
|
|
|
@ -357,15 +357,6 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
|||
encryptedSavedObjectsClient,
|
||||
actionTypeRegistry: actionTypeRegistry!,
|
||||
preconfiguredActions,
|
||||
proxySettings:
|
||||
this.actionsConfig && this.actionsConfig.proxyUrl
|
||||
? {
|
||||
proxyUrl: this.actionsConfig.proxyUrl,
|
||||
proxyHeaders: this.actionsConfig.proxyHeaders,
|
||||
proxyRejectUnauthorizedCertificates: this.actionsConfig
|
||||
.proxyRejectUnauthorizedCertificates,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
const spaceIdToNamespace = (spaceId?: string) => {
|
||||
|
|
|
@ -55,12 +55,6 @@ export interface ActionsPlugin {
|
|||
start: PluginStartContract;
|
||||
}
|
||||
|
||||
export interface ActionsConfigType {
|
||||
enabled: boolean;
|
||||
allowedHosts: string[];
|
||||
enabledActionTypes: string[];
|
||||
}
|
||||
|
||||
// the parameters passed to an action type executor function
|
||||
export interface ActionTypeExecutorOptions<Config, Secrets, Params> {
|
||||
actionId: string;
|
||||
|
@ -68,7 +62,6 @@ export interface ActionTypeExecutorOptions<Config, Secrets, Params> {
|
|||
config: Config;
|
||||
secrets: Secrets;
|
||||
params: Params;
|
||||
proxySettings?: ProxySettings;
|
||||
}
|
||||
|
||||
export interface ActionResult<Config extends ActionTypeConfig = ActionTypeConfig> {
|
||||
|
|
|
@ -17,6 +17,7 @@ interface CreateTestConfigOptions {
|
|||
disabledPlugins?: string[];
|
||||
ssl?: boolean;
|
||||
enableActionsProxy: boolean;
|
||||
rejectUnauthorized?: boolean;
|
||||
}
|
||||
|
||||
// test.not-enabled is specifically not enabled
|
||||
|
@ -39,7 +40,12 @@ const enabledActionTypes = [
|
|||
];
|
||||
|
||||
export function createTestConfig(name: string, options: CreateTestConfigOptions) {
|
||||
const { license = 'trial', disabledPlugins = [], ssl = false } = options;
|
||||
const {
|
||||
license = 'trial',
|
||||
disabledPlugins = [],
|
||||
ssl = false,
|
||||
rejectUnauthorized = true,
|
||||
} = options;
|
||||
|
||||
return async ({ readConfigFile }: FtrConfigProviderContext) => {
|
||||
const xPackApiIntegrationTestsConfig = await readConfigFile(
|
||||
|
@ -95,6 +101,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
|
|||
'--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"',
|
||||
'--xpack.alerts.invalidateApiKeysTask.interval="15s"',
|
||||
`--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`,
|
||||
`--xpack.actions.rejectUnauthorized=${rejectUnauthorized}`,
|
||||
...actionsProxyUrl,
|
||||
|
||||
'--xpack.eventLog.logEntries=true',
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { Plugin, CoreSetup, IRouter } from 'kibana/server';
|
||||
import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server';
|
||||
|
@ -47,7 +48,13 @@ export function getAllExternalServiceSimulatorPaths(): string[] {
|
|||
}
|
||||
|
||||
export async function getWebhookServer(): Promise<http.Server> {
|
||||
return await initWebhook();
|
||||
const { httpServer } = await initWebhook();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
export async function getHttpsWebhookServer(): Promise<https.Server> {
|
||||
const { httpsServer } = await initWebhook();
|
||||
return httpsServer;
|
||||
}
|
||||
|
||||
export async function getSlackServer(): Promise<http.Server> {
|
||||
|
|
|
@ -3,16 +3,35 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import expect from '@kbn/expect';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { promisify } from 'util';
|
||||
import { fromNullable, map, filter, getOrElse } from 'fp-ts/lib/Option';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { constant } from 'fp-ts/lib/function';
|
||||
import { KBN_KEY_PATH, KBN_CERT_PATH } from '@kbn/dev-utils';
|
||||
|
||||
export async function initPlugin() {
|
||||
const payloads: string[] = [];
|
||||
const httpsServerKey = await promisify(fs.readFile)(KBN_KEY_PATH, 'utf8');
|
||||
const httpsServerCert = await promisify(fs.readFile)(KBN_CERT_PATH, 'utf8');
|
||||
|
||||
return http.createServer((request, response) => {
|
||||
return {
|
||||
httpServer: http.createServer(createServerCallback()),
|
||||
httpsServer: https.createServer(
|
||||
{
|
||||
key: httpsServerKey,
|
||||
cert: httpsServerCert,
|
||||
},
|
||||
createServerCallback()
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function createServerCallback() {
|
||||
const payloads: string[] = [];
|
||||
return (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
const credentials = pipe(
|
||||
fromNullable(request.headers.authorization),
|
||||
map((authorization) => authorization.split(/\s+/)),
|
||||
|
@ -77,7 +96,7 @@ export async function initPlugin() {
|
|||
return;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function validateAuthentication(credentials: any, res: any) {
|
||||
|
|
|
@ -11,4 +11,5 @@ export default createTestConfig('spaces_only', {
|
|||
disabledPlugins: ['security'],
|
||||
license: 'trial',
|
||||
enableActionsProxy: false,
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
*/
|
||||
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import getPort from 'get-port';
|
||||
import expect from '@kbn/expect';
|
||||
import { URL, format as formatUrl } from 'url';
|
||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
import { getWebhookServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin';
|
||||
import {
|
||||
getWebhookServer,
|
||||
getHttpsWebhookServer,
|
||||
} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function webhookTest({ getService }: FtrProviderContext) {
|
||||
|
@ -43,32 +47,65 @@ export default function webhookTest({ getService }: FtrProviderContext) {
|
|||
}
|
||||
|
||||
describe('webhook action', () => {
|
||||
let webhookSimulatorURL: string = '';
|
||||
let webhookServer: http.Server;
|
||||
before(async () => {
|
||||
webhookServer = await getWebhookServer();
|
||||
const availablePort = await getPort({ port: 9000 });
|
||||
webhookServer.listen(availablePort);
|
||||
webhookSimulatorURL = `http://localhost:${availablePort}`;
|
||||
describe('with http endpoint', () => {
|
||||
let webhookSimulatorURL: string = '';
|
||||
let webhookServer: http.Server;
|
||||
before(async () => {
|
||||
webhookServer = await getWebhookServer();
|
||||
const availablePort = await getPort({ port: 9000 });
|
||||
webhookServer.listen(availablePort);
|
||||
webhookSimulatorURL = `http://localhost:${availablePort}`;
|
||||
});
|
||||
|
||||
it('webhook can be executed without username and password', async () => {
|
||||
const webhookActionId = await createWebhookAction(webhookSimulatorURL);
|
||||
const { body: result } = await supertest
|
||||
.post(`/api/actions/action/${webhookActionId}/_execute`)
|
||||
.set('kbn-xsrf', 'test')
|
||||
.send({
|
||||
params: {
|
||||
body: 'success',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(result.status).to.eql('ok');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
webhookServer.close();
|
||||
});
|
||||
});
|
||||
|
||||
it('webhook can be executed without username and password', async () => {
|
||||
const webhookActionId = await createWebhookAction(webhookSimulatorURL);
|
||||
const { body: result } = await supertest
|
||||
.post(`/api/actions/action/${webhookActionId}/_execute`)
|
||||
.set('kbn-xsrf', 'test')
|
||||
.send({
|
||||
params: {
|
||||
body: 'success',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
describe('with https endpoint and rejectUnauthorized=false', () => {
|
||||
let webhookSimulatorURL: string = '';
|
||||
let webhookServer: https.Server;
|
||||
|
||||
expect(result.status).to.eql('ok');
|
||||
});
|
||||
before(async () => {
|
||||
webhookServer = await getHttpsWebhookServer();
|
||||
const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) });
|
||||
webhookServer.listen(availablePort);
|
||||
webhookSimulatorURL = `https://localhost:${availablePort}`;
|
||||
});
|
||||
|
||||
after(() => {
|
||||
webhookServer.close();
|
||||
it('should support the POST method against webhook target', async () => {
|
||||
const webhookActionId = await createWebhookAction(webhookSimulatorURL, { method: 'post' });
|
||||
const { body: result } = await supertest
|
||||
.post(`/api/actions/action/${webhookActionId}/_execute`)
|
||||
.set('kbn-xsrf', 'test')
|
||||
.send({
|
||||
params: {
|
||||
body: 'success_post_method',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(result.status).to.eql('ok');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
webhookServer.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue