[Fleet] Restrict integration changes for managed policies (#90675)
## Summary - [x] Integrations cannot be added ~~, unless with a force flag~~ - [x] API - [x] UI - [x] tests - [x] Integrations cannot be removed ~~, unless with a force flag~~ - [x] API - [x] UI - [x] tests closes https://github.com/elastic/kibana/issues/90445 refs https://github.com/elastic/kibana/issues/89617 ### Cannot add integrations to managed policy <img height="400" alt="Screen Shot 2021-02-08 at 1 56 32 PM" src="https://user-images.githubusercontent.com/57655/107277261-25c48300-6a22-11eb-936a-0a7361667093.png"> ### Cannot delete integrations from managed policy <img alt="Screen Shot 2021-02-08 at 3 05 16 PM" src="https://user-images.githubusercontent.com/57655/107277318-337a0880-6a22-11eb-836f-fc66b510d257.png"> ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
4cd0548f48
commit
c92af5a4d5
|
@ -79,11 +79,10 @@ export const createPackagePolicyHandler: RequestHandler<
|
|||
const esClient = context.core.elasticsearch.client.asCurrentUser;
|
||||
const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser;
|
||||
const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined;
|
||||
let newData = { ...request.body };
|
||||
try {
|
||||
newData = await packagePolicyService.runExternalCallbacks(
|
||||
const newData = await packagePolicyService.runExternalCallbacks(
|
||||
'packagePolicyCreate',
|
||||
newData,
|
||||
{ ...request.body },
|
||||
context,
|
||||
request
|
||||
);
|
||||
|
|
|
@ -36,7 +36,11 @@ import {
|
|||
FleetServerPolicy,
|
||||
AGENT_POLICY_INDEX,
|
||||
} from '../../common';
|
||||
import { AgentPolicyNameExistsError, AgentPolicyDeletionError } from '../errors';
|
||||
import {
|
||||
AgentPolicyNameExistsError,
|
||||
AgentPolicyDeletionError,
|
||||
IngestManagerError,
|
||||
} from '../errors';
|
||||
import { createAgentPolicyAction, listAgents } from './agents';
|
||||
import { packagePolicyService } from './package_policy';
|
||||
import { outputService } from './output';
|
||||
|
@ -382,6 +386,10 @@ class AgentPolicyService {
|
|||
throw new Error('Agent policy not found');
|
||||
}
|
||||
|
||||
if (oldAgentPolicy.is_managed) {
|
||||
throw new IngestManagerError(`Cannot update integrations of managed policy ${id}`);
|
||||
}
|
||||
|
||||
return await this._update(
|
||||
soClient,
|
||||
esClient,
|
||||
|
@ -409,6 +417,10 @@ class AgentPolicyService {
|
|||
throw new Error('Agent policy not found');
|
||||
}
|
||||
|
||||
if (oldAgentPolicy.is_managed) {
|
||||
throw new IngestManagerError(`Cannot remove integrations of managed policy ${id}`);
|
||||
}
|
||||
|
||||
return await this._update(
|
||||
soClient,
|
||||
esClient,
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
doesAgentPolicyAlreadyIncludePackage,
|
||||
} from '../../common';
|
||||
import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants';
|
||||
import { IngestManagerError, ingestErrorToResponseOptions } from '../errors';
|
||||
import {
|
||||
NewPackagePolicy,
|
||||
UpdatePackagePolicy,
|
||||
|
@ -63,15 +64,20 @@ class PackagePolicyService {
|
|||
const parentAgentPolicy = await agentPolicyService.get(soClient, packagePolicy.policy_id);
|
||||
if (!parentAgentPolicy) {
|
||||
throw new Error('Agent policy not found');
|
||||
} else {
|
||||
if (
|
||||
(parentAgentPolicy.package_policies as PackagePolicy[]).find(
|
||||
(siblingPackagePolicy) => siblingPackagePolicy.name === packagePolicy.name
|
||||
)
|
||||
) {
|
||||
throw new Error('There is already a package with the same name on this agent policy');
|
||||
}
|
||||
}
|
||||
if (parentAgentPolicy.is_managed) {
|
||||
throw new IngestManagerError(
|
||||
`Cannot add integrations to managed policy ${parentAgentPolicy.id}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
(parentAgentPolicy.package_policies as PackagePolicy[]).find(
|
||||
(siblingPackagePolicy) => siblingPackagePolicy.name === packagePolicy.name
|
||||
)
|
||||
) {
|
||||
throw new Error('There is already a package with the same name on this agent policy');
|
||||
}
|
||||
|
||||
// Add ids to stream
|
||||
const packagePolicyId = options?.id || uuid.v4();
|
||||
let inputs: PackagePolicyInput[] = packagePolicy.inputs.map((input) =>
|
||||
|
@ -285,6 +291,9 @@ class PackagePolicyService {
|
|||
if (!parentAgentPolicy) {
|
||||
throw new Error('Agent policy not found');
|
||||
} else {
|
||||
if (parentAgentPolicy.is_managed) {
|
||||
throw new IngestManagerError(`Cannot update integrations of managed policy ${id}`);
|
||||
}
|
||||
if (
|
||||
(parentAgentPolicy.package_policies as PackagePolicy[]).find(
|
||||
(siblingPackagePolicy) =>
|
||||
|
@ -295,7 +304,7 @@ class PackagePolicyService {
|
|||
}
|
||||
}
|
||||
|
||||
let inputs = await restOfPackagePolicy.inputs.map((input) =>
|
||||
let inputs = restOfPackagePolicy.inputs.map((input) =>
|
||||
assignStreamIdToInput(oldPackagePolicy.id, input)
|
||||
);
|
||||
|
||||
|
@ -363,10 +372,11 @@ class PackagePolicyService {
|
|||
name: packagePolicy.name,
|
||||
success: true,
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
result.push({
|
||||
id,
|
||||
success: false,
|
||||
...ingestErrorToResponseOptions(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ export default function ({ loadTestFile }) {
|
|||
loadTestFile(require.resolve('./package_policy/create'));
|
||||
loadTestFile(require.resolve('./package_policy/update'));
|
||||
loadTestFile(require.resolve('./package_policy/get'));
|
||||
loadTestFile(require.resolve('./package_policy/delete'));
|
||||
|
||||
// Agent policies
|
||||
loadTestFile(require.resolve('./agent_policy/index'));
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { warnAndSkipTest } from '../../helpers';
|
||||
|
||||
|
@ -39,6 +39,52 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.send({ agentPolicyId });
|
||||
});
|
||||
|
||||
it('should fail for managed agent policies', async function () {
|
||||
if (server.enabled) {
|
||||
// get a managed policy
|
||||
const {
|
||||
body: { item: managedPolicy },
|
||||
} = await supertest
|
||||
.post(`/api/fleet/agent_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: `Managed policy from ${Date.now()}`,
|
||||
namespace: 'default',
|
||||
is_managed: true,
|
||||
});
|
||||
|
||||
// try to add an integration to the managed policy
|
||||
const { body } = await supertest
|
||||
.post(`/api/fleet/package_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'filetest-1',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_id: managedPolicy.id,
|
||||
enabled: true,
|
||||
output_id: '',
|
||||
inputs: [],
|
||||
package: {
|
||||
name: 'filetest',
|
||||
title: 'For File Tests',
|
||||
version: '0.1.0',
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(body.statusCode).to.be(400);
|
||||
expect(body.message).to.contain('Cannot add integrations to managed policy');
|
||||
|
||||
// delete policy we just made
|
||||
await supertest.post(`/api/fleet/agent_policies/delete`).set('kbn-xsrf', 'xxxx').send({
|
||||
agentPolicyId: managedPolicy.id,
|
||||
});
|
||||
} else {
|
||||
warnAndSkipTest(this, log);
|
||||
}
|
||||
});
|
||||
|
||||
it('should work with valid values', async function () {
|
||||
if (server.enabled) {
|
||||
await supertest
|
||||
|
|
127
x-pack/test/fleet_api_integration/apis/package_policy/delete.ts
Normal file
127
x-pack/test/fleet_api_integration/apis/package_policy/delete.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
|
||||
export default function (providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
const supertest = getService('supertest');
|
||||
|
||||
// use function () {} and not () => {} here
|
||||
// because `this` has to point to the Mocha context
|
||||
// see https://mochajs.org/#arrow-functions
|
||||
|
||||
describe('Package Policy - delete', async function () {
|
||||
skipIfNoDockerRegistry(providerContext);
|
||||
let agentPolicy: any;
|
||||
let packagePolicy: any;
|
||||
|
||||
before(async function () {
|
||||
let agentPolicyResponse = await supertest
|
||||
.post(`/api/fleet/agent_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'Test policy',
|
||||
namespace: 'default',
|
||||
is_managed: false,
|
||||
});
|
||||
|
||||
// if one already exists, re-use that
|
||||
if (agentPolicyResponse.body.statusCode === 409) {
|
||||
const errorRegex = /^agent policy \'(?<id>[\w,\-]+)\' already exists/i;
|
||||
const result = errorRegex.exec(agentPolicyResponse.body.message);
|
||||
if (result?.groups?.id) {
|
||||
agentPolicyResponse = await supertest
|
||||
.put(`/api/fleet/agent_policies/${result.groups.id}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'Test policy',
|
||||
namespace: 'default',
|
||||
is_managed: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
agentPolicy = agentPolicyResponse.body.item;
|
||||
|
||||
const { body: packagePolicyResponse } = await supertest
|
||||
.post(`/api/fleet/package_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'filetest-1',
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_id: agentPolicy.id,
|
||||
enabled: true,
|
||||
output_id: '',
|
||||
inputs: [],
|
||||
package: {
|
||||
name: 'filetest',
|
||||
title: 'For File Tests',
|
||||
version: '0.1.0',
|
||||
},
|
||||
});
|
||||
packagePolicy = packagePolicyResponse.item;
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await supertest
|
||||
.post(`/api/fleet/agent_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ agentPolicyId: agentPolicy.id });
|
||||
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ packagePolicyIds: [packagePolicy.id] });
|
||||
});
|
||||
|
||||
it('should fail on managed agent policies', async function () {
|
||||
// update existing policy to managed
|
||||
await supertest
|
||||
.put(`/api/fleet/agent_policies/${agentPolicy.id}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: agentPolicy.name,
|
||||
namespace: agentPolicy.namespace,
|
||||
is_managed: true,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
// try to delete
|
||||
const { body: results } = await supertest
|
||||
.post(`/api/fleet/package_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ packagePolicyIds: [packagePolicy.id] })
|
||||
.expect(200);
|
||||
|
||||
// delete always succeeds (returns 200) with Array<{success: boolean}>
|
||||
expect(Array.isArray(results));
|
||||
expect(results.length).to.be(1);
|
||||
expect(results[0].success).to.be(false);
|
||||
expect(results[0].body.message).to.contain('Cannot remove integrations of managed policy');
|
||||
|
||||
// revert existing policy to unmanaged
|
||||
await supertest
|
||||
.put(`/api/fleet/agent_policies/${agentPolicy.id}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: agentPolicy.name,
|
||||
namespace: agentPolicy.namespace,
|
||||
is_managed: false,
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should work for unmanaged policies', async function () {
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ packagePolicyIds: [packagePolicy.id] });
|
||||
});
|
||||
});
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
|
||||
|
@ -21,6 +21,7 @@ export default function (providerContext: FtrProviderContext) {
|
|||
describe('Package Policy - update', async function () {
|
||||
skipIfNoDockerRegistry(providerContext);
|
||||
let agentPolicyId: string;
|
||||
let managedAgentPolicyId: string;
|
||||
let packagePolicyId: string;
|
||||
let packagePolicyId2: string;
|
||||
|
||||
|
@ -35,8 +36,30 @@ export default function (providerContext: FtrProviderContext) {
|
|||
name: 'Test policy',
|
||||
namespace: 'default',
|
||||
});
|
||||
|
||||
agentPolicyId = agentPolicyResponse.item.id;
|
||||
|
||||
const { body: managedAgentPolicyResponse } = await supertest
|
||||
.post(`/api/fleet/agent_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'Test managed policy',
|
||||
namespace: 'default',
|
||||
is_managed: true,
|
||||
});
|
||||
|
||||
// if one already exists, re-use that
|
||||
const managedExists = managedAgentPolicyResponse.statusCode === 409;
|
||||
if (managedExists) {
|
||||
const errorRegex = /^agent policy \'(?<id>[\w,\-]+)\' already exists/i;
|
||||
const result = errorRegex.exec(managedAgentPolicyResponse.message);
|
||||
if (result?.groups?.id) {
|
||||
managedAgentPolicyId = result.groups.id;
|
||||
}
|
||||
} else {
|
||||
managedAgentPolicyId = managedAgentPolicyResponse.item.id;
|
||||
}
|
||||
|
||||
const { body: packagePolicyResponse } = await supertest
|
||||
.post(`/api/fleet/package_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
|
@ -83,6 +106,29 @@ export default function (providerContext: FtrProviderContext) {
|
|||
.send({ agentPolicyId });
|
||||
});
|
||||
|
||||
it('should fail on managed agent policies', async function () {
|
||||
const { body } = await supertest
|
||||
.put(`/api/fleet/package_policies/${packagePolicyId}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: 'filetest-1',
|
||||
description: '',
|
||||
namespace: 'updated_namespace',
|
||||
policy_id: managedAgentPolicyId,
|
||||
enabled: true,
|
||||
output_id: '',
|
||||
inputs: [],
|
||||
package: {
|
||||
name: 'filetest',
|
||||
title: 'For File Tests',
|
||||
version: '0.1.0',
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(body.message).to.contain('Cannot update integrations of managed policy');
|
||||
});
|
||||
|
||||
it('should work with valid values', async function () {
|
||||
await supertest
|
||||
.put(`/api/fleet/package_policies/${packagePolicyId}`)
|
||||
|
|
Loading…
Reference in a new issue