-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Beats Management] APIs: Update beat (#19148)
* 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 * Add API tests * Update template to allow version field for beat * Implement PUT /api/beats/agent/{beat ID} API * Make enroll beat code consistent with update beat code * Fixing minor typo in TODO comment * Allow version in request payload * Make sure beat is not updated in ES in error scenarios * Adding version as required field in Enroll Beat API payload * Using destructuring * Fixing rename that was accidentally reversed in conflict fixing
- Loading branch information
1 parent
2cea7a6
commit 6e2c1a3
Showing
9 changed files
with
256 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,9 @@ | |
"type": { | ||
"type": "keyword" | ||
}, | ||
"version": { | ||
"type": "keyword" | ||
}, | ||
"host_ip": { | ||
"type": "ip" | ||
}, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
x-pack/plugins/beats/server/routes/api/register_update_beat_route.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* 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 { get } from 'lodash'; | ||
import { INDEX_NAMES } from '../../../common/constants'; | ||
import { callWithInternalUserFactory } from '../../lib/client'; | ||
import { wrapEsError } from '../../lib/error_wrappers'; | ||
|
||
async function getBeat(callWithInternalUser, beatId) { | ||
const params = { | ||
index: INDEX_NAMES.BEATS, | ||
type: '_doc', | ||
id: `beat:${beatId}`, | ||
ignore: [ 404 ] | ||
}; | ||
|
||
const response = await callWithInternalUser('get', params); | ||
if (!response.found) { | ||
return null; | ||
} | ||
|
||
return get(response, '_source.beat'); | ||
} | ||
|
||
function persistBeat(callWithInternalUser, beat) { | ||
const body = { | ||
type: 'beat', | ||
beat | ||
}; | ||
|
||
const params = { | ||
index: INDEX_NAMES.BEATS, | ||
type: '_doc', | ||
id: `beat:${beat.id}`, | ||
body, | ||
refresh: 'wait_for' | ||
}; | ||
return callWithInternalUser('index', params); | ||
} | ||
|
||
// TODO: add license check pre-hook | ||
// TODO: write to Kibana audit log file (include who did the verification as well) | ||
export function registerUpdateBeatRoute(server) { | ||
server.route({ | ||
method: 'PUT', | ||
path: '/api/beats/agent/{beatId}', | ||
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() | ||
}, | ||
auth: false | ||
}, | ||
handler: async (request, reply) => { | ||
const callWithInternalUser = callWithInternalUserFactory(server); | ||
const { beatId } = request.params; | ||
|
||
try { | ||
const beat = await getBeat(callWithInternalUser, beatId); | ||
if (beat === null) { | ||
return reply({ message: 'Beat not found' }).code(404); | ||
} | ||
|
||
const isAccessTokenValid = beat.access_token === request.payload.access_token; | ||
if (!isAccessTokenValid) { | ||
return reply({ message: 'Invalid access token' }).code(401); | ||
} | ||
|
||
const isBeatVerified = beat.hasOwnProperty('verified_on'); | ||
if (!isBeatVerified) { | ||
return reply({ message: 'Beat has not been verified' }).code(400); | ||
} | ||
|
||
const remoteAddress = request.info.remoteAddress; | ||
await persistBeat(callWithInternalUser, { | ||
...beat, | ||
...request.payload, | ||
host_ip: remoteAddress | ||
}); | ||
} catch (err) { | ||
return reply(wrapEsError(err)); | ||
} | ||
|
||
reply().code(204); | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* 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'); | ||
const esArchiver = getService('esArchiver'); | ||
|
||
describe('update_beat', () => { | ||
let beat; | ||
const archive = 'beats/list'; | ||
|
||
beforeEach('load beats archive', () => esArchiver.load(archive)); | ||
beforeEach(() => { | ||
const version = chance.integer({ min: 1, max: 10 }) | ||
+ '.' | ||
+ chance.integer({ min: 1, max: 10 }) | ||
+ '.' | ||
+ chance.integer({ min: 1, max: 10 }); | ||
|
||
beat = { | ||
access_token: '93c4a4dd08564c189a7ec4e4f046b975', | ||
type: `${chance.word()}beat`, | ||
host_name: `www.${chance.word()}.net`, | ||
version, | ||
ephemeral_id: chance.word() | ||
}; | ||
}); | ||
|
||
afterEach('unload beats archive', () => esArchiver.unload(archive)); | ||
|
||
it('should update an existing verified beat', async () => { | ||
const beatId = 'foo'; | ||
await supertest | ||
.put( | ||
`/api/beats/agent/${beatId}` | ||
) | ||
.set('kbn-xsrf', 'xxx') | ||
.send(beat) | ||
.expect(204); | ||
|
||
const beatInEs = await es.get({ | ||
index: ES_INDEX_NAME, | ||
type: ES_TYPE_NAME, | ||
id: `beat:${beatId}` | ||
}); | ||
|
||
expect(beatInEs._source.beat.id).to.be(beatId); | ||
expect(beatInEs._source.beat.type).to.be(beat.type); | ||
expect(beatInEs._source.beat.host_name).to.be(beat.host_name); | ||
expect(beatInEs._source.beat.version).to.be(beat.version); | ||
expect(beatInEs._source.beat.ephemeral_id).to.be(beat.ephemeral_id); | ||
}); | ||
|
||
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') | ||
.send(beat) | ||
.expect(401); | ||
|
||
expect(body.message).to.be('Invalid access token'); | ||
|
||
const beatInEs = await es.get({ | ||
index: ES_INDEX_NAME, | ||
type: ES_TYPE_NAME, | ||
id: `beat:${beatId}` | ||
}); | ||
|
||
expect(beatInEs._source.beat.id).to.be(beatId); | ||
expect(beatInEs._source.beat.type).to.not.be(beat.type); | ||
expect(beatInEs._source.beat.host_name).to.not.be(beat.host_name); | ||
expect(beatInEs._source.beat.version).to.not.be(beat.version); | ||
expect(beatInEs._source.beat.ephemeral_id).to.not.be(beat.ephemeral_id); | ||
}); | ||
|
||
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') | ||
.send(beat) | ||
.expect(400); | ||
|
||
expect(body.message).to.be('Beat has not been verified'); | ||
|
||
const beatInEs = await es.get({ | ||
index: ES_INDEX_NAME, | ||
type: ES_TYPE_NAME, | ||
id: `beat:${beatId}` | ||
}); | ||
|
||
expect(beatInEs._source.beat.id).to.be(beatId); | ||
expect(beatInEs._source.beat.type).to.not.be(beat.type); | ||
expect(beatInEs._source.beat.host_name).to.not.be(beat.host_name); | ||
expect(beatInEs._source.beat.version).to.not.be(beat.version); | ||
expect(beatInEs._source.beat.ephemeral_id).to.not.be(beat.ephemeral_id); | ||
}); | ||
|
||
it('should return an error for a non-existent beat', async () => { | ||
const beatId = chance.word(); | ||
const { body } = await supertest | ||
.put( | ||
`/api/beats/agent/${beatId}` | ||
) | ||
.set('kbn-xsrf', 'xxx') | ||
.send(beat) | ||
.expect(404); | ||
|
||
expect(body.message).to.be('Beat not found'); | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,9 @@ | |
"type": { | ||
"type": "keyword" | ||
}, | ||
"version": { | ||
"type": "keyword" | ||
}, | ||
"host_ip": { | ||
"type": "ip" | ||
}, | ||
|