From 66eb24cb3637bdfe8a08da7b4115dce2a0556947 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 22 May 2018 10:51:08 -0700 Subject: [PATCH] [Beats Management] APIs: take auth tokens via headers (#19210) * WIP checkin * WIP checkin * Add API integration test * Converting to Jest test * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Creating POST /api/beats/agents/verify API * Refactoring: extracting out helper functions * Expanding TODO note so I won't forget :) * Fixing file name * Updating mapping * Fixing minor typo in TODO comment * Make "Enroll Beat" API take enrollment token via header instead of request body * Make "Update Beat" API take access token via header instead of request body --- .../routes/api/register_enroll_beat_route.js | 8 +++++--- .../routes/api/register_update_beat_route.js | 8 +++++--- .../routes/api/register_verify_beats_route.js | 16 ++++++++-------- .../api_integration/apis/beats/enroll_beat.js | 13 +++++++------ .../api_integration/apis/beats/update_beat.js | 7 ++++--- 5 files changed, 29 insertions(+), 23 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 2a86f33b0d28f..07e336a1e091b 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 @@ -62,11 +62,13 @@ export function registerEnrollBeatRoute(server) { config: { validate: { payload: Joi.object({ - enrollment_token: Joi.string().required(), type: Joi.string().required(), version: Joi.string().required(), host_name: Joi.string().required() - }).required() + }).required(), + headers: Joi.object({ + 'kbn-beats-enrollment-token': Joi.string().required() + }).options({ allowUnknown: true }) }, auth: false }, @@ -76,7 +78,7 @@ export function registerEnrollBeatRoute(server) { let accessToken; try { - const enrollmentToken = request.payload.enrollment_token; + const enrollmentToken = request.headers['kbn-beats-enrollment-token']; const { token, expires_on: expiresOn } = await getEnrollmentToken(callWithInternalUser, enrollmentToken); if (!token || token !== enrollmentToken) { return reply({ message: 'Invalid enrollment token' }).code(400); diff --git a/x-pack/plugins/beats/server/routes/api/register_update_beat_route.js b/x-pack/plugins/beats/server/routes/api/register_update_beat_route.js index c93eca7590454..fe615ffe1a11c 100644 --- a/x-pack/plugins/beats/server/routes/api/register_update_beat_route.js +++ b/x-pack/plugins/beats/server/routes/api/register_update_beat_route.js @@ -51,14 +51,16 @@ export function registerUpdateBeatRoute(server) { config: { validate: { payload: Joi.object({ - access_token: Joi.string().required(), type: Joi.string(), version: Joi.string(), host_name: Joi.string(), ephemeral_id: Joi.string(), local_configuration_yml: Joi.string(), metadata: Joi.object() - }).required() + }).required(), + headers: Joi.object({ + 'kbn-beats-access-token': Joi.string().required() + }).options({ allowUnknown: true }) }, auth: false }, @@ -72,7 +74,7 @@ export function registerUpdateBeatRoute(server) { return reply({ message: 'Beat not found' }).code(404); } - const isAccessTokenValid = beat.access_token === request.payload.access_token; + const isAccessTokenValid = beat.access_token === request.headers['kbn-beats-access-token']; if (!isAccessTokenValid) { return reply({ message: 'Invalid access token' }).code(401); } 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 index b2113029224a5..6aaa61b07c5f8 100644 --- 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 @@ -49,7 +49,7 @@ async function verifyBeats(callWithRequest, beatIds) { return get(response, 'items', []); } -function findNonExistentBeatIds(beatsFromEs, beatIdsFromRequest) { +function determineNonExistentBeatIds(beatsFromEs, beatIdsFromRequest) { return beatsFromEs.reduce((nonExistentBeatIds, beatFromEs, idx) => { if (!beatFromEs.found) { nonExistentBeatIds.push(beatIdsFromRequest[idx]); @@ -58,21 +58,21 @@ function findNonExistentBeatIds(beatsFromEs, beatIdsFromRequest) { }, []); } -function findAlreadyVerifiedBeatIds(beatsFromEs) { +function determineAlreadyVerifiedBeatIds(beatsFromEs) { return beatsFromEs .filter(beat => beat.found) .filter(beat => beat._source.beat.hasOwnProperty('verified_on')) .map(beat => beat._source.beat.id); } -function findToBeVerifiedBeatIds(beatsFromEs) { +function determineToBeVerifiedBeatIds(beatsFromEs) { return beatsFromEs .filter(beat => beat.found) .filter(beat => !beat._source.beat.hasOwnProperty('verified_on')) .map(beat => beat._source.beat.id); } -function findVerifiedBeatIds(verifications, toBeVerifiedBeatIds) { +function determineVerifiedBeatIds(verifications, toBeVerifiedBeatIds) { return verifications.reduce((verifiedBeatIds, verification, idx) => { if (verification.update.status === 200) { verifiedBeatIds.push(toBeVerifiedBeatIds[idx]); @@ -109,12 +109,12 @@ export function registerVerifyBeatsRoute(server) { try { const beatsFromEs = await getBeats(callWithRequest, beatIds); - nonExistentBeatIds = findNonExistentBeatIds(beatsFromEs, beatIds); - alreadyVerifiedBeatIds = findAlreadyVerifiedBeatIds(beatsFromEs); - const toBeVerifiedBeatIds = findToBeVerifiedBeatIds(beatsFromEs); + nonExistentBeatIds = determineNonExistentBeatIds(beatsFromEs, beatIds); + alreadyVerifiedBeatIds = determineAlreadyVerifiedBeatIds(beatsFromEs); + const toBeVerifiedBeatIds = determineToBeVerifiedBeatIds(beatsFromEs); const verifications = await verifyBeats(callWithRequest, toBeVerifiedBeatIds); - verifiedBeatIds = findVerifiedBeatIds(verifications, toBeVerifiedBeatIds); + verifiedBeatIds = determineVerifiedBeatIds(verifications, toBeVerifiedBeatIds); } 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 388987f6d6d22..91317bca976ee 100644 --- a/x-pack/test/api_integration/apis/beats/enroll_beat.js +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -31,7 +31,6 @@ export default function ({ getService }) { + chance.integer({ min: 1, max: 10 }); beat = { - enrollment_token: validEnrollmentToken, type: 'filebeat', host_name: 'foo.bar.com', version @@ -57,6 +56,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-enrollment-token', validEnrollmentToken) .send(beat) .expect(201); @@ -76,6 +76,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-enrollment-token', validEnrollmentToken) .send(beat) .expect(201); @@ -94,14 +95,12 @@ export default function ({ getService }) { }); 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') + .set('kbn-beats-enrollment-token', chance.word()) .send(beat) .expect(400); @@ -124,13 +123,12 @@ export default function ({ getService }) { } }); - beat.enrollment_token = expiredEnrollmentToken; - const { body: apiResponse } = await supertest .post( `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-enrollment-token', expiredEnrollmentToken) .send(beat) .expect(400); @@ -143,6 +141,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-enrollment-token', validEnrollmentToken) .send(beat) .expect(201); @@ -162,6 +161,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-enrollment-token', validEnrollmentToken) .send(beat) .expect(201); @@ -183,6 +183,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-enrollment-token', validEnrollmentToken) .send(beat) .expect(409); }); diff --git a/x-pack/test/api_integration/apis/beats/update_beat.js b/x-pack/test/api_integration/apis/beats/update_beat.js index 09d286be0fcf8..92e5771e0ef4b 100644 --- a/x-pack/test/api_integration/apis/beats/update_beat.js +++ b/x-pack/test/api_integration/apis/beats/update_beat.js @@ -29,7 +29,6 @@ export default function ({ getService }) { + chance.integer({ min: 1, max: 10 }); beat = { - access_token: '93c4a4dd08564c189a7ec4e4f046b975', type: `${chance.word()}beat`, host_name: `www.${chance.word()}.net`, version, @@ -46,6 +45,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-access-token', '93c4a4dd08564c189a7ec4e4f046b975') .send(beat) .expect(204); @@ -64,12 +64,12 @@ export default function ({ getService }) { it('should return an error for an invalid access token', async () => { const beatId = 'foo'; - beat.access_token = chance.word(); const { body } = await supertest .put( `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-access-token', chance.word()) .send(beat) .expect(401); @@ -90,12 +90,12 @@ export default function ({ getService }) { it('should return an error for an existing but unverified beat', async () => { const beatId = 'bar'; - beat.access_token = '3c4a4dd08564c189a7ec4e4f046b9759'; const { body } = await supertest .put( `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-access-token', '3c4a4dd08564c189a7ec4e4f046b9759') .send(beat) .expect(400); @@ -121,6 +121,7 @@ export default function ({ getService }) { `/api/beats/agent/${beatId}` ) .set('kbn-xsrf', 'xxx') + .set('kbn-beats-access-token', chance.word()) .send(beat) .expect(404);