From 891b258d0b3120068e37efd5e056cf78720bd0c0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 11 May 2018 06:42:46 -0700 Subject: [PATCH 01/11] WIP checkin --- .../check_license/__tests__/check_license.js | 180 ++++++++++++++++++ .../server/lib/check_license/check_license.js | 69 +++++++ .../beats/server/lib/check_license/index.js | 7 + .../__tests__/wrap_custom_error.js | 21 ++ .../error_wrappers/__tests__/wrap_es_error.js | 41 ++++ .../__tests__/wrap_unknown_error.js | 19 ++ .../lib/error_wrappers/wrap_custom_error.js | 18 ++ .../lib/error_wrappers/wrap_unknown_error.js | 17 ++ .../__tests__/license_pre_routing_factory.js | 72 +++++++ .../lib/license_pre_routing_factory/index.js | 7 + .../license_pre_routing_factory.js | 28 +++ .../lib/register_license_checker/index.js | 7 + .../register_license_checker.js | 21 ++ .../api/register_disenroll_beats_route.js | 0 .../routes/api/register_enroll_beats_route.js | 0 .../routes/api/register_list_beats_route.js | 0 .../routes/api/register_verify_beats_route.js | 0 17 files changed, 507 insertions(+) create mode 100644 x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js create mode 100644 x-pack/plugins/beats/server/lib/check_license/check_license.js create mode 100644 x-pack/plugins/beats/server/lib/check_license/index.js create mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js create mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js create mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js create mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js create mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js create mode 100644 x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js create mode 100644 x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js create mode 100644 x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js create mode 100644 x-pack/plugins/beats/server/lib/register_license_checker/index.js create mode 100644 x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js create mode 100644 x-pack/plugins/beats/server/routes/api/register_disenroll_beats_route.js create mode 100644 x-pack/plugins/beats/server/routes/api/register_enroll_beats_route.js create mode 100644 x-pack/plugins/beats/server/routes/api/register_list_beats_route.js create mode 100644 x-pack/plugins/beats/server/routes/api/register_verify_beats_route.js diff --git a/x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js b/x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js new file mode 100644 index 0000000000000..449ff3a60b9e7 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { set } from 'lodash'; +import { checkLicense } from '../check_license'; + +describe('check_license', function () { + + let mockLicenseInfo; + beforeEach(() => mockLicenseInfo = {}); + + describe('license information is undefined', () => { + beforeEach(() => mockLicenseInfo = undefined); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set isReadOnly to false', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is not available', () => { + beforeEach(() => mockLicenseInfo.isAvailable = () => false); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it('should set isReadOnly to false', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('license information is available', () => { + beforeEach(() => { + mockLicenseInfo.isAvailable = () => true; + set(mockLicenseInfo, 'license.getType', () => 'basic'); + }); + + describe('& license is trial, standard, gold, platinum', () => { + beforeEach(() => { + set(mockLicenseInfo, 'license.isOneOf', () => true); + mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled + }); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set isAvailable to true', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); + }); + + it ('should set enableLinks to true', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); + }); + + it ('should set isReadOnly to false', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); + }); + + it('should not set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set isAvailable to true', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); + }); + + it ('should set enableLinks to true', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); + }); + + it ('should set isReadOnly to true', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(true); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); + + describe('& license is basic', () => { + beforeEach(() => { + set(mockLicenseInfo, 'license.isOneOf', () => false); + mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled + }); + + describe('& license is active', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it ('should set isReadOnly to false', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + + describe('& license is expired', () => { + beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it ('should set isReadOnly to false', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); + + describe('& security is disabled', () => { + beforeEach(() => { + mockLicenseInfo.feature = () => ({ isEnabled: () => false }); // Security feature is disabled + set(mockLicenseInfo, 'license.isOneOf', () => true); + set(mockLicenseInfo, 'license.isActive', () => true); + }); + + it('should set isAvailable to false', () => { + expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); + }); + + it ('should set enableLinks to false', () => { + expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); + }); + + it ('should set isReadOnly to false', () => { + expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); + }); + + it('should set a message', () => { + expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); + }); + }); + }); +}); diff --git a/x-pack/plugins/beats/server/lib/check_license/check_license.js b/x-pack/plugins/beats/server/lib/check_license/check_license.js new file mode 100644 index 0000000000000..aa1704cc02730 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/check_license/check_license.js @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export function checkLicense(xpackLicenseInfo) { + // If, for some reason, we cannot get the license information + // from Elasticsearch, assume worst case and disable the Logstash pipeline UI + if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { + return { + isAvailable: false, + enableLinks: false, + isReadOnly: false, + message: 'You cannot manage Logstash pipelines because license information is not available at this time.' + }; + } + + const VALID_LICENSE_MODES = [ + 'trial', + 'standard', + 'gold', + 'platinum' + ]; + + const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); + const isLicenseActive = xpackLicenseInfo.license.isActive(); + const licenseType = xpackLicenseInfo.license.getType(); + const isSecurityEnabled = xpackLicenseInfo.feature('security').isEnabled(); + + // Security is not enabled in ES + if (!isSecurityEnabled) { + const message = 'Security must be enabled in order to use Logstash pipeline management features.' + + ' Please set xpack.security.enabled: true in your elasticsearch.yml.'; + return { + isAvailable: false, + enableLinks: false, + isReadOnly: false, + message + }; + } + + // License is not valid + if (!isLicenseModeValid) { + return { + isAvailable: false, + enableLinks: false, + isReadOnly: false, + message: `Your ${licenseType} license does not support Logstash pipeline management features. Please upgrade your license.` + }; + } + + // License is valid but not active, we go into a read-only mode. + if (!isLicenseActive) { + return { + isAvailable: true, + enableLinks: true, + isReadOnly: true, + message: `You cannot edit, create, or delete your Logstash pipelines because your ${licenseType} license has expired.` + }; + } + + // License is valid and active + return { + isAvailable: true, + enableLinks: true, + isReadOnly: false + }; +} diff --git a/x-pack/plugins/beats/server/lib/check_license/index.js b/x-pack/plugins/beats/server/lib/check_license/index.js new file mode 100644 index 0000000000000..f2c070fd44b6e --- /dev/null +++ b/x-pack/plugins/beats/server/lib/check_license/index.js @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { checkLicense } from './check_license'; diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js new file mode 100644 index 0000000000000..443744ccb0cc8 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { wrapCustomError } from '../wrap_custom_error'; + +describe('wrap_custom_error', () => { + describe('#wrapCustomError', () => { + it('should return a Boom object', () => { + const originalError = new Error('I am an error'); + const statusCode = 404; + const wrappedError = wrapCustomError(originalError, statusCode); + + expect(wrappedError.isBoom).to.be(true); + expect(wrappedError.output.statusCode).to.equal(statusCode); + }); + }); +}); diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js new file mode 100644 index 0000000000000..f1b956bdcc3bb --- /dev/null +++ b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { wrapEsError } from '../wrap_es_error'; + +describe('wrap_es_error', () => { + describe('#wrapEsError', () => { + + let originalError; + beforeEach(() => { + originalError = new Error('I am an error'); + originalError.statusCode = 404; + }); + + it('should return a Boom object', () => { + const wrappedError = wrapEsError(originalError); + + expect(wrappedError.isBoom).to.be(true); + }); + + it('should return the correct Boom object', () => { + const wrappedError = wrapEsError(originalError); + + expect(wrappedError.output.statusCode).to.be(originalError.statusCode); + expect(wrappedError.output.payload.message).to.be(originalError.message); + }); + + it('should return invalid permissions message for 403 errors', () => { + const securityError = new Error('I am an error'); + securityError.statusCode = 403; + const wrappedError = wrapEsError(securityError); + + expect(wrappedError.isBoom).to.be(true); + expect(wrappedError.message).to.be('Insufficient user permissions for managing Logstash pipelines'); + }); + }); +}); diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js new file mode 100644 index 0000000000000..6d6a336417bef --- /dev/null +++ b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { wrapUnknownError } from '../wrap_unknown_error'; + +describe('wrap_unknown_error', () => { + describe('#wrapUnknownError', () => { + it('should return a Boom object', () => { + const originalError = new Error('I am an error'); + const wrappedError = wrapUnknownError(originalError); + + expect(wrappedError.isBoom).to.be(true); + }); + }); +}); diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js new file mode 100644 index 0000000000000..890a366ac65c1 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +/** + * Wraps a custom error into a Boom error response and returns it + * + * @param err Object error + * @param statusCode Error status code + * @return Object Boom error response + */ +export function wrapCustomError(err, statusCode) { + return Boom.wrap(err, statusCode); +} diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js new file mode 100644 index 0000000000000..b0cdced7adbef --- /dev/null +++ b/x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; + +/** + * Wraps an unknown error into a Boom error response and returns it + * + * @param err Object Unknown error + * @return Object Boom error response + */ +export function wrapUnknownError(err) { + return Boom.wrap(err); +} diff --git a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js new file mode 100644 index 0000000000000..c543d79814dd3 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { licensePreRoutingFactory } from '../license_pre_routing_factory'; + +describe('license_pre_routing_factory', () => { + describe('#logstashFeaturePreRoutingFactory', () => { + let mockServer; + let mockLicenseCheckResults; + + beforeEach(() => { + mockServer = { + plugins: { + xpack_main: { + info: { + feature: () => ({ + getLicenseCheckResults: () => mockLicenseCheckResults + }) + } + } + } + }; + }); + + it('only instantiates one instance per server', () => { + const firstInstance = licensePreRoutingFactory(mockServer); + const secondInstance = licensePreRoutingFactory(mockServer); + + expect(firstInstance).to.be(secondInstance); + }); + + describe('isAvailable is false', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: false + }; + }); + + it ('replies with 403', (done) => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + licensePreRouting(stubRequest, (response) => { + expect(response).to.be.an(Error); + expect(response.isBoom).to.be(true); + expect(response.output.statusCode).to.be(403); + done(); + }); + }); + }); + + describe('isAvailable is true', () => { + beforeEach(() => { + mockLicenseCheckResults = { + isAvailable: true + }; + }); + + it ('replies with nothing', (done) => { + const licensePreRouting = licensePreRoutingFactory(mockServer); + const stubRequest = {}; + licensePreRouting(stubRequest, (response) => { + expect(response).to.be(undefined); + done(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js new file mode 100644 index 0000000000000..0743e443955f4 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { licensePreRoutingFactory } from './license_pre_routing_factory'; diff --git a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js new file mode 100644 index 0000000000000..4ae31f692bfd7 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { once } from 'lodash'; +import { wrapCustomError } from '../error_wrappers'; +import { PLUGIN } from '../../../common/constants'; + +export const licensePreRoutingFactory = once((server) => { + const xpackMainPlugin = server.plugins.xpack_main; + + // License checking and enable/disable logic + function licensePreRouting(request, reply) { + const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); + if (!licenseCheckResults.isAvailable) { + const error = new Error(licenseCheckResults.message); + const statusCode = 403; + const wrappedError = wrapCustomError(error, statusCode); + reply(wrappedError); + } else { + reply(); + } + } + + return licensePreRouting; +}); diff --git a/x-pack/plugins/beats/server/lib/register_license_checker/index.js b/x-pack/plugins/beats/server/lib/register_license_checker/index.js new file mode 100644 index 0000000000000..7b0f97c38d129 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/register_license_checker/index.js @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js b/x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js new file mode 100644 index 0000000000000..8a17fb2eea497 --- /dev/null +++ b/x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; +import { checkLicense } from '../check_license'; +import { PLUGIN } from '../../../common/constants'; + +export function registerLicenseChecker(server) { + const xpackMainPlugin = server.plugins.xpack_main; + const logstashPlugin = server.plugins.logstash; + + mirrorPluginStatus(xpackMainPlugin, logstashPlugin); + xpackMainPlugin.status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); + }); +} diff --git a/x-pack/plugins/beats/server/routes/api/register_disenroll_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_disenroll_beats_route.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beats_route.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/plugins/beats/server/routes/api/register_list_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_list_beats_route.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/plugins/beats/server/routes/api/register_verify_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_verify_beats_route.js new file mode 100644 index 0000000000000..e69de29bb2d1d From ecb0e5d2349dc1c43288db30bfb0d98345b77bb1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 12 May 2018 06:55:32 -0700 Subject: [PATCH 02/11] Add API integration test --- .../check_license/__tests__/check_license.js | 180 ------------------ .../server/lib/check_license/check_license.js | 69 ------- .../beats/server/lib/check_license/index.js | 7 - .../__tests__/wrap_custom_error.js | 21 -- .../__tests__/wrap_unknown_error.js | 19 -- .../lib/error_wrappers/wrap_custom_error.js | 18 -- .../lib/error_wrappers/wrap_unknown_error.js | 17 -- .../__tests__/license_pre_routing_factory.js | 72 ------- .../lib/license_pre_routing_factory/index.js | 7 - .../license_pre_routing_factory.js | 28 --- .../lib/register_license_checker/index.js | 7 - .../register_license_checker.js | 21 -- .../api/register_disenroll_beats_route.js | 0 .../routes/api/register_enroll_beats_route.js | 0 .../routes/api/register_list_beats_route.js | 0 .../routes/api/register_verify_beats_route.js | 0 16 files changed, 466 deletions(-) delete mode 100644 x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js delete mode 100644 x-pack/plugins/beats/server/lib/check_license/check_license.js delete mode 100644 x-pack/plugins/beats/server/lib/check_license/index.js delete mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js delete mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js delete mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js delete mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js delete mode 100644 x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js delete mode 100644 x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js delete mode 100644 x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js delete mode 100644 x-pack/plugins/beats/server/lib/register_license_checker/index.js delete mode 100644 x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js delete mode 100644 x-pack/plugins/beats/server/routes/api/register_disenroll_beats_route.js delete mode 100644 x-pack/plugins/beats/server/routes/api/register_enroll_beats_route.js delete mode 100644 x-pack/plugins/beats/server/routes/api/register_list_beats_route.js delete mode 100644 x-pack/plugins/beats/server/routes/api/register_verify_beats_route.js diff --git a/x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js b/x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js deleted file mode 100644 index 449ff3a60b9e7..0000000000000 --- a/x-pack/plugins/beats/server/lib/check_license/__tests__/check_license.js +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from 'expect.js'; -import { set } from 'lodash'; -import { checkLicense } from '../check_license'; - -describe('check_license', function () { - - let mockLicenseInfo; - beforeEach(() => mockLicenseInfo = {}); - - describe('license information is undefined', () => { - beforeEach(() => mockLicenseInfo = undefined); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is not available', () => { - beforeEach(() => mockLicenseInfo.isAvailable = () => false); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is available', () => { - beforeEach(() => { - mockLicenseInfo.isAvailable = () => true; - set(mockLicenseInfo, 'license.getType', () => 'basic'); - }); - - describe('& license is trial, standard, gold, platinum', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isOneOf', () => true); - mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled - }); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it ('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it ('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should not set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it ('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it ('should set isReadOnly to true', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(true); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - - describe('& license is basic', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isOneOf', () => false); - mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled - }); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it ('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it ('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it ('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it ('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - - describe('& security is disabled', () => { - beforeEach(() => { - mockLicenseInfo.feature = () => ({ isEnabled: () => false }); // Security feature is disabled - set(mockLicenseInfo, 'license.isOneOf', () => true); - set(mockLicenseInfo, 'license.isActive', () => true); - }); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it ('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it ('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); -}); diff --git a/x-pack/plugins/beats/server/lib/check_license/check_license.js b/x-pack/plugins/beats/server/lib/check_license/check_license.js deleted file mode 100644 index aa1704cc02730..0000000000000 --- a/x-pack/plugins/beats/server/lib/check_license/check_license.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function checkLicense(xpackLicenseInfo) { - // If, for some reason, we cannot get the license information - // from Elasticsearch, assume worst case and disable the Logstash pipeline UI - if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { - return { - isAvailable: false, - enableLinks: false, - isReadOnly: false, - message: 'You cannot manage Logstash pipelines because license information is not available at this time.' - }; - } - - const VALID_LICENSE_MODES = [ - 'trial', - 'standard', - 'gold', - 'platinum' - ]; - - const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); - const isLicenseActive = xpackLicenseInfo.license.isActive(); - const licenseType = xpackLicenseInfo.license.getType(); - const isSecurityEnabled = xpackLicenseInfo.feature('security').isEnabled(); - - // Security is not enabled in ES - if (!isSecurityEnabled) { - const message = 'Security must be enabled in order to use Logstash pipeline management features.' - + ' Please set xpack.security.enabled: true in your elasticsearch.yml.'; - return { - isAvailable: false, - enableLinks: false, - isReadOnly: false, - message - }; - } - - // License is not valid - if (!isLicenseModeValid) { - return { - isAvailable: false, - enableLinks: false, - isReadOnly: false, - message: `Your ${licenseType} license does not support Logstash pipeline management features. Please upgrade your license.` - }; - } - - // License is valid but not active, we go into a read-only mode. - if (!isLicenseActive) { - return { - isAvailable: true, - enableLinks: true, - isReadOnly: true, - message: `You cannot edit, create, or delete your Logstash pipelines because your ${licenseType} license has expired.` - }; - } - - // License is valid and active - return { - isAvailable: true, - enableLinks: true, - isReadOnly: false - }; -} diff --git a/x-pack/plugins/beats/server/lib/check_license/index.js b/x-pack/plugins/beats/server/lib/check_license/index.js deleted file mode 100644 index f2c070fd44b6e..0000000000000 --- a/x-pack/plugins/beats/server/lib/check_license/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { checkLicense } from './check_license'; diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js deleted file mode 100644 index 443744ccb0cc8..0000000000000 --- a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_custom_error.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from 'expect.js'; -import { wrapCustomError } from '../wrap_custom_error'; - -describe('wrap_custom_error', () => { - describe('#wrapCustomError', () => { - it('should return a Boom object', () => { - const originalError = new Error('I am an error'); - const statusCode = 404; - const wrappedError = wrapCustomError(originalError, statusCode); - - expect(wrappedError.isBoom).to.be(true); - expect(wrappedError.output.statusCode).to.equal(statusCode); - }); - }); -}); diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js deleted file mode 100644 index 6d6a336417bef..0000000000000 --- a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_unknown_error.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from 'expect.js'; -import { wrapUnknownError } from '../wrap_unknown_error'; - -describe('wrap_unknown_error', () => { - describe('#wrapUnknownError', () => { - it('should return a Boom object', () => { - const originalError = new Error('I am an error'); - const wrappedError = wrapUnknownError(originalError); - - expect(wrappedError.isBoom).to.be(true); - }); - }); -}); diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js deleted file mode 100644 index 890a366ac65c1..0000000000000 --- a/x-pack/plugins/beats/server/lib/error_wrappers/wrap_custom_error.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -/** - * Wraps a custom error into a Boom error response and returns it - * - * @param err Object error - * @param statusCode Error status code - * @return Object Boom error response - */ -export function wrapCustomError(err, statusCode) { - return Boom.wrap(err, statusCode); -} diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js deleted file mode 100644 index b0cdced7adbef..0000000000000 --- a/x-pack/plugins/beats/server/lib/error_wrappers/wrap_unknown_error.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; - -/** - * Wraps an unknown error into a Boom error response and returns it - * - * @param err Object Unknown error - * @return Object Boom error response - */ -export function wrapUnknownError(err) { - return Boom.wrap(err); -} diff --git a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js deleted file mode 100644 index c543d79814dd3..0000000000000 --- a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from 'expect.js'; -import { licensePreRoutingFactory } from '../license_pre_routing_factory'; - -describe('license_pre_routing_factory', () => { - describe('#logstashFeaturePreRoutingFactory', () => { - let mockServer; - let mockLicenseCheckResults; - - beforeEach(() => { - mockServer = { - plugins: { - xpack_main: { - info: { - feature: () => ({ - getLicenseCheckResults: () => mockLicenseCheckResults - }) - } - } - } - }; - }); - - it('only instantiates one instance per server', () => { - const firstInstance = licensePreRoutingFactory(mockServer); - const secondInstance = licensePreRoutingFactory(mockServer); - - expect(firstInstance).to.be(secondInstance); - }); - - describe('isAvailable is false', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: false - }; - }); - - it ('replies with 403', (done) => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const stubRequest = {}; - licensePreRouting(stubRequest, (response) => { - expect(response).to.be.an(Error); - expect(response.isBoom).to.be(true); - expect(response.output.statusCode).to.be(403); - done(); - }); - }); - }); - - describe('isAvailable is true', () => { - beforeEach(() => { - mockLicenseCheckResults = { - isAvailable: true - }; - }); - - it ('replies with nothing', (done) => { - const licensePreRouting = licensePreRoutingFactory(mockServer); - const stubRequest = {}; - licensePreRouting(stubRequest, (response) => { - expect(response).to.be(undefined); - done(); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js deleted file mode 100644 index 0743e443955f4..0000000000000 --- a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { licensePreRoutingFactory } from './license_pre_routing_factory'; diff --git a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js deleted file mode 100644 index 4ae31f692bfd7..0000000000000 --- a/x-pack/plugins/beats/server/lib/license_pre_routing_factory/license_pre_routing_factory.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { once } from 'lodash'; -import { wrapCustomError } from '../error_wrappers'; -import { PLUGIN } from '../../../common/constants'; - -export const licensePreRoutingFactory = once((server) => { - const xpackMainPlugin = server.plugins.xpack_main; - - // License checking and enable/disable logic - function licensePreRouting(request, reply) { - const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); - if (!licenseCheckResults.isAvailable) { - const error = new Error(licenseCheckResults.message); - const statusCode = 403; - const wrappedError = wrapCustomError(error, statusCode); - reply(wrappedError); - } else { - reply(); - } - } - - return licensePreRouting; -}); diff --git a/x-pack/plugins/beats/server/lib/register_license_checker/index.js b/x-pack/plugins/beats/server/lib/register_license_checker/index.js deleted file mode 100644 index 7b0f97c38d129..0000000000000 --- a/x-pack/plugins/beats/server/lib/register_license_checker/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js b/x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js deleted file mode 100644 index 8a17fb2eea497..0000000000000 --- a/x-pack/plugins/beats/server/lib/register_license_checker/register_license_checker.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; -import { checkLicense } from '../check_license'; -import { PLUGIN } from '../../../common/constants'; - -export function registerLicenseChecker(server) { - const xpackMainPlugin = server.plugins.xpack_main; - const logstashPlugin = server.plugins.logstash; - - mirrorPluginStatus(xpackMainPlugin, logstashPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); - }); -} diff --git a/x-pack/plugins/beats/server/routes/api/register_disenroll_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_disenroll_beats_route.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beats_route.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/x-pack/plugins/beats/server/routes/api/register_list_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_list_beats_route.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/x-pack/plugins/beats/server/routes/api/register_verify_beats_route.js b/x-pack/plugins/beats/server/routes/api/register_verify_beats_route.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 0ed988cfe2f3cfbad12a0a9a763f54fa90b8d161 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 12 May 2018 09:22:39 -0700 Subject: [PATCH 03/11] Converting to Jest test --- .../error_wrappers/__tests__/wrap_es_error.js | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js diff --git a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js b/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js deleted file mode 100644 index f1b956bdcc3bb..0000000000000 --- a/x-pack/plugins/beats/server/lib/error_wrappers/__tests__/wrap_es_error.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from 'expect.js'; -import { wrapEsError } from '../wrap_es_error'; - -describe('wrap_es_error', () => { - describe('#wrapEsError', () => { - - let originalError; - beforeEach(() => { - originalError = new Error('I am an error'); - originalError.statusCode = 404; - }); - - it('should return a Boom object', () => { - const wrappedError = wrapEsError(originalError); - - expect(wrappedError.isBoom).to.be(true); - }); - - it('should return the correct Boom object', () => { - const wrappedError = wrapEsError(originalError); - - expect(wrappedError.output.statusCode).to.be(originalError.statusCode); - expect(wrappedError.output.payload.message).to.be(originalError.message); - }); - - it('should return invalid permissions message for 403 errors', () => { - const securityError = new Error('I am an error'); - securityError.statusCode = 403; - const wrappedError = wrapEsError(securityError); - - expect(wrappedError.isBoom).to.be(true); - expect(wrappedError.message).to.be('Insufficient user permissions for managing Logstash pipelines'); - }); - }); -}); From 28ac4e1b9197defcbebc1caae4cc004c47930874 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 14 May 2018 15:33:52 -0700 Subject: [PATCH 04/11] Create API for enrolling a beat --- .../call_with_request_factory.js | 5 +- .../plugins/beats/server/lib/client/index.js | 1 + .../plugins/beats/server/routes/api/index.js | 2 + ...register_create_enrollment_tokens_route.js | 2 +- .../routes/api/register_enroll_beat_route.js | 60 +++++++++++++++ .../api_integration/apis/beats/constants.js} | 4 +- .../apis/beats/create_enrollment_tokens.js | 19 ++--- .../api_integration/apis/beats/enroll_beat.js | 73 +++++++++++++++++++ .../test/api_integration/apis/beats/index.js | 16 +++- 9 files changed, 161 insertions(+), 21 deletions(-) rename x-pack/plugins/beats/server/lib/{call_with_request_factory => client}/call_with_request_factory.js (73%) create mode 100644 x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js rename x-pack/{plugins/beats/server/lib/call_with_request_factory/index.js => test/api_integration/apis/beats/constants.js} (73%) create mode 100644 x-pack/test/api_integration/apis/beats/enroll_beat.js diff --git a/x-pack/plugins/beats/server/lib/call_with_request_factory/call_with_request_factory.js b/x-pack/plugins/beats/server/lib/client/call_with_request_factory.js similarity index 73% rename from x-pack/plugins/beats/server/lib/call_with_request_factory/call_with_request_factory.js rename to x-pack/plugins/beats/server/lib/client/call_with_request_factory.js index 0c4f909d12f61..c81670ed0cdec 100644 --- a/x-pack/plugins/beats/server/lib/call_with_request_factory/call_with_request_factory.js +++ b/x-pack/plugins/beats/server/lib/client/call_with_request_factory.js @@ -7,9 +7,8 @@ import { once } from 'lodash'; const callWithRequest = once((server) => { - const config = server.config().get('elasticsearch'); - const cluster = server.plugins.elasticsearch.createCluster('beats', config); - return cluster.callWithRequest; + const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin'); + return callWithRequest; }); export const callWithRequestFactory = (server, request) => { diff --git a/x-pack/plugins/beats/server/lib/client/index.js b/x-pack/plugins/beats/server/lib/client/index.js index a56a50e2864a5..cdeee091cc66f 100644 --- a/x-pack/plugins/beats/server/lib/client/index.js +++ b/x-pack/plugins/beats/server/lib/client/index.js @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ +export { callWithRequestFactory } from './call_with_request_factory'; export { callWithInternalUserFactory } from './call_with_internal_user_factory'; diff --git a/x-pack/plugins/beats/server/routes/api/index.js b/x-pack/plugins/beats/server/routes/api/index.js index 8bf546045fe40..07d923876ee79 100644 --- a/x-pack/plugins/beats/server/routes/api/index.js +++ b/x-pack/plugins/beats/server/routes/api/index.js @@ -5,7 +5,9 @@ */ import { registerCreateEnrollmentTokensRoute } from './register_create_enrollment_tokens_route'; +import { registerEnrollBeatRoute } from './register_enroll_beat_route'; export function registerApiRoutes(server) { registerCreateEnrollmentTokensRoute(server); + registerEnrollBeatRoute(server); } diff --git a/x-pack/plugins/beats/server/routes/api/register_create_enrollment_tokens_route.js b/x-pack/plugins/beats/server/routes/api/register_create_enrollment_tokens_route.js index 582ae59062d8b..87ae30cd0e532 100644 --- a/x-pack/plugins/beats/server/routes/api/register_create_enrollment_tokens_route.js +++ b/x-pack/plugins/beats/server/routes/api/register_create_enrollment_tokens_route.js @@ -12,7 +12,7 @@ import { flatten } from 'lodash'; import { INDEX_NAMES } from '../../../common/constants'; -import { callWithRequestFactory } from '../../lib/call_with_request_factory'; +import { callWithRequestFactory } from '../../lib/client'; import { wrapEsError } from '../../lib/error_wrappers'; function persistTokens(callWithRequest, tokens, enrollmentTokensTtlInSeconds) { diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js new file mode 100644 index 0000000000000..0a49ac95bdfa1 --- /dev/null +++ b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Joi from 'joi'; +import uuid from 'uuid'; +import { INDEX_NAMES } from '../../../common/constants'; +import { callWithInternalUserFactory } from '../../lib/client'; +import { wrapEsError } from '../../lib/error_wrappers'; + +function persistBeat(callWithInternalUser, beat, beatId, accessToken) { + const body = { + type: 'beat', + beat: { ...beat, id: beatId, access_token: accessToken } + }; + + const params = { + index: INDEX_NAMES.BEATS, + type: '_doc', + id: `beat:${beatId}`, + body, + refresh: 'wait_for' + }; + return callWithInternalUser('index', params); +} + +// TODO: add license check pre-hook +// TODO: write to Kibana audit log file +export function registerEnrollBeatRoute(server) { + server.route({ + method: 'POST', + path: '/api/beats/agent/{beatId}', + config: { + validate: { + payload: Joi.object({ + enrollment_token: Joi.string().required(), + type: Joi.string().required(), + host_name: Joi.string().required(), + host_ip: Joi.string().required() + }).required() + }, + auth: false + }, + handler: async (request, reply) => { + const callWithInternalUser = callWithInternalUserFactory(server); + const accessToken = uuid.v4().replace(/-/g, ""); + + try { + await persistBeat(callWithInternalUser, request.payload, request.params.beatId, accessToken); + } catch (err) { + return reply(wrapEsError(err)); + } + + const response = { access_token: accessToken }; + reply(response).code(201); + } + }); +} diff --git a/x-pack/plugins/beats/server/lib/call_with_request_factory/index.js b/x-pack/test/api_integration/apis/beats/constants.js similarity index 73% rename from x-pack/plugins/beats/server/lib/call_with_request_factory/index.js rename to x-pack/test/api_integration/apis/beats/constants.js index 787814d87dff9..00327aface171 100644 --- a/x-pack/plugins/beats/server/lib/call_with_request_factory/index.js +++ b/x-pack/test/api_integration/apis/beats/constants.js @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { callWithRequestFactory } from './call_with_request_factory'; +export const ES_INDEX_NAME = '.management-beats'; +export const ES_TYPE_NAME = '_doc'; + diff --git a/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js b/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js index a12849d7a1c34..86b80323773b4 100644 --- a/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js +++ b/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js @@ -6,26 +6,17 @@ import expect from 'expect.js'; import moment from 'moment'; +import { + ES_INDEX_NAME, + ES_TYPE_NAME +} from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); const chance = getService('chance'); const es = getService('es'); - const ES_INDEX_NAME = '.management-beats'; - const ES_TYPE_NAME = '_doc'; - - describe('create_enrollment_tokens', () => { - const cleanup = () => { - return es.indices.delete({ - index: ES_INDEX_NAME, - ignore: [ 404 ] - }); - }; - - beforeEach(cleanup); - afterEach(cleanup); - + describe('create_enrollment_token', () => { it('should create one token by default', async () => { const { body: apiResponse } = await supertest .post( diff --git a/x-pack/test/api_integration/apis/beats/enroll_beat.js b/x-pack/test/api_integration/apis/beats/enroll_beat.js new file mode 100644 index 0000000000000..32e927badd130 --- /dev/null +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from 'expect.js'; +import { + ES_INDEX_NAME, + ES_TYPE_NAME +} from './constants'; + +export default function ({ getService }) { + const supertest = getService('supertest'); + const chance = getService('chance'); + const es = getService('es'); + + describe('enroll_beat', () => { + let beatId; + let beat; + beforeEach(() => { + beatId = chance.word(); + beat = { + enrollment_token: chance.string(), + type: 'filebeat', + host_ip: '11.22.33.44', + host_name: 'foo.bar.com', + }; + }); + + it('should enroll beat in an unverified state', async () => { + await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(201); + + const esResponse = await es.get({ + index: ES_INDEX_NAME, + type: ES_TYPE_NAME, + id: `beat:${beatId}` + }); + + expect(esResponse._source.beat).to.not.have.property('verified_on'); + }); + + it('should contain an access token in the response', async () => { + const { body: apiResponse } = await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(201); + + const accessTokenFromApi = apiResponse.access_token; + + const esResponse = await es.get({ + index: ES_INDEX_NAME, + type: ES_TYPE_NAME, + id: `beat:${beatId}` + }); + + const accessTokenInEs = esResponse._source.beat.access_token; + + expect(accessTokenFromApi.length).to.be.greaterThan(0); + expect(accessTokenFromApi).to.eql(accessTokenInEs); + }); + + }); +} diff --git a/x-pack/test/api_integration/apis/beats/index.js b/x-pack/test/api_integration/apis/beats/index.js index f8344895f02aa..b5fd2f58df39d 100644 --- a/x-pack/test/api_integration/apis/beats/index.js +++ b/x-pack/test/api_integration/apis/beats/index.js @@ -4,8 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -export default function ({ loadTestFile }) { +import { ES_INDEX_NAME } from './constants'; + +export default function ({ getService, loadTestFile }) { + const es = getService('es'); + describe('beats', () => { - loadTestFile(require.resolve('./create_enrollment_tokens')); + const cleanup = () => es.indices.delete({ + index: ES_INDEX_NAME, + ignore: [ 404 ] + }); + + beforeEach(cleanup); + + loadTestFile(require.resolve('./create_enrollment_token')); + loadTestFile(require.resolve('./enroll_beat')); }); } From cd5412c9223d67e91efede1ba3b0a10844bac4c2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 14 May 2018 18:43:12 -0700 Subject: [PATCH 05/11] Handle invalid or expired enrollment tokens --- .../routes/api/register_enroll_beat_route.js | 38 ++++++++- .../api_integration/apis/beats/enroll_beat.js | 83 ++++++++++++++++++- 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js index 0a49ac95bdfa1..cc043b6bf42f4 100644 --- a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js +++ b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js @@ -6,10 +6,34 @@ import Joi from 'joi'; import uuid from 'uuid'; +import moment from 'moment'; +import { get } from 'lodash'; import { INDEX_NAMES } from '../../../common/constants'; import { callWithInternalUserFactory } from '../../lib/client'; import { wrapEsError } from '../../lib/error_wrappers'; +async function getEnrollmentToken(callWithInternalUser, enrollmentToken) { + const params = { + index: INDEX_NAMES.BEATS, + type: '_doc', + id: `enrollment_token:${enrollmentToken}`, + ignore: [ 404 ] + }; + + const response = await callWithInternalUser('get', params); + return get(response, '_source.enrollment_token', {}); +} + +function deleteUsedEnrollmentToken(callWithInternalUser, enrollmentToken) { + const params = { + index: INDEX_NAMES.BEATS, + type: '_doc', + id: `enrollment_token:${enrollmentToken}` + }; + + return callWithInternalUser('delete', params); +} + function persistBeat(callWithInternalUser, beat, beatId, accessToken) { const body = { type: 'beat', @@ -45,10 +69,22 @@ export function registerEnrollBeatRoute(server) { }, handler: async (request, reply) => { const callWithInternalUser = callWithInternalUserFactory(server); - const accessToken = uuid.v4().replace(/-/g, ""); + let accessToken; try { + const enrollmentToken = request.payload.enrollment_token; + const { token, expires_on: expiresOn } = await getEnrollmentToken(callWithInternalUser, enrollmentToken); + if (!token || token !== enrollmentToken) { + return reply({ message: 'Invalid enrollment token' }).code(400); + } + if (moment(expiresOn).isBefore(moment())) { + return reply({ message: 'Expired enrollment token' }).code(400); + } + + accessToken = uuid.v4().replace(/-/g, ""); await persistBeat(callWithInternalUser, request.payload, request.params.beatId, accessToken); + + await deleteUsedEnrollmentToken(callWithInternalUser, enrollmentToken); } catch (err) { return reply(wrapEsError(err)); } diff --git a/x-pack/test/api_integration/apis/beats/enroll_beat.js b/x-pack/test/api_integration/apis/beats/enroll_beat.js index 32e927badd130..738adb28ec4f5 100644 --- a/x-pack/test/api_integration/apis/beats/enroll_beat.js +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -5,6 +5,7 @@ */ import expect from 'expect.js'; +import moment from 'moment'; import { ES_INDEX_NAME, ES_TYPE_NAME @@ -16,16 +17,32 @@ export default function ({ getService }) { const es = getService('es'); describe('enroll_beat', () => { + let validEnrollmentToken; let beatId; let beat; - beforeEach(() => { + + beforeEach(async () => { + validEnrollmentToken = chance.word(); beatId = chance.word(); beat = { - enrollment_token: chance.string(), + enrollment_token: validEnrollmentToken, type: 'filebeat', host_ip: '11.22.33.44', host_name: 'foo.bar.com', }; + + await es.index({ + index: ES_INDEX_NAME, + type: ES_TYPE_NAME, + id: `enrollment_token:${validEnrollmentToken}`, + body: { + type: 'enrollment_token', + enrollment_token: { + token: validEnrollmentToken, + expires_on: moment().add(4, 'hours').toJSON() + } + } + }); }); it('should enroll beat in an unverified state', async () => { @@ -69,5 +86,67 @@ export default function ({ getService }) { expect(accessTokenFromApi).to.eql(accessTokenInEs); }); + it('should reject an invalid enrollment token', async () => { + const invalidEnrollmentToken = chance.word(); + beat.enrollment_token = invalidEnrollmentToken; + + const { body: apiResponse } = await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(400); + + expect(apiResponse).to.eql({ message: 'Invalid enrollment token' }); + }); + + it('should reject an expired enrollment token', async () => { + const expiredEnrollmentToken = chance.word(); + + await es.index({ + index: ES_INDEX_NAME, + type: ES_TYPE_NAME, + id: `enrollment_token:${expiredEnrollmentToken}`, + body: { + type: 'enrollment_token', + enrollment_token: { + token: expiredEnrollmentToken, + expires_on: moment().subtract(1, 'minute').toJSON() + } + } + }); + + beat.enrollment_token = expiredEnrollmentToken; + + const { body: apiResponse } = await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(400); + + expect(apiResponse).to.eql({ message: 'Expired enrollment token' }); + }); + + it('should delete the given enrollment token so it may not be reused', async () => { + await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(201); + + const esResponse = await es.get({ + index: ES_INDEX_NAME, + type: ES_TYPE_NAME, + id: `enrollment_token:${validEnrollmentToken}`, + ignore: [ 404 ] + }); + + expect(esResponse.found).to.be(false); + }); }); } From 85f80811ca093f8f011c0c99d321a0ca5ce654a2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 15 May 2018 10:10:57 -0700 Subject: [PATCH 06/11] Use create instead of index to prevent same beat from being enrolled twice --- .../beats/server/routes/api/register_enroll_beat_route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js index cc043b6bf42f4..ed1ec54de67a6 100644 --- a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js +++ b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js @@ -47,7 +47,7 @@ function persistBeat(callWithInternalUser, beat, beatId, accessToken) { body, refresh: 'wait_for' }; - return callWithInternalUser('index', params); + return callWithInternalUser('create', params); } // TODO: add license check pre-hook From 42db66ceb927ce626363cfa80d46811cf44aaccd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 15 May 2018 10:11:30 -0700 Subject: [PATCH 07/11] Adding unit test for duplicate beat enrollment --- .../api_integration/apis/beats/enroll_beat.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/x-pack/test/api_integration/apis/beats/enroll_beat.js b/x-pack/test/api_integration/apis/beats/enroll_beat.js index 738adb28ec4f5..7d5e04488d5ab 100644 --- a/x-pack/test/api_integration/apis/beats/enroll_beat.js +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -148,5 +148,36 @@ export default function ({ getService }) { expect(esResponse.found).to.be(false); }); + + it('should fail if the beat with the same ID is enrolled twice', async () => { + await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(201); + + await es.index({ + index: ES_INDEX_NAME, + type: ES_TYPE_NAME, + id: `enrollment_token:${validEnrollmentToken}`, + body: { + type: 'enrollment_token', + enrollment_token: { + token: validEnrollmentToken, + expires_on: moment().add(4, 'hours').toJSON() + } + } + }); + + await supertest + .post( + `/api/beats/agent/${beatId}` + ) + .set('kbn-xsrf', 'xxx') + .send(beat) + .expect(409); + }); }); } From 4a7d26933fec150a08727e0c4fc1288e481e9c49 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 15 May 2018 12:12:08 -0700 Subject: [PATCH 08/11] Do not persist enrollment token with beat once token has been checked and used --- .../server/lib/index_template/beats_template.json | 3 --- .../server/routes/api/register_enroll_beat_route.js | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/beats/server/lib/index_template/beats_template.json b/x-pack/plugins/beats/server/lib/index_template/beats_template.json index 6ef57b9c549ed..5a47658b93ba2 100644 --- a/x-pack/plugins/beats/server/lib/index_template/beats_template.json +++ b/x-pack/plugins/beats/server/lib/index_template/beats_template.json @@ -45,9 +45,6 @@ "id": { "type": "keyword" }, - "enrollment_token": { - "type": "keyword" - }, "access_token": { "type": "keyword" }, diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js index ed1ec54de67a6..500de82ebbd2f 100644 --- a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js +++ b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js @@ -7,7 +7,10 @@ import Joi from 'joi'; import uuid from 'uuid'; import moment from 'moment'; -import { get } from 'lodash'; +import { + get, + omit +} from 'lodash'; import { INDEX_NAMES } from '../../../common/constants'; import { callWithInternalUserFactory } from '../../lib/client'; import { wrapEsError } from '../../lib/error_wrappers'; @@ -37,7 +40,11 @@ function deleteUsedEnrollmentToken(callWithInternalUser, enrollmentToken) { function persistBeat(callWithInternalUser, beat, beatId, accessToken) { const body = { type: 'beat', - beat: { ...beat, id: beatId, access_token: accessToken } + beat: { + ...omit(beat, 'enrollment_token'), + id: beatId, + access_token: accessToken + } }; const params = { From 26c3bedf0dd82957b4c6c7ff64e79007ab20ebfc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 15 May 2018 12:50:07 -0700 Subject: [PATCH 09/11] Fix datatype of host_ip field --- .../plugins/beats/server/lib/index_template/beats_template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/beats/server/lib/index_template/beats_template.json b/x-pack/plugins/beats/server/lib/index_template/beats_template.json index 5a47658b93ba2..9b37b7e816bf8 100644 --- a/x-pack/plugins/beats/server/lib/index_template/beats_template.json +++ b/x-pack/plugins/beats/server/lib/index_template/beats_template.json @@ -55,7 +55,7 @@ "type": "keyword" }, "host_ip": { - "type": "keyword" + "type": "ip" }, "host_name": { "type": "keyword" From fc1f88f3935bf86ddf27f50832c0135b90f22d39 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 15 May 2018 12:50:35 -0700 Subject: [PATCH 10/11] Make Kibana API guess host IP instead of requiring it in payload --- .../server/routes/api/register_enroll_beat_route.js | 11 ++++++----- x-pack/test/api_integration/apis/beats/enroll_beat.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js index 500de82ebbd2f..fb004fbb79e12 100644 --- a/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js +++ b/x-pack/plugins/beats/server/routes/api/register_enroll_beat_route.js @@ -37,13 +37,14 @@ function deleteUsedEnrollmentToken(callWithInternalUser, enrollmentToken) { return callWithInternalUser('delete', params); } -function persistBeat(callWithInternalUser, beat, beatId, accessToken) { +function persistBeat(callWithInternalUser, beat, beatId, accessToken, remoteAddress) { const body = { type: 'beat', beat: { ...omit(beat, 'enrollment_token'), id: beatId, - access_token: accessToken + access_token: accessToken, + host_ip: remoteAddress } }; @@ -68,8 +69,7 @@ export function registerEnrollBeatRoute(server) { payload: Joi.object({ enrollment_token: Joi.string().required(), type: Joi.string().required(), - host_name: Joi.string().required(), - host_ip: Joi.string().required() + host_name: Joi.string().required() }).required() }, auth: false @@ -89,7 +89,8 @@ export function registerEnrollBeatRoute(server) { } accessToken = uuid.v4().replace(/-/g, ""); - await persistBeat(callWithInternalUser, request.payload, request.params.beatId, accessToken); + const remoteAddress = request.info.remoteAddress; + await persistBeat(callWithInternalUser, request.payload, request.params.beatId, accessToken, remoteAddress); await deleteUsedEnrollmentToken(callWithInternalUser, enrollmentToken); } catch (err) { diff --git a/x-pack/test/api_integration/apis/beats/enroll_beat.js b/x-pack/test/api_integration/apis/beats/enroll_beat.js index 7d5e04488d5ab..ec3785f8eb35d 100644 --- a/x-pack/test/api_integration/apis/beats/enroll_beat.js +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -27,7 +27,6 @@ export default function ({ getService }) { beat = { enrollment_token: validEnrollmentToken, type: 'filebeat', - host_ip: '11.22.33.44', host_name: 'foo.bar.com', }; @@ -61,6 +60,7 @@ export default function ({ getService }) { }); expect(esResponse._source.beat).to.not.have.property('verified_on'); + expect(esResponse._source.beat).to.have.property('host_ip'); }); it('should contain an access token in the response', async () => { From 9e06a746117cd9399a13d90410dbd1e221e256f6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 16 May 2018 06:17:49 -0700 Subject: [PATCH 11/11] Fixing error introduced in rebase conflict resolution --- x-pack/test/api_integration/apis/beats/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/beats/index.js b/x-pack/test/api_integration/apis/beats/index.js index b5fd2f58df39d..dc6137f979019 100644 --- a/x-pack/test/api_integration/apis/beats/index.js +++ b/x-pack/test/api_integration/apis/beats/index.js @@ -17,7 +17,7 @@ export default function ({ getService, loadTestFile }) { beforeEach(cleanup); - loadTestFile(require.resolve('./create_enrollment_token')); + loadTestFile(require.resolve('./create_enrollment_tokens')); loadTestFile(require.resolve('./enroll_beat')); }); }