diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index 700750761def4..6e25a4719cd0f 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -31,3 +31,5 @@ export class PackageCacheError extends IngestManagerError {} export class PackageOperationNotSupportedError extends IngestManagerError {} export class FleetAdminUserInvalidError extends IngestManagerError {} export class ConcurrentInstallOperationError extends IngestManagerError {} +export class AgentReassignmentError extends IngestManagerError {} +export class AgentUnenrollmentError extends IngestManagerError {} diff --git a/x-pack/plugins/fleet/server/services/agents/reassign.test.ts b/x-pack/plugins/fleet/server/services/agents/reassign.test.ts index 49a38166e9cc7..5a940818c280d 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign.test.ts @@ -7,6 +7,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import type { SavedObject } from 'kibana/server'; import type { Agent, AgentPolicy } from '../../types'; +import { AgentReassignmentError } from '../../errors'; import { reassignAgent, reassignAgents } from './reassign'; const agentInManagedSO = { @@ -57,7 +58,7 @@ describe('reassignAgent (singular)', () => { agentInUnmanagedSO.id, agentInManagedSO.attributes.policy_id! ) - ).rejects.toThrow('to managed'); + ).rejects.toThrowError(AgentReassignmentError); // does not call ES update expect(soClient.update).toBeCalledTimes(0); @@ -68,13 +69,13 @@ describe('reassignAgent (singular)', () => { const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await expect( reassignAgent(soClient, esClient, agentInManagedSO.id, agentInManagedSO2.id) - ).rejects.toThrow('from managed'); + ).rejects.toThrowError(AgentReassignmentError); // does not call ES update expect(soClient.update).toBeCalledTimes(0); await expect( reassignAgent(soClient, esClient, agentInManagedSO.id, agentInUnmanagedSO.id) - ).rejects.toThrow('from managed'); + ).rejects.toThrowError(AgentReassignmentError); // does not call ES update expect(soClient.update).toBeCalledTimes(0); }); diff --git a/x-pack/plugins/fleet/server/services/agents/reassign.ts b/x-pack/plugins/fleet/server/services/agents/reassign.ts index c29ace6e6245b..97ab737734734 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, ElasticsearchClient } from 'kibana/server'; +import type { SavedObjectsClientContract, ElasticsearchClient } from 'kibana/server'; import Boom from '@hapi/boom'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; -import { AgentSOAttributes } from '../../types'; +import type { AgentSOAttributes } from '../../types'; +import { AgentReassignmentError } from '../../errors'; import { agentPolicyService } from '../agent_policy'; import { getAgentPolicyForAgent, getAgents, listAllAgents } from './crud'; import { createAgentAction, bulkCreateAgentActions } from './actions'; @@ -45,12 +46,16 @@ export async function reassignAgentIsAllowed( ) { const agentPolicy = await getAgentPolicyForAgent(soClient, esClient, agentId); if (agentPolicy?.is_managed) { - throw new Error(`Cannot reassign an agent from managed agent policy ${agentPolicy.id}`); + throw new AgentReassignmentError( + `Cannot reassign an agent from managed agent policy ${agentPolicy.id}` + ); } const newAgentPolicy = await agentPolicyService.get(soClient, newAgentPolicyId); if (newAgentPolicy?.is_managed) { - throw new Error(`Cannot reassign an agent to managed agent policy ${newAgentPolicy.id}`); + throw new AgentReassignmentError( + `Cannot reassign an agent to managed agent policy ${newAgentPolicy.id}` + ); } return true; diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts index 57ac6d4334885..a65d64d0e8ae1 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.test.ts @@ -7,6 +7,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import type { SavedObject } from 'kibana/server'; import type { Agent, AgentPolicy } from '../../types'; +import { AgentUnenrollmentError } from '../../errors'; import { unenrollAgent, unenrollAgents } from './unenroll'; const agentInManagedSO = { @@ -46,7 +47,9 @@ describe('unenrollAgent (singular)', () => { it('cannot unenroll from managed policy', async () => { const soClient = createClientMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - await expect(unenrollAgent(soClient, esClient, agentInManagedSO.id)).rejects.toThrow('managed'); + await expect(unenrollAgent(soClient, esClient, agentInManagedSO.id)).rejects.toThrowError( + AgentUnenrollmentError + ); // does not call ES update expect(soClient.update).toBeCalledTimes(0); }); diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.ts index eab99d9cc145d..aec628e78e405 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.ts @@ -3,8 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; -import { AgentSOAttributes } from '../../types'; +import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; +import type { AgentSOAttributes } from '../../types'; +import { AgentUnenrollmentError } from '../../errors'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import * as APIKeyService from '../api_keys'; import { createAgentAction, bulkCreateAgentActions } from './actions'; @@ -17,7 +18,9 @@ async function unenrollAgentIsAllowed( ) { const agentPolicy = await getAgentPolicyForAgent(soClient, esClient, agentId); if (agentPolicy?.is_managed) { - throw new Error(`Cannot unenroll ${agentId} from a managed agent policy ${agentPolicy.id}`); + throw new AgentUnenrollmentError( + `Cannot unenroll ${agentId} from a managed agent policy ${agentPolicy.id}` + ); } return true; diff --git a/x-pack/test/fleet_api_integration/apis/agents/reassign.ts b/x-pack/test/fleet_api_integration/apis/agents/reassign.ts index ae0da2eb759f6..d0e6ca2f8e8b6 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/reassign.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/reassign.ts @@ -14,10 +14,10 @@ export default function (providerContext: FtrProviderContext) { describe('fleet_reassign_agent', () => { setupFleetAndAgents(providerContext); - before(async () => { + beforeEach(async () => { await esArchiver.loadIfNeeded('fleet/agents'); }); - after(async () => { + afterEach(async () => { await esArchiver.unload('fleet/agents'); }); @@ -86,5 +86,34 @@ export default function (providerContext: FtrProviderContext) { }) .expect(404); }); + + it('can reassign from unmanaged policy to unmanaged', async () => { + // policy2 is not managed + // reassign succeeds + await supertest + .put(`/api/fleet/agents/agent1/reassign`) + .set('kbn-xsrf', 'xxx') + .send({ + policy_id: 'policy2', + }) + .expect(200); + }); + it('cannot reassign from unmanaged policy to managed', async () => { + // agent1 is enrolled in policy1. set policy1 to managed + await supertest + .put(`/api/fleet/agent_policies/policy1`) + .set('kbn-xsrf', 'xxx') + .send({ name: 'Test policy', namespace: 'default', is_managed: true }) + .expect(200); + + // reassign fails + await supertest + .put(`/api/fleet/agents/agent1/reassign`) + .set('kbn-xsrf', 'xxx') + .send({ + policy_id: 'policy2', + }) + .expect(400); + }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts b/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts index 6192ecddea9ef..87dc636b180e9 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/unenroll.ts @@ -72,7 +72,7 @@ export default function (providerContext: FtrProviderContext) { .send({ name: 'Test policy', namespace: 'default', is_managed: true }) .expect(200); - await supertest.post(`/api/fleet/agents/agent1/unenroll`).set('kbn-xsrf', 'xxx').expect(500); + await supertest.post(`/api/fleet/agents/agent1/unenroll`).set('kbn-xsrf', 'xxx').expect(400); }); it('/agents/{agent_id}/unenroll should allow from unmanaged policy', async () => {