[Fleet] added check for Enterprise license in package policy create/update APIs (#187467)

## Summary

Relates https://github.com/elastic/ingest-dev/issues/3464

Added check to reject integration policy shared by multiple agent
policies if Enterprise license is not available.

### Testing

- Enable a local enterprise licence
([steps](46802910/Internal+License+-+X-Pack+and+Endgame))
- Enable flag `enableReusableIntegrationPolicies`
- Try create/update package policy API with multiple `policy_ids`,
expect to work

Repeat the steps with any lower licence, the API should reject the
request with a 400 error.

```
POST kbn:/api/fleet/package_policies
{
  "policy_ids": [
    "policy-1", "policy-2"
  ],
  "package": {
    "name": "apache",
    "version": "1.20.0"
  },
  "name": "apache-4",
  "description": "",
  "namespace": "",
  "inputs": []
}
```

Test with Basic license:
<img width="910" alt="image"
src="d99b3765-0b0b-4abd-ae4c-dc9f396a465a">

Test with Enterprise license:
<img width="910" alt="image"
src="d42b761e-32f0-46b6-95a8-0a565f898532">


### 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:
Julia Bardi 2024-07-03 15:28:09 +02:00 committed by GitHub
parent 83a17990fe
commit 82d32a757f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 70 additions and 3 deletions

View file

@ -12,7 +12,7 @@ import type { RouteConfig } from '@kbn/core/server';
import type { FleetAuthzRouter } from '../../services/security';
import { PACKAGE_POLICY_API_ROUTES } from '../../../common/constants';
import { appContextService, packagePolicyService } from '../../services';
import { appContextService, licenseService, packagePolicyService } from '../../services';
import { createAppContextStartContractMock, xpackMocks } from '../../mocks';
import type { PackagePolicyClient, FleetRequestHandlerContext } from '../..';
import type {
@ -190,6 +190,29 @@ describe('When calling package policy', () => {
},
});
});
it('should throw if no enterprise license and multiple policy_ids is provided', async () => {
const request = getCreateKibanaRequest({ ...newPolicy, policy_ids: ['1', '2'] } as any);
await createPackagePolicyHandler(context, request as any, response);
expect(response.customError).toHaveBeenCalledWith({
statusCode: 400,
body: {
message: 'Reusable integration policies are only available with an Enterprise license',
},
});
});
it('should not throw if enterprise license and multiple policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
const request = getCreateKibanaRequest({ ...newPolicy, policy_ids: ['1', '2'] } as any);
await createPackagePolicyHandler(context, request as any, response);
expect(response.customError).not.toHaveBeenCalledWith({
statusCode: 400,
body: {
message: 'Reusable integration policies are only available with an Enterprise license',
},
});
});
});
describe('update api handler', () => {
@ -338,6 +361,25 @@ describe('When calling package policy', () => {
body: { item: { ...existingPolicy, namespace: 'namespace' } },
});
});
it('should throw if no enterprise license and multiple policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false);
const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any);
await routeHandler(context, request, response);
expect(response.customError).toHaveBeenCalledWith({
statusCode: 400,
body: {
message: 'Reusable integration policies are only available with an Enterprise license',
},
});
});
it('should not throw if enterprise license and multiple policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any);
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
});
});
describe('list api handler', () => {

View file

@ -62,7 +62,11 @@ import {
import type { SimplifiedPackagePolicy } from '../../../common/services/simplified_package_policy_helper';
import { isSimplifiedCreatePackagePolicyRequest, removeFieldsFromInputSchema } from './utils';
import {
canUseMultipleAgentPolicies,
isSimplifiedCreatePackagePolicyRequest,
removeFieldsFromInputSchema,
} from './utils';
export const isNotNull = <T>(value: T | null): value is T => value !== null;
@ -246,6 +250,11 @@ export const createPackagePolicyHandler: FleetRequestHandler<
throw new PackagePolicyRequestError('Either policy_id or policy_ids must be provided');
}
const { canUseReusablePolicies, errorMessage } = canUseMultipleAgentPolicies();
if ((newPolicy.policy_ids ?? []).length > 1 && !canUseReusablePolicies) {
throw new PackagePolicyRequestError(errorMessage);
}
let newPackagePolicy: NewPackagePolicy;
if (isSimplifiedCreatePackagePolicyRequest(newPolicy)) {
if (!pkg) {
@ -407,6 +416,11 @@ export const updatePackagePolicyHandler: FleetRequestHandler<
newData.overrides = overrides;
}
}
const { canUseReusablePolicies, errorMessage } = canUseMultipleAgentPolicies();
if ((newData.policy_ids ?? []).length > 1 && !canUseReusablePolicies) {
throw new PackagePolicyRequestError(errorMessage);
}
const updatedPackagePolicy = await packagePolicyService.update(
soClient,
esClient,

View file

@ -8,7 +8,7 @@
import type { TypeOf } from '@kbn/config-schema';
import type { CreatePackagePolicyRequestSchema, PackagePolicyInput } from '../../../types';
import { licenseService } from '../../../services';
import type { SimplifiedPackagePolicy } from '../../../../common/services/simplified_package_policy_helper';
export function isSimplifiedCreatePackagePolicyRequest(
@ -39,3 +39,14 @@ export function removeFieldsFromInputSchema(
return newInput;
});
}
const LICENCE_FOR_MULTIPLE_AGENT_POLICIES = 'enterprise';
export function canUseMultipleAgentPolicies() {
const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES);
return {
canUseReusablePolicies: hasEnterpriseLicence,
errorMessage: 'Reusable integration policies are only available with an Enterprise license',
};
}