Skip to content

Commit

Permalink
[Fleet] added check for Enterprise license in package policy create/u…
Browse files Browse the repository at this point in the history
…pdate APIs (#187467)

## Summary

Relates elastic/ingest-dev#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](https://elasticco.atlassian.net/wiki/spaces/PM/pages/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="https://github.com/elastic/kibana/assets/90178898/d99b3765-0b0b-4abd-ae4c-dc9f396a465a">

Test with Enterprise license:
<img width="910" alt="image"
src="https://github.com/elastic/kibana/assets/90178898/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
  • Loading branch information
juliaElastic committed Jul 3, 2024
1 parent 83a1799 commit 82d32a7
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down
16 changes: 15 additions & 1 deletion x-pack/plugins/fleet/server/routes/package_policy/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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',
};
}

0 comments on commit 82d32a7

Please sign in to comment.