From 67d57145bb0ba718825e018a7e267886877c86b6 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Wed, 18 Mar 2020 19:22:13 -0400 Subject: [PATCH 1/8] EMT-248: add resource to allow to post new agent action. --- .../ingest_manager/common/constants/routes.ts | 1 + .../common/types/models/agent.ts | 2 + .../common/types/rest_spec/agent.ts | 16 +- .../routes/agent/actions_handlers.test.ts | 93 +++++++++ .../server/routes/agent/actions_handlers.ts | 42 ++++ .../server/routes/agent/index.ts | 16 ++ .../server/services/agents/actions.test.ts | 62 ++++++ .../server/services/agents/actions.ts | 58 ++++++ .../server/services/agents/index.ts | 1 + .../server/types/models/agent.ts | 11 ++ .../server/types/rest_spec/agent.ts | 13 +- .../apis/fleet/agents/actions.ts | 183 ++++++++++++++++++ .../test/api_integration/apis/fleet/index.js | 1 + 13 files changed, 496 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/actions.ts create mode 100644 x-pack/test/api_integration/apis/fleet/agents/actions.ts diff --git a/x-pack/plugins/ingest_manager/common/constants/routes.ts b/x-pack/plugins/ingest_manager/common/constants/routes.ts index 1dc98f9bc89476..5bf7c910168c0a 100644 --- a/x-pack/plugins/ingest_manager/common/constants/routes.ts +++ b/x-pack/plugins/ingest_manager/common/constants/routes.ts @@ -50,6 +50,7 @@ export const AGENT_API_ROUTES = { EVENTS_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/events`, CHECKIN_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/checkin`, ACKS_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/acks`, + ACTIONS_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/actions`, ENROLL_PATTERN: `${FLEET_API_ROOT}/agents/enroll`, UNENROLL_PATTERN: `${FLEET_API_ROOT}/agents/unenroll`, STATUS_PATTERN: `${FLEET_API_ROOT}/agent-status`, diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index ad06e8d3c9c11b..bd075cf3142364 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -22,6 +22,8 @@ export interface AgentAction extends SavedObjectAttributes { sent_at?: string; } +export type NewAgentAction = Pick; + export interface AgentEvent { type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; subtype: // State diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index 7bbaf42422bb25..21ab41740ce3e2 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Agent, AgentAction, AgentEvent, AgentStatus, AgentType } from '../models'; +import { Agent, AgentAction, AgentEvent, AgentStatus, AgentType, NewAgentAction } from '../models'; export interface GetAgentsRequest { query: { @@ -81,6 +81,20 @@ export interface PostAgentAcksResponse { success: boolean; } +export interface PostNewAgentActionRequest { + body: { + action: NewAgentAction; + }; + params: { + agentId: string; + }; +} + +export interface PostNewAgentActionResponse { + success: boolean; + item: AgentAction; +} + export interface PostAgentUnenrollRequest { body: { kuery: string } | { ids: string[] }; } diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts new file mode 100644 index 00000000000000..418351293121ec --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { NewAgentActionSchema } from '../../types/models'; +import { + KibanaResponseFactory, + RequestHandlerContext, + SavedObjectsClientContract, +} from 'kibana/server'; +import { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; +import { httpServerMock } from '../../../../../../src/core/server/http/http_server.mocks'; +import { ActionsService } from '../../services/agents'; +import { AgentAction } from '../../../common/types/models'; +import { postNewAgentActionHandlerBuilder } from './actions_handlers'; +import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; + +describe('test actions handlers schema', () => { + it('validate that agent schema is valid', async () => { + expect( + NewAgentActionSchema.validate({ + type: 'CONFIG_CHANGE', + data: 'data', + sent_at: '2020-03-14T19:45:02.620Z', + }) + ).toBeTruthy(); + }); + + it('validate that agent schema is invalid when required properties are not provided', async () => { + expect(() => { + NewAgentActionSchema.validate({ + data: 'data', + sent_at: '2020-03-14T19:45:02.620Z', + }); + }).toThrowError(); + }); +}); + +describe('test actions handlers', () => { + let mockResponse: jest.Mocked; + let mockSavedObjectsClient: jest.Mocked; + + beforeEach(() => { + mockSavedObjectsClient = savedObjectsClientMock.create(); + mockResponse = httpServerMock.createResponseFactory(); + }); + + it('should succeed on valid new agent action', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + headers: { + authorization: 'ApiKey TmVqTDBIQUJsRkw1em52R1ZIUF86NS1NaTItdHFUTHFHbThmQW1Fb0ljUQ==', + }, + body: { + action: { + type: 'CONFIG_CHANGE', + data: 'data', + sent_at: '2020-03-14T19:45:02.620Z', + }, + }, + }); + + const agentAction = ({ + type: 'CONFIG_CHANGE', + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + } as unknown) as AgentAction; + + const actionsService: ActionsService = { + getAgentByAccessAPIKeyId: jest.fn().mockReturnValueOnce({ + id: 'agent', + }), + getSavedObjectsClientContract: jest.fn().mockReturnValueOnce(mockSavedObjectsClient), + updateAgentActions: jest.fn().mockReturnValueOnce(agentAction), + } as jest.Mocked; + + const postNewAgentActionHandler = postNewAgentActionHandlerBuilder(actionsService); + await postNewAgentActionHandler( + ({} as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + const expectedAgentActionResponse = (mockResponse.ok.mock.calls[0][0] + ?.body as unknown) as PostNewAgentActionResponse; + + expect(expectedAgentActionResponse.item).toEqual(agentAction); + expect(expectedAgentActionResponse.success).toEqual(true); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts new file mode 100644 index 00000000000000..d8dc1e97537aa7 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ + +// handlers that handle agent actions request + +import { RequestHandler } from 'kibana/server'; +import { TypeOf } from '@kbn/config-schema'; +import { PostNewAgentActionRequestSchema } from '../../types/rest_spec'; +import { ActionsService } from '../../services/agents'; +import * as APIKeyService from '../../services/api_keys'; +import { NewAgentAction } from '../../../common/types/models'; +import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; + +export const postNewAgentActionHandlerBuilder = function( + actionsService: ActionsService +): RequestHandler< + TypeOf, + undefined, + TypeOf +> { + return async (context, request, response) => { + const soClient = actionsService.getSavedObjectsClientContract(request); + const res = APIKeyService.parseApiKey(request.headers); + const agent = await actionsService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId as string); + const newAgentAction = request.body.action as NewAgentAction; + const savedAgentAction = await actionsService.updateAgentActions( + soClient, + agent, + newAgentAction + ); + + const body: PostNewAgentActionResponse = { + success: true, + item: savedAgentAction, + }; + + return response.ok({ body }); + }; +}; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index 414d2d79e90671..21922caf3e2a11 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -22,6 +22,7 @@ import { PostAgentAcksRequestSchema, PostAgentUnenrollRequestSchema, GetAgentStatusRequestSchema, + PostNewAgentActionRequestSchema, } from '../../types'; import { getAgentsHandler, @@ -37,6 +38,7 @@ import { } from './handlers'; import { postAgentAcksHandlerBuilder } from './acks_handlers'; import * as AgentService from '../../services/agents'; +import { postNewAgentActionHandlerBuilder } from './actions_handlers'; export const registerRoutes = (router: IRouter) => { // Get one @@ -111,6 +113,20 @@ export const registerRoutes = (router: IRouter) => { }) ); + // Agent actions + router.post( + { + path: AGENT_API_ROUTES.ACTIONS_PATTERN, + validate: PostNewAgentActionRequestSchema, + options: { tags: [] }, + }, + postNewAgentActionHandlerBuilder({ + getAgentByAccessAPIKeyId: AgentService.getAgentByAccessAPIKeyId, + getSavedObjectsClientContract: getInternalUserSOClient, + updateAgentActions: AgentService.updateAgentActions, + }) + ); + router.post( { path: AGENT_API_ROUTES.UNENROLL_PATTERN, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts new file mode 100644 index 00000000000000..838c8c797b299a --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -0,0 +1,62 @@ +/* + * 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 { createAgentAction, updateAgentActions } from './actions'; +import { Agent, AgentAction, NewAgentAction } from '../../../common/types/models'; +import { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; +import { AGENT_TYPE_PERMANENT } from '../../../common/constants'; + +interface UpdatedActions { + actions: AgentAction[]; +} + +describe('test agent actions services', () => { + it('should update agent current action with new action', async () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + const newAgentAction: NewAgentAction = { + type: 'CONFIG_CHANGE', + data: 'data', + sent_at: '2020-03-14T19:45:02.620Z', + }; + await updateAgentActions( + mockSavedObjectsClient, + ({ + id: 'id', + type: AGENT_TYPE_PERMANENT, + actions: [ + { + type: 'CONFIG_CHANGE', + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + ], + } as unknown) as Agent, + newAgentAction + ); + + const updatedAgentActions = (mockSavedObjectsClient.update.mock + .calls[0][2] as unknown) as UpdatedActions; + const actualAgentAction = updatedAgentActions.actions.find(action => action?.data === 'data'); + expect(actualAgentAction?.type).toEqual(newAgentAction.type); + expect(actualAgentAction?.data).toEqual(newAgentAction.data); + expect(actualAgentAction?.sent_at).toEqual(newAgentAction.sent_at); + }); + + it('should create agent action from new agent action model', async () => { + const newAgentAction: NewAgentAction = { + type: 'CONFIG_CHANGE', + data: 'data', + sent_at: '2020-03-14T19:45:02.620Z', + }; + const now = new Date(); + const agentAction = createAgentAction(now, newAgentAction); + expect(agentAction.type).toEqual(newAgentAction.type); + expect(agentAction.data).toEqual(newAgentAction.data); + expect(agentAction.sent_at).toEqual(newAgentAction.sent_at); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts new file mode 100644 index 00000000000000..24c141620c145b --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -0,0 +1,58 @@ +/* + * 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 { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; +import uuid from 'uuid'; +import { + Agent, + AgentAction, + AgentSOAttributes, + NewAgentAction, +} from '../../../common/types/models'; +import { AGENT_SAVED_OBJECT_TYPE } from '../../../common/constants'; + +export async function updateAgentActions( + soClient: SavedObjectsClientContract, + agent: Agent, + newAgentAction: NewAgentAction +): Promise { + const agentAction = createAgentAction(new Date(), newAgentAction); + agent.actions.push(agentAction); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { + actions: agent.actions, + }); + return agentAction; +} + +export function createAgentAction(createdAt: Date, newAgentAction: NewAgentAction): AgentAction { + const agentAction: object = { + id: uuid.v4(), + created_at: createdAt.toISOString(), + }; + + Object.assign(agentAction, ...keys(newAgentAction).map(key => ({ [key]: newAgentAction[key] }))); + + return agentAction as AgentAction; +} + +function keys(obj: O): Array { + return Object.keys(obj) as Array; +} + +export interface ActionsService { + getAgentByAccessAPIKeyId: ( + soClient: SavedObjectsClientContract, + accessAPIKeyId: string + ) => Promise; + + getSavedObjectsClientContract: (kibanaRequest: KibanaRequest) => SavedObjectsClientContract; + + updateAgentActions: ( + soClient: SavedObjectsClientContract, + agent: Agent, + newAgentAction: NewAgentAction + ) => Promise; +} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/index.ts b/x-pack/plugins/ingest_manager/server/services/agents/index.ts index 477f081d1900b1..c95c9ecc2a1d88 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/index.ts @@ -12,3 +12,4 @@ export * from './unenroll'; export * from './status'; export * from './crud'; export * from './update'; +export * from './actions'; diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index e0d252faaaf87c..f70b3cf0ed092b 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -52,3 +52,14 @@ export const AckEventSchema = schema.object({ export const AgentEventSchema = schema.object({ ...AgentEventBase, }); + +export const NewAgentActionSchema = schema.object({ + type: schema.oneOf([ + schema.literal('CONFIG_CHANGE'), + schema.literal('DATA_DUMP'), + schema.literal('RESUME'), + schema.literal('PAUSE'), + ]), + data: schema.maybe(schema.string()), + sent_at: schema.maybe(schema.string()), +}); diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index 9fe84c12521add..445eb53bff3d11 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema } from '@kbn/config-schema'; -import { AckEventSchema, AgentEventSchema, AgentTypeSchema } from '../models'; +import { schema} from '@kbn/config-schema'; +import { AckEventSchema, AgentEventSchema, AgentTypeSchema, NewAgentActionSchema } from '../models'; export const GetAgentsRequestSchema = { query: schema.object({ @@ -52,6 +52,15 @@ export const PostAgentAcksRequestSchema = { }), }; +export const PostNewAgentActionRequestSchema = { + body: schema.object({ + action: NewAgentActionSchema, + }), + params: schema.object({ + agentId: schema.string(), + }), +}; + export const PostAgentUnenrollRequestSchema = { body: schema.oneOf([ schema.object({ diff --git a/x-pack/test/api_integration/apis/fleet/agents/actions.ts b/x-pack/test/api_integration/apis/fleet/agents/actions.ts new file mode 100644 index 00000000000000..d622976a59d2d2 --- /dev/null +++ b/x-pack/test/api_integration/apis/fleet/agents/actions.ts @@ -0,0 +1,183 @@ +/* + * 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 '@kbn/expect'; +import uuid from 'uuid'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { getSupertestWithoutAuth } from './services'; + +export default function(providerContext: FtrProviderContext) { + const { getService } = providerContext; + const esArchiver = getService('esArchiver'); + const esClient = getService('es'); + + const supertest = getSupertestWithoutAuth(providerContext); + let apiKey: { id: string; api_key: string }; + + describe('fleet_agents_actions', () => { + before(async () => { + await esArchiver.loadIfNeeded('fleet/agents'); + + const { body: apiKeyBody } = await esClient.security.createApiKey({ + body: { + name: `test access api key: ${uuid.v4()}`, + }, + }); + apiKey = apiKeyBody; + const { + body: { _source: agentDoc }, + } = await esClient.get({ + index: '.kibana', + id: 'agents:agent1', + }); + agentDoc.agents.access_api_key_id = apiKey.id; + await esClient.update({ + index: '.kibana', + id: 'agents:agent1', + refresh: 'true', + body: { + doc: agentDoc, + }, + }); + }); + after(async () => { + await esArchiver.unload('fleet/agents'); + }); + + it('should return a 401 if this a not a valid actions access', async () => { + await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/actions`) + .set('kbn-xsrf', 'xx') + .set('Authorization', 'ApiKey NOT_A_VALID_TOKEN') + .send({ + action_ids: [], + }) + .expect(401); + }); + + it('should return a 200 if this a valid actions request', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/actions`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + action: { + type: 'CONFIG_CHANGE', + data: 'action_data', + sent_at: '2020-03-18T19:45:02.620Z', + }, + }) + .expect(200); + + expect(apiResponse.success).to.be(true); + expect(apiResponse.item.data).to.be('action_data'); + expect(apiResponse.item.sent_at).to.be('2020-03-18T19:45:02.620Z'); + + const { body: agentResponse } = await supertest + .get(`/api/ingest_manager/fleet/agents/agent1`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .expect(200); + + const updatedAction = agentResponse.item.actions.find( + (itemAction: Record) => itemAction?.data === 'action_data' + ); + + expect(updatedAction.type).to.be('CONFIG_CHANGE'); + expect(updatedAction.data).to.be('action_data'); + expect(updatedAction.sent_at).to.be('2020-03-18T19:45:02.620Z'); + }); + + it('should return a 400 when request does not have type information', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/actions`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + action: { + data: 'action_data', + sent_at: '2020-03-18T19:45:02.620Z', + }, + }) + .expect(400); + expect(apiResponse.message).to.eql( + '[request body.action.type]: expected at least one defined value but got [undefined]' + ); + }); + + /* + it('should return a 400 when request event list contains action that does not belong to agent current actions', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/acks`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + events: [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'does-not-exist', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + ], + }) + .expect(400); + expect(apiResponse.message).to.eql('all actions should belong to current agent'); + }); + + it('should return a 400 when request event list contains action types that are not allowed for acknowledgement', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/acks`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + events: [ + { + type: 'ACTION', + subtype: 'FAILED', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + ], + }) + .expect(400); + expect(apiResponse.message).to.eql( + 'ACTION not allowed for acknowledgment only ACTION_RESULT' + ); + });*/ + }); +} diff --git a/x-pack/test/api_integration/apis/fleet/index.js b/x-pack/test/api_integration/apis/fleet/index.js index 69d30291f030bf..547bbb8c7c6ee9 100644 --- a/x-pack/test/api_integration/apis/fleet/index.js +++ b/x-pack/test/api_integration/apis/fleet/index.js @@ -15,5 +15,6 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./agents/acks')); loadTestFile(require.resolve('./enrollment_api_keys/crud')); loadTestFile(require.resolve('./install')); + loadTestFile(require.resolve('./agents/actions')); }); } From 510a0734c14beded8568e168480fa683a44ce794 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Wed, 18 Mar 2020 19:24:02 -0400 Subject: [PATCH 2/8] EMT-248: fix formatting --- x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index 445eb53bff3d11..f94c02ccee40bc 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema} from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; import { AckEventSchema, AgentEventSchema, AgentTypeSchema, NewAgentActionSchema } from '../models'; export const GetAgentsRequestSchema = { From e13cee09fd28c4a453d9c88fa51b6a0e5b26a4c7 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Wed, 18 Mar 2020 22:26:56 -0400 Subject: [PATCH 3/8] EMT-248: cruft removal --- .../routes/agent/actions_handlers.test.ts | 4 +- .../server/routes/agent/actions_handlers.ts | 4 ++ .../server/services/agents/actions.test.ts | 3 +- .../server/services/agents/actions.ts | 3 + .../apis/fleet/agents/actions.ts | 62 ------------------- 5 files changed, 11 insertions(+), 65 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index 418351293121ec..a948616639d77d 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -18,7 +18,7 @@ import { postNewAgentActionHandlerBuilder } from './actions_handlers'; import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; describe('test actions handlers schema', () => { - it('validate that agent schema is valid', async () => { + it('validate that new agent actions schema is valid', async () => { expect( NewAgentActionSchema.validate({ type: 'CONFIG_CHANGE', @@ -28,7 +28,7 @@ describe('test actions handlers schema', () => { ).toBeTruthy(); }); - it('validate that agent schema is invalid when required properties are not provided', async () => { + it('validate that new agent actions schema is invalid when required properties are not provided', async () => { expect(() => { NewAgentActionSchema.validate({ data: 'data', diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index d8dc1e97537aa7..ee3a381029a33c 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -23,9 +23,13 @@ export const postNewAgentActionHandlerBuilder = function( > { return async (context, request, response) => { const soClient = actionsService.getSavedObjectsClientContract(request); + const res = APIKeyService.parseApiKey(request.headers); + const agent = await actionsService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId as string); + const newAgentAction = request.body.action as NewAgentAction; + const savedAgentAction = await actionsService.updateAgentActions( soClient, agent, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts index 838c8c797b299a..c12108b0107e6b 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -14,7 +14,7 @@ interface UpdatedActions { } describe('test agent actions services', () => { - it('should update agent current action with new action', async () => { + it('should update agent current actions with new action', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); const newAgentAction: NewAgentAction = { type: 'CONFIG_CHANGE', @@ -41,6 +41,7 @@ describe('test agent actions services', () => { const updatedAgentActions = (mockSavedObjectsClient.update.mock .calls[0][2] as unknown) as UpdatedActions; + expect(updatedAgentActions.actions.length).toEqual(2); const actualAgentAction = updatedAgentActions.actions.find(action => action?.data === 'data'); expect(actualAgentAction?.type).toEqual(newAgentAction.type); expect(actualAgentAction?.data).toEqual(newAgentAction.data); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 24c141620c145b..4939042dd001fc 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -20,10 +20,13 @@ export async function updateAgentActions( newAgentAction: NewAgentAction ): Promise { const agentAction = createAgentAction(new Date(), newAgentAction); + agent.actions.push(agentAction); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { actions: agent.actions, }); + return agentAction; } diff --git a/x-pack/test/api_integration/apis/fleet/agents/actions.ts b/x-pack/test/api_integration/apis/fleet/agents/actions.ts index d622976a59d2d2..d0b28e5a64a6f6 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/actions.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/actions.ts @@ -117,67 +117,5 @@ export default function(providerContext: FtrProviderContext) { '[request body.action.type]: expected at least one defined value but got [undefined]' ); }); - - /* - it('should return a 400 when request event list contains action that does not belong to agent current actions', async () => { - const { body: apiResponse } = await supertest - .post(`/api/ingest_manager/fleet/agents/agent1/acks`) - .set('kbn-xsrf', 'xx') - .set( - 'Authorization', - `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` - ) - .send({ - events: [ - { - type: 'ACTION_RESULT', - subtype: 'CONFIG', - timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: '48cebde1-c906-4893-b89f-595d943b72a1', - agent_id: 'agent1', - message: 'hello', - payload: 'payload', - }, - { - type: 'ACTION_RESULT', - subtype: 'CONFIG', - timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: 'does-not-exist', - agent_id: 'agent1', - message: 'hello', - payload: 'payload', - }, - ], - }) - .expect(400); - expect(apiResponse.message).to.eql('all actions should belong to current agent'); - }); - - it('should return a 400 when request event list contains action types that are not allowed for acknowledgement', async () => { - const { body: apiResponse } = await supertest - .post(`/api/ingest_manager/fleet/agents/agent1/acks`) - .set('kbn-xsrf', 'xx') - .set( - 'Authorization', - `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` - ) - .send({ - events: [ - { - type: 'ACTION', - subtype: 'FAILED', - timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: '48cebde1-c906-4893-b89f-595d943b72a1', - agent_id: 'agent1', - message: 'hello', - payload: 'payload', - }, - ], - }) - .expect(400); - expect(apiResponse.message).to.eql( - 'ACTION not allowed for acknowledgment only ACTION_RESULT' - ); - });*/ }); } From b47d7a4917ae3d08af5dc8aa253e6d25f5477c5b Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Thu, 19 Mar 2020 11:47:22 -0400 Subject: [PATCH 4/8] EMT-248: fix PR comments, use kibana access --- .../routes/agent/actions_handlers.test.ts | 8 +-- .../server/routes/agent/actions_handlers.ts | 53 +++++++++------ .../server/routes/agent/index.ts | 4 +- .../server/services/agents/actions.ts | 5 +- .../apis/fleet/agents/actions.ts | 67 +++++-------------- 5 files changed, 55 insertions(+), 82 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index a948616639d77d..9072cfc19659d9 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -49,9 +49,6 @@ describe('test actions handlers', () => { it('should succeed on valid new agent action', async () => { const mockRequest = httpServerMock.createKibanaRequest({ - headers: { - authorization: 'ApiKey TmVqTDBIQUJsRkw1em52R1ZIUF86NS1NaTItdHFUTHFHbThmQW1Fb0ljUQ==', - }, body: { action: { type: 'CONFIG_CHANGE', @@ -59,6 +56,9 @@ describe('test actions handlers', () => { sent_at: '2020-03-14T19:45:02.620Z', }, }, + params: { + agentId: 'id', + }, }); const agentAction = ({ @@ -70,7 +70,7 @@ describe('test actions handlers', () => { } as unknown) as AgentAction; const actionsService: ActionsService = { - getAgentByAccessAPIKeyId: jest.fn().mockReturnValueOnce({ + getAgent: jest.fn().mockReturnValueOnce({ id: 'agent', }), getSavedObjectsClientContract: jest.fn().mockReturnValueOnce(mockSavedObjectsClient), diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index ee3a381029a33c..6ea8695e27e712 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -10,7 +10,6 @@ import { RequestHandler } from 'kibana/server'; import { TypeOf } from '@kbn/config-schema'; import { PostNewAgentActionRequestSchema } from '../../types/rest_spec'; import { ActionsService } from '../../services/agents'; -import * as APIKeyService from '../../services/api_keys'; import { NewAgentAction } from '../../../common/types/models'; import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; @@ -22,25 +21,37 @@ export const postNewAgentActionHandlerBuilder = function( TypeOf > { return async (context, request, response) => { - const soClient = actionsService.getSavedObjectsClientContract(request); - - const res = APIKeyService.parseApiKey(request.headers); - - const agent = await actionsService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId as string); - - const newAgentAction = request.body.action as NewAgentAction; - - const savedAgentAction = await actionsService.updateAgentActions( - soClient, - agent, - newAgentAction - ); - - const body: PostNewAgentActionResponse = { - success: true, - item: savedAgentAction, - }; - - return response.ok({ body }); + try { + const soClient = actionsService.getSavedObjectsClientContract(request); + + const agent = await actionsService.getAgent(soClient, request.params.agentId); + + const newAgentAction = request.body.action as NewAgentAction; + + const savedAgentAction = await actionsService.updateAgentActions( + soClient, + agent, + newAgentAction + ); + + const body: PostNewAgentActionResponse = { + success: true, + item: savedAgentAction, + }; + + return response.ok({ body }); + } catch (e) { + if (e.isBoom) { + return response.customError({ + statusCode: e.output.statusCode, + body: { message: e.message }, + }); + } + + return response.customError({ + statusCode: 500, + body: { message: e.message }, + }); + } }; }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index 21922caf3e2a11..d94029b5610010 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -118,10 +118,10 @@ export const registerRoutes = (router: IRouter) => { { path: AGENT_API_ROUTES.ACTIONS_PATTERN, validate: PostNewAgentActionRequestSchema, - options: { tags: [] }, + options: { tags: [`access:${PLUGIN_ID}-all`] }, }, postNewAgentActionHandlerBuilder({ - getAgentByAccessAPIKeyId: AgentService.getAgentByAccessAPIKeyId, + getAgent: AgentService.getAgent, getSavedObjectsClientContract: getInternalUserSOClient, updateAgentActions: AgentService.updateAgentActions, }) diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 4939042dd001fc..d2684e5066f3d1 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -46,10 +46,7 @@ function keys(obj: O): Array { } export interface ActionsService { - getAgentByAccessAPIKeyId: ( - soClient: SavedObjectsClientContract, - accessAPIKeyId: string - ) => Promise; + getAgent: (soClient: SavedObjectsClientContract, agentId: string) => Promise; getSavedObjectsClientContract: (kibanaRequest: KibanaRequest) => SavedObjectsClientContract; diff --git a/x-pack/test/api_integration/apis/fleet/agents/actions.ts b/x-pack/test/api_integration/apis/fleet/agents/actions.ts index d0b28e5a64a6f6..f27b932cff5cba 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/actions.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/actions.ts @@ -5,68 +5,26 @@ */ import expect from '@kbn/expect'; -import uuid from 'uuid'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { getSupertestWithoutAuth } from './services'; export default function(providerContext: FtrProviderContext) { const { getService } = providerContext; const esArchiver = getService('esArchiver'); - const esClient = getService('es'); - - const supertest = getSupertestWithoutAuth(providerContext); - let apiKey: { id: string; api_key: string }; + const supertest = getService('supertest'); describe('fleet_agents_actions', () => { before(async () => { await esArchiver.loadIfNeeded('fleet/agents'); - - const { body: apiKeyBody } = await esClient.security.createApiKey({ - body: { - name: `test access api key: ${uuid.v4()}`, - }, - }); - apiKey = apiKeyBody; - const { - body: { _source: agentDoc }, - } = await esClient.get({ - index: '.kibana', - id: 'agents:agent1', - }); - agentDoc.agents.access_api_key_id = apiKey.id; - await esClient.update({ - index: '.kibana', - id: 'agents:agent1', - refresh: 'true', - body: { - doc: agentDoc, - }, - }); }); after(async () => { await esArchiver.unload('fleet/agents'); }); - it('should return a 401 if this a not a valid actions access', async () => { - await supertest - .post(`/api/ingest_manager/fleet/agents/agent1/actions`) - .set('kbn-xsrf', 'xx') - .set('Authorization', 'ApiKey NOT_A_VALID_TOKEN') - .send({ - action_ids: [], - }) - .expect(401); - }); - it('should return a 200 if this a valid actions request', async () => { const { body: apiResponse } = await supertest .post(`/api/ingest_manager/fleet/agents/agent1/actions`) .set('kbn-xsrf', 'xx') - .set( - 'Authorization', - `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` - ) .send({ action: { type: 'CONFIG_CHANGE', @@ -83,10 +41,6 @@ export default function(providerContext: FtrProviderContext) { const { body: agentResponse } = await supertest .get(`/api/ingest_manager/fleet/agents/agent1`) .set('kbn-xsrf', 'xx') - .set( - 'Authorization', - `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` - ) .expect(200); const updatedAction = agentResponse.item.actions.find( @@ -102,10 +56,6 @@ export default function(providerContext: FtrProviderContext) { const { body: apiResponse } = await supertest .post(`/api/ingest_manager/fleet/agents/agent1/actions`) .set('kbn-xsrf', 'xx') - .set( - 'Authorization', - `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` - ) .send({ action: { data: 'action_data', @@ -117,5 +67,20 @@ export default function(providerContext: FtrProviderContext) { '[request body.action.type]: expected at least one defined value but got [undefined]' ); }); + + it('should return a 404 when agent does not exist', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent100/actions`) + .set('kbn-xsrf', 'xx') + .send({ + action: { + type: 'CONFIG_CHANGE', + data: 'action_data', + sent_at: '2020-03-18T19:45:02.620Z', + }, + }) + .expect(404); + expect(apiResponse.message).to.eql('Saved object [agents/agent100] not found'); + }); }); } From 2a3054fbe3ab454b47a4b77c27bd7a9cdcaa81b6 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Thu, 19 Mar 2020 12:56:45 -0400 Subject: [PATCH 5/8] EMT-248: review comments and some clean up --- .../ingest_manager/common/types/models/agent.ts | 9 +++++---- .../server/routes/agent/actions_handlers.test.ts | 9 ++++++--- .../ingest_manager/server/services/agents/actions.ts | 10 ++-------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index 78856873e97ca2..bca8d80e1e5d5f 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -14,15 +14,16 @@ export type AgentType = export type AgentStatus = 'offline' | 'error' | 'online' | 'inactive' | 'warning'; -export interface AgentAction extends SavedObjectAttributes { +export interface NewAgentAction { type: 'CONFIG_CHANGE' | 'DATA_DUMP' | 'RESUME' | 'PAUSE'; - id: string; - created_at: string; data?: string; sent_at?: string; } -export type NewAgentAction = Pick; +export interface AgentAction extends NewAgentAction, SavedObjectAttributes { + id: string; + created_at: string; +} export interface AgentEvent { type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index 9072cfc19659d9..756e1b06dfba00 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -15,7 +15,10 @@ import { httpServerMock } from '../../../../../../src/core/server/http/http_serv import { ActionsService } from '../../services/agents'; import { AgentAction } from '../../../common/types/models'; import { postNewAgentActionHandlerBuilder } from './actions_handlers'; -import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; +import { + PostNewAgentActionRequest, + PostNewAgentActionResponse, +} from '../../../common/types/rest_spec'; describe('test actions handlers schema', () => { it('validate that new agent actions schema is valid', async () => { @@ -48,7 +51,7 @@ describe('test actions handlers', () => { }); it('should succeed on valid new agent action', async () => { - const mockRequest = httpServerMock.createKibanaRequest({ + const mockRequest = httpServerMock.createKibanaRequest(({ body: { action: { type: 'CONFIG_CHANGE', @@ -59,7 +62,7 @@ describe('test actions handlers', () => { params: { agentId: 'id', }, - }); + } as unknown) as PostNewAgentActionRequest); const agentAction = ({ type: 'CONFIG_CHANGE', diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index d2684e5066f3d1..7de56a17d7e33c 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -31,18 +31,12 @@ export async function updateAgentActions( } export function createAgentAction(createdAt: Date, newAgentAction: NewAgentAction): AgentAction { - const agentAction: object = { + const agentAction = { id: uuid.v4(), created_at: createdAt.toISOString(), }; - Object.assign(agentAction, ...keys(newAgentAction).map(key => ({ [key]: newAgentAction[key] }))); - - return agentAction as AgentAction; -} - -function keys(obj: O): Array { - return Object.keys(obj) as Array; + return Object.assign(agentAction, newAgentAction) as AgentAction; } export interface ActionsService { From d343c93550cf18a2d563e63d47eb3ff203accff8 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Thu, 19 Mar 2020 14:03:23 -0400 Subject: [PATCH 6/8] EMT-248: remove as --- x-pack/plugins/ingest_manager/server/services/agents/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 7de56a17d7e33c..6f7d61ed3eed02 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -36,7 +36,7 @@ export function createAgentAction(createdAt: Date, newAgentAction: NewAgentActio created_at: createdAt.toISOString(), }; - return Object.assign(agentAction, newAgentAction) as AgentAction; + return Object.assign(agentAction, newAgentAction); } export interface ActionsService { From 74a472917123f031da3638098a55c4c43af51836 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Thu, 19 Mar 2020 15:05:12 -0400 Subject: [PATCH 7/8] EMT-248: use union type, do some clean up --- x-pack/plugins/ingest_manager/common/types/models/agent.ts | 4 ++-- .../server/routes/agent/actions_handlers.test.ts | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index bca8d80e1e5d5f..aa5729a101e111 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -20,10 +20,10 @@ export interface NewAgentAction { sent_at?: string; } -export interface AgentAction extends NewAgentAction, SavedObjectAttributes { +export type AgentAction = NewAgentAction & { id: string; created_at: string; -} +} & SavedObjectAttributes; export interface AgentEvent { type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index 756e1b06dfba00..ade4030a0f5e56 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -51,7 +51,7 @@ describe('test actions handlers', () => { }); it('should succeed on valid new agent action', async () => { - const mockRequest = httpServerMock.createKibanaRequest(({ + const postNewAgentActionRequest: PostNewAgentActionRequest = { body: { action: { type: 'CONFIG_CHANGE', @@ -62,7 +62,9 @@ describe('test actions handlers', () => { params: { agentId: 'id', }, - } as unknown) as PostNewAgentActionRequest); + }; + + const mockRequest = httpServerMock.createKibanaRequest(postNewAgentActionRequest); const agentAction = ({ type: 'CONFIG_CHANGE', From 58c342001b0962b47d5127c4c46292941aa38d88 Mon Sep 17 00:00:00 2001 From: nnamdifrankie Date: Thu, 19 Mar 2020 15:49:55 -0400 Subject: [PATCH 8/8] EMT-248: use core client --- .../server/routes/agent/actions_handlers.test.ts | 9 +++++++-- .../server/routes/agent/actions_handlers.ts | 2 +- .../plugins/ingest_manager/server/routes/agent/index.ts | 1 - .../server/services/agents/actions.test.ts | 4 ++++ .../ingest_manager/server/services/agents/actions.ts | 4 +--- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index ade4030a0f5e56..a20ba4a880537d 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -78,13 +78,18 @@ describe('test actions handlers', () => { getAgent: jest.fn().mockReturnValueOnce({ id: 'agent', }), - getSavedObjectsClientContract: jest.fn().mockReturnValueOnce(mockSavedObjectsClient), updateAgentActions: jest.fn().mockReturnValueOnce(agentAction), } as jest.Mocked; const postNewAgentActionHandler = postNewAgentActionHandlerBuilder(actionsService); await postNewAgentActionHandler( - ({} as unknown) as RequestHandlerContext, + ({ + core: { + savedObjects: { + client: mockSavedObjectsClient, + }, + }, + } as unknown) as RequestHandlerContext, mockRequest, mockResponse ); diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index 6ea8695e27e712..2b9c2308035932 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -22,7 +22,7 @@ export const postNewAgentActionHandlerBuilder = function( > { return async (context, request, response) => { try { - const soClient = actionsService.getSavedObjectsClientContract(request); + const soClient = context.core.savedObjects.client; const agent = await actionsService.getAgent(soClient, request.params.agentId); diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index d94029b5610010..d4610270178429 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -122,7 +122,6 @@ export const registerRoutes = (router: IRouter) => { }, postNewAgentActionHandlerBuilder({ getAgent: AgentService.getAgent, - getSavedObjectsClientContract: getInternalUserSOClient, updateAgentActions: AgentService.updateAgentActions, }) ); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts index c12108b0107e6b..b500aeb825fec7 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -16,11 +16,13 @@ interface UpdatedActions { describe('test agent actions services', () => { it('should update agent current actions with new action', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const newAgentAction: NewAgentAction = { type: 'CONFIG_CHANGE', data: 'data', sent_at: '2020-03-14T19:45:02.620Z', }; + await updateAgentActions( mockSavedObjectsClient, ({ @@ -41,6 +43,7 @@ describe('test agent actions services', () => { const updatedAgentActions = (mockSavedObjectsClient.update.mock .calls[0][2] as unknown) as UpdatedActions; + expect(updatedAgentActions.actions.length).toEqual(2); const actualAgentAction = updatedAgentActions.actions.find(action => action?.data === 'data'); expect(actualAgentAction?.type).toEqual(newAgentAction.type); @@ -56,6 +59,7 @@ describe('test agent actions services', () => { }; const now = new Date(); const agentAction = createAgentAction(now, newAgentAction); + expect(agentAction.type).toEqual(newAgentAction.type); expect(agentAction.data).toEqual(newAgentAction.data); expect(agentAction.sent_at).toEqual(newAgentAction.sent_at); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 6f7d61ed3eed02..2f8ed9f504453a 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'kibana/server'; import uuid from 'uuid'; import { Agent, @@ -42,8 +42,6 @@ export function createAgentAction(createdAt: Date, newAgentAction: NewAgentActio export interface ActionsService { getAgent: (soClient: SavedObjectsClientContract, agentId: string) => Promise; - getSavedObjectsClientContract: (kibanaRequest: KibanaRequest) => SavedObjectsClientContract; - updateAgentActions: ( soClient: SavedObjectsClientContract, agent: Agent,