-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Beats Management] Move tokens to use JWT, add more complete test suite #20317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cb372ed
1ef3cf6
010abbc
8c35056
fcac739
39007e3
cb11e13
b864956
f0e60f1
5040309
634aab5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Documentation for Beats CM in x-pack kibana | ||
|
|
||
| ### Run tests | ||
|
|
||
| ``` | ||
| node scripts/jest.js plugins/beats --watch | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ | |
| * you may not use this file except in compliance with the Elastic License. | ||
| */ | ||
|
|
||
| import { flatten, get, omit } from 'lodash'; | ||
| import { flatten, get as _get, omit } from 'lodash'; | ||
| import moment from 'moment'; | ||
| import { INDEX_NAMES } from '../../../../common/constants'; | ||
| import { | ||
|
|
@@ -35,7 +35,7 @@ export class ElasticsearchBeatsAdapter implements CMBeatsAdapter { | |
| return null; | ||
| } | ||
|
|
||
| return get(response, '_source.beat'); | ||
| return _get(response, '_source.beat'); | ||
| } | ||
|
|
||
| public async insert(beat: CMBeat) { | ||
|
|
@@ -73,22 +73,6 @@ export class ElasticsearchBeatsAdapter implements CMBeatsAdapter { | |
| public async getWithIds(req: FrameworkRequest, beatIds: string[]) { | ||
| const ids = beatIds.map(beatId => `beat:${beatId}`); | ||
|
|
||
| const params = { | ||
| _source: false, | ||
| body: { | ||
| ids, | ||
| }, | ||
| index: INDEX_NAMES.BEATS, | ||
| type: '_doc', | ||
| }; | ||
| const response = await this.framework.callWithRequest(req, 'mget', params); | ||
| return get(response, 'docs', []); | ||
| } | ||
|
|
||
| // TODO merge with getBeatsWithIds | ||
| public async getVerifiedWithIds(req: FrameworkRequest, beatIds: string[]) { | ||
| const ids = beatIds.map(beatId => `beat:${beatId}`); | ||
|
|
||
| const params = { | ||
| _sourceInclude: ['beat.id', 'beat.verified_on'], | ||
| body: { | ||
|
|
@@ -98,7 +82,10 @@ export class ElasticsearchBeatsAdapter implements CMBeatsAdapter { | |
| type: '_doc', | ||
| }; | ||
| const response = await this.framework.callWithRequest(req, 'mget', params); | ||
| return get(response, 'docs', []); | ||
|
|
||
| return get(response, 'docs', []) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More of an observation than a requested change - I found the use of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a fair argument. Any thoughts on a better name? I just used get because it is a CRUD term, and it did not duplicate as I thought a beats adapter, with all of its methods referring to beats might feel odd to say getBeats... So get was used for lack of imagination on my part.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I think
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah good idea
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| .filter((b: any) => b.found) | ||
| .map((b: any) => b._source.beat); | ||
| } | ||
|
|
||
| public async verifyBeats(req: FrameworkRequest, beatIds: string[]) { | ||
|
|
@@ -115,14 +102,19 @@ export class ElasticsearchBeatsAdapter implements CMBeatsAdapter { | |
| ); | ||
|
|
||
| const params = { | ||
| _sourceInclude: ['beat.id', 'beat.verified_on'], | ||
| body, | ||
| index: INDEX_NAMES.BEATS, | ||
| refresh: 'wait_for', | ||
| type: '_doc', | ||
| }; | ||
|
|
||
| const response = await this.framework.callWithRequest(req, 'bulk', params); | ||
| return get(response, 'items', []); | ||
|
|
||
| return _get(response, 'items', []).map(b => ({ | ||
| ..._get(b, 'update.get._source.beat', {}), | ||
| updateStatus: _get(b, 'update.result', 'unknown error'), | ||
| })); | ||
| } | ||
|
|
||
| public async getAll(req: FrameworkRequest) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| /* | ||
| * 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 { omit } from 'lodash'; | ||
| import moment from 'moment'; | ||
|
|
||
| import { | ||
| CMBeat, | ||
| CMBeatsAdapter, | ||
| CMTagAssignment, | ||
| FrameworkRequest, | ||
| } from '../../lib'; | ||
|
|
||
| export class MemoryBeatsAdapter implements CMBeatsAdapter { | ||
| private beatsDB: CMBeat[]; | ||
|
|
||
| constructor(beatsDB: CMBeat[]) { | ||
| this.beatsDB = beatsDB; | ||
| } | ||
|
|
||
| public async get(id: string) { | ||
| return this.beatsDB.find(beat => beat.id === id); | ||
| } | ||
|
|
||
| public async insert(beat: CMBeat) { | ||
| this.beatsDB.push(beat); | ||
| } | ||
|
|
||
| public async update(beat: CMBeat) { | ||
| const beatIndex = this.beatsDB.findIndex(b => b.id === beat.id); | ||
|
|
||
| this.beatsDB[beatIndex] = { | ||
| ...this.beatsDB[beatIndex], | ||
| ...beat, | ||
| }; | ||
| } | ||
|
|
||
| public async getWithIds(req: FrameworkRequest, beatIds: string[]) { | ||
| return this.beatsDB.filter(beat => beatIds.includes(beat.id)); | ||
| } | ||
|
|
||
| public async verifyBeats(req: FrameworkRequest, beatIds: string[]) { | ||
| if (!Array.isArray(beatIds) || beatIds.length === 0) { | ||
| return []; | ||
| } | ||
|
|
||
| const verifiedOn = moment().toJSON(); | ||
|
|
||
| this.beatsDB.forEach((beat, i) => { | ||
| if (beatIds.includes(beat.id)) { | ||
| this.beatsDB[i].verified_on = verifiedOn; | ||
| } | ||
| }); | ||
|
|
||
| return this.beatsDB.filter(beat => beatIds.includes(beat.id)); | ||
| } | ||
|
|
||
| public async getAll(req: FrameworkRequest) { | ||
| return this.beatsDB.map((beat: any) => omit(beat, ['access_token'])); | ||
| } | ||
|
|
||
| public async removeTagsFromBeats( | ||
| req: FrameworkRequest, | ||
| removals: CMTagAssignment[] | ||
| ): Promise<CMTagAssignment[]> { | ||
| const beatIds = removals.map(r => r.beatId); | ||
|
|
||
| const response = this.beatsDB | ||
| .filter(beat => beatIds.includes(beat.id)) | ||
| .map(beat => { | ||
| const tagData = removals.find(r => r.beatId === beat.id); | ||
| if (tagData) { | ||
| if (beat.tags) { | ||
| beat.tags = beat.tags.filter(tag => tag !== tagData.tag); | ||
| } | ||
| } | ||
| return beat; | ||
| }); | ||
|
|
||
| return response.map<any>((item: CMBeat, resultIdx: number) => ({ | ||
| idxInRequest: removals[resultIdx].idxInRequest, | ||
| result: 'updated', | ||
| status: 200, | ||
| })); | ||
| } | ||
|
|
||
| public async assignTagsToBeats( | ||
| req: FrameworkRequest, | ||
| assignments: CMTagAssignment[] | ||
| ): Promise<CMTagAssignment[]> { | ||
| const beatIds = assignments.map(r => r.beatId); | ||
|
|
||
| this.beatsDB.filter(beat => beatIds.includes(beat.id)).map(beat => { | ||
| // get tags that need to be assigned to this beat | ||
| const tags = assignments | ||
| .filter(a => a.beatId === beat.id) | ||
| .map((t: CMTagAssignment) => t.tag); | ||
|
|
||
| if (tags.length > 0) { | ||
| if (!beat.tags) { | ||
| beat.tags = []; | ||
| } | ||
| const nonExistingTags = tags.filter( | ||
| (t: string) => beat.tags && !beat.tags.includes(t) | ||
| ); | ||
|
|
||
| if (nonExistingTags.length > 0) { | ||
| beat.tags = beat.tags.concat(nonExistingTags); | ||
| } | ||
| } | ||
| return beat; | ||
| }); | ||
|
|
||
| return assignments.map<any>((item: CMTagAssignment, resultIdx: number) => ({ | ||
| idxInRequest: assignments[resultIdx].idxInRequest, | ||
| result: 'updated', | ||
| status: 200, | ||
| })); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /* | ||
| * 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 { Client } from 'elasticsearch'; | ||
| import { Request } from 'hapi'; | ||
| import { get } from 'lodash'; | ||
| import { | ||
| BackendFrameworkAdapter, | ||
| FrameworkRequest, | ||
| FrameworkRouteOptions, | ||
| WrappableRequest, | ||
| } from '../../../lib'; | ||
|
|
||
| interface TestSettings { | ||
| enrollmentTokensTtlInSeconds: number; | ||
| encryptionKey: string; | ||
| } | ||
|
|
||
| export class TestingBackendFrameworkAdapter implements BackendFrameworkAdapter { | ||
| public version: string; | ||
| private client: Client | null; | ||
| private settings: TestSettings; | ||
|
|
||
| constructor(client: Client | null, settings: TestSettings) { | ||
| this.client = client; | ||
| this.settings = settings || { | ||
| encryptionKey: 'something_who_cares', | ||
| enrollmentTokensTtlInSeconds: 10 * 60, // 10 minutes | ||
| }; | ||
| this.version = 'testing'; | ||
| } | ||
|
|
||
| public getSetting(settingPath: string) { | ||
| switch (settingPath) { | ||
| case 'xpack.beats.enrollmentTokensTtlInSeconds': | ||
| return this.settings.enrollmentTokensTtlInSeconds; | ||
| case 'xpack.beats.encryptionKey': | ||
| return this.settings.encryptionKey; | ||
| } | ||
| } | ||
|
|
||
| public exposeStaticDir(urlPath: string, dir: string): void { | ||
| // not yet testable | ||
| } | ||
|
|
||
| public registerRoute<RouteRequest extends WrappableRequest, RouteResponse>( | ||
| route: FrameworkRouteOptions<RouteRequest, RouteResponse> | ||
| ) { | ||
| // not yet testable | ||
| } | ||
|
|
||
| public installIndexTemplate(name: string, template: {}) { | ||
| if (this.client) { | ||
| return this.client.indices.putTemplate({ | ||
| body: template, | ||
| name, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| public async callWithInternalUser(esMethod: string, options: {}) { | ||
| const api = get<any>(this.client, esMethod); | ||
|
|
||
| api(options); | ||
|
|
||
| return await api(options); | ||
| } | ||
|
|
||
| public async callWithRequest( | ||
| req: FrameworkRequest<Request>, | ||
| esMethod: string, | ||
| options: {} | ||
| ) { | ||
| const api = get<any>(this.client, esMethod); | ||
|
|
||
| api(options); | ||
|
|
||
| return await api(options); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍