diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index 6f1ac12edb163c..2ef2cf0ef9648c 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -8,7 +8,6 @@ import { uniq } from 'lodash'; import { type RequestHandler, SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; -import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; import type { GetAgentsResponse, @@ -21,7 +20,6 @@ import type { GetAgentUploadsResponse, PostAgentReassignResponse, PostRetrieveAgentsByActionsResponse, - Agent, } from '../../../common/types'; import type { GetAgentsRequestSchema, @@ -45,18 +43,7 @@ import { defaultFleetErrorHandler, FleetNotFoundError } from '../../errors'; import * as AgentService from '../../services/agents'; import { fetchAndAssignAgentMetrics } from '../../services/agents/agent_metrics'; import { getAgentStatusForAgentPolicy } from '../../services/agents'; - -export function verifyNamespace(agent: Agent, currentNamespace?: string) { - const isInNamespace = - (currentNamespace && agent.namespaces?.includes(currentNamespace)) || - (!currentNamespace && - (!agent.namespaces || - agent.namespaces.length === 0 || - agent.namespaces?.includes(DEFAULT_NAMESPACE_STRING))); - if (!isInNamespace) { - throw new FleetNotFoundError(`${agent.id} not found in namespace`); - } -} +import { isAgentInNamespace } from '../../services/agents/namespace'; export const getAgentHandler: FleetRequestHandler< TypeOf, @@ -68,7 +55,9 @@ export const getAgentHandler: FleetRequestHandler< let agent = await fleetContext.agentClient.asCurrentUser.getAgent(request.params.agentId); - verifyNamespace(agent, coreContext.savedObjects.client.getCurrentNamespace()); + if (!isAgentInNamespace(agent, coreContext.savedObjects.client.getCurrentNamespace())) { + throw new FleetNotFoundError(`${agent.id} not found in namespace`); + } if (request.query.withMetrics) { agent = (await fetchAndAssignAgentMetrics(esClientCurrentUser, [agent]))[0]; @@ -90,12 +79,17 @@ export const getAgentHandler: FleetRequestHandler< } }; -export const deleteAgentHandler: RequestHandler< +export const deleteAgentHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { + const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); + const esClient = coreContext.elasticsearch.client.asInternalUser; + try { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asInternalUser; + const agent = await fleetContext.agentClient.asCurrentUser.getAgent(request.params.agentId); + if (!isAgentInNamespace(agent, coreContext.savedObjects.client.getCurrentNamespace())) { + throw new FleetNotFoundError(`${agent.id} not found in namespace`); + } await AgentService.deleteAgent(esClient, request.params.agentId); @@ -116,12 +110,12 @@ export const deleteAgentHandler: RequestHandler< } }; -export const updateAgentHandler: RequestHandler< +export const updateAgentHandler: FleetRequestHandler< TypeOf, undefined, TypeOf > = async (context, request, response) => { - const coreContext = await context.core; + const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); const esClient = coreContext.elasticsearch.client.asInternalUser; const soClient = coreContext.savedObjects.client; @@ -134,6 +128,11 @@ export const updateAgentHandler: RequestHandler< } try { + const agent = await fleetContext.agentClient.asCurrentUser.getAgent(request.params.agentId); + if (!isAgentInNamespace(agent, coreContext.savedObjects.client.getCurrentNamespace())) { + throw new FleetNotFoundError(`${agent.id} not found in namespace`); + } + await AgentService.updateAgent(esClient, request.params.agentId, partialAgent); const body = { item: await AgentService.getAgentById(esClient, soClient, request.params.agentId), diff --git a/x-pack/plugins/fleet/server/services/agents/namespace.ts b/x-pack/plugins/fleet/server/services/agents/namespace.ts new file mode 100644 index 00000000000000..13231604ab272c --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agents/namespace.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; + +import type { Agent } from '../../types'; + +export function isAgentInNamespace(agent: Agent, namespace?: string) { + return ( + (namespace && agent.namespaces?.includes(namespace)) || + (!namespace && + (!agent.namespaces || + agent.namespaces.length === 0 || + agent.namespaces?.includes(DEFAULT_NAMESPACE_STRING))) + ); +} diff --git a/x-pack/test/fleet_api_integration/apis/space_awareness/agents.ts b/x-pack/test/fleet_api_integration/apis/space_awareness/agents.ts index 73c2675d7b2d59..511347337bb861 100644 --- a/x-pack/test/fleet_api_integration/apis/space_awareness/agents.ts +++ b/x-pack/test/fleet_api_integration/apis/space_awareness/agents.ts @@ -132,5 +132,41 @@ export default function (providerContext: FtrProviderContext) { expect(err?.message).to.match(/404 "Not Found"/); }); }); + + describe('PUT /agents/{id}', () => { + it('should allow to update an agent in the same space', async () => { + await apiClient.updateAgent(testSpaceAgent1, { tags: ['foo'] }, TEST_SPACE_1); + }); + + it('should not allow to update an agent from a different space from the default space', async () => { + let err: Error | undefined; + try { + await apiClient.updateAgent(testSpaceAgent1, { tags: ['foo'] }); + } catch (_err) { + err = _err; + } + + expect(err).to.be.an(Error); + expect(err?.message).to.match(/404 "Not Found"/); + }); + }); + + describe('DELETE /agents/{id}', () => { + it('should allow to delete an agent in the same space', async () => { + await apiClient.deleteAgent(testSpaceAgent1, TEST_SPACE_1); + }); + + it('should not allow to delete an agent from a different space from the default space', async () => { + let err: Error | undefined; + try { + await apiClient.deleteAgent(testSpaceAgent1); + } catch (_err) { + err = _err; + } + + expect(err).to.be.an(Error); + expect(err?.message).to.match(/404 "Not Found"/); + }); + }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/space_awareness/api_helper.ts b/x-pack/test/fleet_api_integration/apis/space_awareness/api_helper.ts index 633d34cfa2d154..5e63cd0154727b 100644 --- a/x-pack/test/fleet_api_integration/apis/space_awareness/api_helper.ts +++ b/x-pack/test/fleet_api_integration/apis/space_awareness/api_helper.ts @@ -173,6 +173,23 @@ export class SpaceTestApiClient { return res; } + async updateAgent(agentId: string, data: any, spaceId?: string) { + const { body: res } = await this.supertest + .put(`${this.getBaseUrl(spaceId)}/api/fleet/agents/${agentId}`) + .set('kbn-xsrf', 'xxxx') + .send(data) + .expect(200); + + return res; + } + async deleteAgent(agentId: string, spaceId?: string) { + const { body: res } = await this.supertest + .delete(`${this.getBaseUrl(spaceId)}/api/fleet/agents/${agentId}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + return res; + } // Enrollment Settings async getEnrollmentSettings(spaceId?: string): Promise { const { body: res } = await this.supertest