From 291385e99dd7db387d30b213146b7649160d4530 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Wed, 26 May 2021 13:47:22 -0700 Subject: [PATCH] added integration tests for legacy rejectUnauthorized fale --- .../alerting_api_integration/common/config.ts | 3 + .../spaces_only_legacy/config.ts | 19 ++ .../spaces_only_legacy/scenarios.ts | 35 +++ .../tests/actions/webhook.ts | 201 ++++++++++++++++++ .../spaces_only_legacy/tests/index.ts | 33 +++ 5 files changed, 291 insertions(+) create mode 100644 x-pack/test/alerting_api_integration/spaces_only_legacy/config.ts create mode 100644 x-pack/test/alerting_api_integration/spaces_only_legacy/scenarios.ts create mode 100644 x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/webhook.ts create mode 100644 x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 18a1f24c2b8f7..c56e8adfbe34f 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -23,6 +23,7 @@ interface CreateTestConfigOptions { publicBaseUrl?: boolean; preconfiguredAlertHistoryEsIndex?: boolean; customizeLocalHostTls?: boolean; + rejectUnauthorized?: boolean; // legacy } // test.not-enabled is specifically not enabled @@ -52,6 +53,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) verificationMode = 'full', preconfiguredAlertHistoryEsIndex = false, customizeLocalHostTls = false, + rejectUnauthorized = true, // legacy } = options; return async ({ readConfigFile }: FtrConfigProviderContext) => { @@ -150,6 +152,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) '--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', '--xpack.alerting.invalidateApiKeysTask.interval="15s"', `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, + `--xpack.actions.rejectUnauthorized=${rejectUnauthorized}`, `--xpack.actions.tls.verificationMode=${verificationMode}`, ...actionsProxyUrl, ...customHostSettings, diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/config.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/config.ts new file mode 100644 index 0000000000000..511e97b96e35d --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/config.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('spaces_only', { + disabledPlugins: ['security'], + license: 'trial', + enableActionsProxy: false, + rejectUnauthorized: false, + verificationMode: undefined, + customizeLocalHostTls: true, + preconfiguredAlertHistoryEsIndex: true, +}); diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/scenarios.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/scenarios.ts new file mode 100644 index 0000000000000..5c00ad2f4f70f --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/scenarios.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Space } from '../common/types'; + +const Space1: Space = { + id: 'space1', + namespace: 'space1', + name: 'Space 1', + disabledFeatures: [], +}; + +const Other: Space = { + id: 'other', + namespace: 'other', + name: 'Other', + disabledFeatures: [], +}; + +const Default: Space = { + id: 'default', + namespace: undefined, + name: 'Default', + disabledFeatures: [], +}; + +export const Spaces = { + space1: Space1, + other: Other, + default: Default, +}; diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/webhook.ts new file mode 100644 index 0000000000000..4af33136cd42c --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/actions/webhook.ts @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +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, + getHttpsWebhookServer, +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; +import { createTlsWebhookServer } from '../../../../common/lib/get_tls_webhook_servers'; + +// eslint-disable-next-line import/no-default-export +export default function webhookTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + async function createWebhookAction( + webhookSimulatorURL: string, + config: Record> = {} + ): Promise { + const url = formatUrl(new URL(webhookSimulatorURL), { auth: false }); + const composedConfig = { + headers: { + 'Content-Type': 'text/plain', + }, + ...config, + url, + }; + + const { body: createdAction } = await supertest + .post('/api/actions/action') + .set('kbn-xsrf', 'test') + .send({ + name: 'A generic Webhook action', + actionTypeId: '.webhook', + secrets: {}, + config: composedConfig, + }) + .expect(200); + + return createdAction.id; + } + + async function getPortOfConnector(connectorId: string): Promise { + const response = await supertest.get(`/api/actions/connectors`).expect(200); + const connector = response.body.find((conn: { id: string }) => conn.id === connectorId); + if (connector === undefined) { + throw new Error(`unable to find connector with id ${connectorId}`); + } + + // server URL is the connector name + const url = connector.name; + const parsedUrl = new URL(url); + return parsedUrl.port; + } + + describe('webhook action', () => { + 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(); + }); + }); + + describe('with https endpoint and rejectUnauthorized=false', () => { + let webhookSimulatorURL: string = ''; + let webhookServer: https.Server; + + before(async () => { + webhookServer = await getHttpsWebhookServer(); + const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); + webhookServer.listen(availablePort); + webhookSimulatorURL = `https://localhost:${availablePort}`; + }); + + 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(); + }); + }); + + describe('tls customization', () => { + it('should handle the xpack.actions.rejectUnauthorized: false', async () => { + const connectorId = 'custom.tls.noCustom'; + const port = await getPortOfConnector(connectorId); + const server = await createTlsWebhookServer(port); + const { status, body } = await supertest + .post(`/api/actions/connector/${connectorId}/_execute`) + .set('kbn-xsrf', 'test') + .send({ + params: { + body: 'foo', + }, + }); + expect(status).to.eql(200); + server.close(); + + expect(body.status).to.eql('ok'); + }); + + it('should handle the customized rejectUnauthorized: false', async () => { + const connectorId = 'custom.tls.rejectUnauthorizedFalse'; + const port = await getPortOfConnector(connectorId); + const server = await createTlsWebhookServer(port); + const { status, body } = await supertest + .post(`/api/actions/connector/custom.tls.rejectUnauthorizedFalse/_execute`) + .set('kbn-xsrf', 'test') + .send({ + params: { + body: 'foo', + }, + }); + expect(status).to.eql(200); + server.close(); + + expect(body.status).to.eql('ok'); + }); + + it('should handle the customized rejectUnauthorized: true', async () => { + const connectorId = 'custom.tls.rejectUnauthorizedTrue'; + const port = await getPortOfConnector(connectorId); + const server = await createTlsWebhookServer(port); + const { status, body } = await supertest + .post(`/api/actions/connector/custom.tls.rejectUnauthorizedTrue/_execute`) + .set('kbn-xsrf', 'test') + .send({ + params: { + body: 'foo', + }, + }); + expect(status).to.eql(200); + server.close(); + + expect(body.status).to.eql('error'); + expect(body.service_message.indexOf('certificate')).to.be.greaterThan(0); + }); + + it('should handle the customized ca file', async () => { + const connectorId = 'custom.tls.caFile'; + const port = await getPortOfConnector(connectorId); + const server = await createTlsWebhookServer(port); + const { status, body } = await supertest + .post(`/api/actions/connector/custom.tls.caFile/_execute`) + .set('kbn-xsrf', 'test') + .send({ + params: { + body: 'foo', + }, + }); + expect(status).to.eql(200); + server.close(); + + expect(body.status).to.eql('ok'); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts new file mode 100644 index 0000000000000..9240d93ea0fff --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only_legacy/tests/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { Spaces } from '../scenarios'; + +// eslint-disable-next-line import/no-default-export +export default function alertingApiIntegrationTests({ loadTestFile }: FtrProviderContext) { + describe('alerting api integration spaces only legacy configuration', function () { + this.tags('ciGroup12'); + + loadTestFile(require.resolve('./actions/webhook')); + }); +} + +export async function buildUp(getService: FtrProviderContext['getService']) { + const spacesService = getService('spaces'); + for (const space of Object.values(Spaces)) { + if (space.id === 'default') continue; + + const { id, name, disabledFeatures } = space; + await spacesService.create({ id, name, disabledFeatures }); + } +} + +export async function tearDown(getService: FtrProviderContext['getService']) { + const esArchiver = getService('esArchiver'); + await esArchiver.unload('empty_kibana'); +}