[Security Solution][Endpoint][Admin] Api guard for paid policy features (#84667)

This commit is contained in:
Candace Park 2020-12-10 18:53:55 -05:00 committed by GitHub
parent c0f9de1f5a
commit 4e4e55029c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 391 additions and 174 deletions

View file

@ -34,6 +34,7 @@ export const createPackagePolicyServiceMock = () => {
getByIDs: jest.fn(),
list: jest.fn(),
update: jest.fn(),
runExternalCallbacks: jest.fn(),
} as jest.Mocked<PackagePolicyServiceInterface>;
};

View file

@ -52,7 +52,12 @@ import {
registerSettingsRoutes,
registerAppRoutes,
} from './routes';
import { EsAssetReference, FleetConfigType, NewPackagePolicy } from '../common';
import {
EsAssetReference,
FleetConfigType,
NewPackagePolicy,
UpdatePackagePolicy,
} from '../common';
import {
appContextService,
licenseService,
@ -119,14 +124,23 @@ const allSavedObjectTypes = [
/**
* Callbacks supported by the Fleet plugin
*/
export type ExternalCallback = [
'packagePolicyCreate',
(
newPackagePolicy: NewPackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
) => Promise<NewPackagePolicy>
];
export type ExternalCallback =
| [
'packagePolicyCreate',
(
newPackagePolicy: NewPackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
) => Promise<NewPackagePolicy>
]
| [
'packagePolicyUpdate',
(
newPackagePolicy: UpdatePackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
) => Promise<UpdatePackagePolicy>
];
export type ExternalCallbacksStorage = Map<ExternalCallback[0], Set<ExternalCallback[1]>>;
@ -302,8 +316,8 @@ export class FleetPlugin
getFullAgentPolicy: agentPolicyService.getFullAgentPolicy,
},
packagePolicyService,
registerExternalCallback: (...args: ExternalCallback) => {
return appContextService.addExternalCallback(...args);
registerExternalCallback: (type: ExternalCallback[0], callback: ExternalCallback[1]) => {
return appContextService.addExternalCallback(type, callback);
},
};
}

View file

@ -5,7 +5,7 @@
*/
import { httpServerMock, httpServiceMock } from 'src/core/server/mocks';
import { IRouter, KibanaRequest, Logger, RequestHandler, RouteConfig } from 'kibana/server';
import { IRouter, KibanaRequest, RequestHandler, RouteConfig } from 'kibana/server';
import { registerRoutes } from './index';
import { PACKAGE_POLICY_API_ROUTES } from '../../../common/constants';
import { xpackMocks } from '../../../../../mocks';
@ -48,6 +48,9 @@ jest.mock('../../services/package_policy', (): {
getByIDs: jest.fn(),
list: jest.fn(),
update: jest.fn(),
runExternalCallbacks: jest.fn((callbackType, newPackagePolicy, context, request) =>
Promise.resolve(newPackagePolicy)
),
},
};
});
@ -164,50 +167,26 @@ describe('When calling package policy', () => {
afterEach(() => (callbackCallingOrder.length = 0));
it('should call external callbacks in expected order', async () => {
it('should create with data from callback', async () => {
const request = getCreateKibanaRequest();
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
expect(callbackCallingOrder).toEqual(['one', 'two']);
});
it('should feed package policy returned by last callback', async () => {
const request = getCreateKibanaRequest();
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
expect(callbackOne).toHaveBeenCalledWith(
{
policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c',
description: '',
enabled: true,
inputs: [],
name: 'endpoint-1',
namespace: 'default',
output_id: '',
package: {
name: 'endpoint',
title: 'Elastic Endpoint',
version: '0.5.0',
},
},
context,
request
);
expect(callbackTwo).toHaveBeenCalledWith(
{
packagePolicyServiceMock.runExternalCallbacks.mockImplementationOnce(() =>
Promise.resolve({
policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c',
description: '',
enabled: true,
inputs: [
{
type: 'endpoint',
enabled: true,
streams: [],
config: {
one: {
value: 'inserted by callbackOne',
},
two: {
value: 'inserted by callbackTwo',
},
},
enabled: true,
streams: [],
type: 'endpoint',
},
],
name: 'endpoint-1',
@ -218,14 +197,8 @@ describe('When calling package policy', () => {
title: 'Elastic Endpoint',
version: '0.5.0',
},
},
context,
request
})
);
});
it('should create with data from callback', async () => {
const request = getCreateKibanaRequest();
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
expect(packagePolicyServiceMock.create.mock.calls[0][2]).toEqual({
@ -257,91 +230,6 @@ describe('When calling package policy', () => {
},
});
});
describe('and a callback throws an exception', () => {
const callbackThree: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('three');
throw new Error('callbackThree threw error on purpose');
});
const callbackFour: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('four');
return {
...ds,
inputs: [
{
...ds.inputs[0],
config: {
...ds.inputs[0].config,
four: {
value: 'inserted by callbackFour',
},
},
},
],
};
});
beforeEach(() => {
appContextService.addExternalCallback('packagePolicyCreate', callbackThree);
appContextService.addExternalCallback('packagePolicyCreate', callbackFour);
});
it('should skip over callback exceptions and still execute other callbacks', async () => {
const request = getCreateKibanaRequest();
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
expect(callbackCallingOrder).toEqual(['one', 'two', 'three', 'four']);
});
it('should log errors', async () => {
const errorLogger = (appContextService.getLogger() as jest.Mocked<Logger>).error;
const request = getCreateKibanaRequest();
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
expect(errorLogger.mock.calls).toEqual([
['An external registered [packagePolicyCreate] callback failed when executed'],
[new Error('callbackThree threw error on purpose')],
]);
});
it('should create package policy with last successful returned package policy', async () => {
const request = getCreateKibanaRequest();
await routeHandler(context, request, response);
expect(response.ok).toHaveBeenCalled();
expect(packagePolicyServiceMock.create.mock.calls[0][2]).toEqual({
policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c',
description: '',
enabled: true,
inputs: [
{
config: {
one: {
value: 'inserted by callbackOne',
},
two: {
value: 'inserted by callbackTwo',
},
four: {
value: 'inserted by callbackFour',
},
},
enabled: true,
streams: [],
type: 'endpoint',
},
],
name: 'endpoint-1',
namespace: 'default',
output_id: '',
package: {
name: 'endpoint',
title: 'Elastic Endpoint',
version: '0.5.0',
},
});
});
});
});
});
});

View file

@ -13,7 +13,6 @@ import {
CreatePackagePolicyRequestSchema,
UpdatePackagePolicyRequestSchema,
DeletePackagePoliciesRequestSchema,
NewPackagePolicy,
} from '../../types';
import { CreatePackagePolicyResponse, DeletePackagePoliciesResponse } from '../../../common';
import { defaultIngestErrorHandler } from '../../errors';
@ -77,31 +76,14 @@ export const createPackagePolicyHandler: RequestHandler<
const soClient = context.core.savedObjects.client;
const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser;
const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined;
const logger = appContextService.getLogger();
let newData = { ...request.body };
try {
// If we have external callbacks, then process those now before creating the actual package policy
const externalCallbacks = appContextService.getExternalCallbacks('packagePolicyCreate');
if (externalCallbacks && externalCallbacks.size > 0) {
let updatedNewData: NewPackagePolicy = newData;
for (const callback of externalCallbacks) {
try {
// ensure that the returned value by the callback passes schema validation
updatedNewData = CreatePackagePolicyRequestSchema.body.validate(
await callback(updatedNewData, context, request)
);
} catch (error) {
// Log the error, but keep going and process the other callbacks
logger.error(
'An external registered [packagePolicyCreate] callback failed when executed'
);
logger.error(error);
}
}
newData = updatedNewData;
}
newData = await packagePolicyService.runExternalCallbacks(
'packagePolicyCreate',
newData,
context,
request
);
// Create package policy
const packagePolicy = await packagePolicyService.create(soClient, callCluster, newData, {
@ -112,6 +94,12 @@ export const createPackagePolicyHandler: RequestHandler<
body,
});
} catch (error) {
if (error.statusCode) {
return response.customError({
statusCode: error.statusCode,
body: { message: error.message },
});
}
return defaultIngestErrorHandler({ error, response });
}
};
@ -123,16 +111,23 @@ export const updatePackagePolicyHandler: RequestHandler<
> = async (context, request, response) => {
const soClient = context.core.savedObjects.client;
const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined;
const packagePolicy = await packagePolicyService.get(soClient, request.params.packagePolicyId);
if (!packagePolicy) {
throw Boom.notFound('Package policy not found');
}
let newData = { ...request.body };
const pkg = newData.package || packagePolicy.package;
const inputs = newData.inputs || packagePolicy.inputs;
try {
const packagePolicy = await packagePolicyService.get(soClient, request.params.packagePolicyId);
if (!packagePolicy) {
throw Boom.notFound('Package policy not found');
}
const newData = { ...request.body };
const pkg = newData.package || packagePolicy.package;
const inputs = newData.inputs || packagePolicy.inputs;
newData = await packagePolicyService.runExternalCallbacks(
'packagePolicyUpdate',
newData,
context,
request
);
const updatedPackagePolicy = await packagePolicyService.update(
soClient,

View file

@ -9,6 +9,12 @@ import { createPackagePolicyMock } from '../../common/mocks';
import { packagePolicyService } from './package_policy';
import { PackageInfo, PackagePolicySOAttributes } from '../types';
import { SavedObjectsUpdateResponse } from 'src/core/server';
import { httpServerMock } from 'src/core/server/mocks';
import { KibanaRequest } from 'kibana/server';
import { xpackMocks } from '../../../../mocks';
import { ExternalCallback } from '..';
import { appContextService } from './app_context';
import { createAppContextStartContractMock } from '../mocks';
async function mockedGetAssetsData(_a: any, _b: any, dataset: string) {
if (dataset === 'dataset1') {
@ -318,4 +324,180 @@ describe('Package policy service', () => {
).rejects.toThrow('Saved object [abc/123] conflict');
});
});
describe('runExternalCallbacks', () => {
let context: ReturnType<typeof xpackMocks.createRequestHandlerContext>;
let request: KibanaRequest;
const newPackagePolicy = {
policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c',
description: '',
enabled: true,
inputs: [],
name: 'endpoint-1',
namespace: 'default',
output_id: '',
package: {
name: 'endpoint',
title: 'Elastic Endpoint',
version: '0.5.0',
},
};
const callbackCallingOrder: string[] = [];
// Callback one adds an input that includes a `config` property
const callbackOne: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('one');
return {
...ds,
inputs: [
{
type: 'endpoint',
enabled: true,
streams: [],
config: {
one: {
value: 'inserted by callbackOne',
},
},
},
],
};
});
// Callback two adds an additional `input[0].config` property
const callbackTwo: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('two');
return {
...ds,
inputs: [
{
...ds.inputs[0],
config: {
...ds.inputs[0].config,
two: {
value: 'inserted by callbackTwo',
},
},
},
],
};
});
beforeEach(() => {
context = xpackMocks.createRequestHandlerContext();
request = httpServerMock.createKibanaRequest();
appContextService.start(createAppContextStartContractMock());
});
afterEach(() => {
appContextService.stop();
jest.clearAllMocks();
callbackCallingOrder.length = 0;
});
it('should call external callbacks in expected order', async () => {
const callbackA: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('a');
return ds;
});
const callbackB: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('b');
return ds;
});
appContextService.addExternalCallback('packagePolicyCreate', callbackA);
appContextService.addExternalCallback('packagePolicyCreate', callbackB);
await packagePolicyService.runExternalCallbacks(
'packagePolicyCreate',
newPackagePolicy,
context,
request
);
expect(callbackCallingOrder).toEqual(['a', 'b']);
});
it('should feed package policy returned by last callback', async () => {
appContextService.addExternalCallback('packagePolicyCreate', callbackOne);
appContextService.addExternalCallback('packagePolicyCreate', callbackTwo);
await packagePolicyService.runExternalCallbacks(
'packagePolicyCreate',
newPackagePolicy,
context,
request
);
expect((callbackOne as jest.Mock).mock.calls[0][0].inputs).toHaveLength(0);
expect((callbackTwo as jest.Mock).mock.calls[0][0].inputs).toHaveLength(1);
expect((callbackTwo as jest.Mock).mock.calls[0][0].inputs[0].config.one.value).toEqual(
'inserted by callbackOne'
);
});
describe('with a callback that throws an exception', () => {
const callbackThree: ExternalCallback[1] = jest.fn(async () => {
callbackCallingOrder.push('three');
throw new Error('callbackThree threw error on purpose');
});
const callbackFour: ExternalCallback[1] = jest.fn(async (ds) => {
callbackCallingOrder.push('four');
return {
...ds,
inputs: [
{
...ds.inputs[0],
config: {
...ds.inputs[0].config,
four: {
value: 'inserted by callbackFour',
},
},
},
],
};
});
beforeEach(() => {
appContextService.addExternalCallback('packagePolicyCreate', callbackOne);
appContextService.addExternalCallback('packagePolicyCreate', callbackTwo);
appContextService.addExternalCallback('packagePolicyCreate', callbackThree);
appContextService.addExternalCallback('packagePolicyCreate', callbackFour);
});
it('should fail to execute remaining callbacks after a callback exception', async () => {
try {
await packagePolicyService.runExternalCallbacks(
'packagePolicyCreate',
newPackagePolicy,
context,
request
);
} catch (e) {
// expecting an error
}
expect(callbackCallingOrder).toEqual(['one', 'two', 'three']);
expect((callbackOne as jest.Mock).mock.calls.length).toBe(1);
expect((callbackTwo as jest.Mock).mock.calls.length).toBe(1);
expect((callbackThree as jest.Mock).mock.calls.length).toBe(1);
expect((callbackFour as jest.Mock).mock.calls.length).toBe(0);
});
it('should fail to return the package policy', async () => {
expect(
packagePolicyService.runExternalCallbacks(
'packagePolicyCreate',
newPackagePolicy,
context,
request
)
).rejects.toThrow('callbackThree threw error on purpose');
});
});
});
});

View file

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObjectsClientContract } from 'src/core/server';
import { KibanaRequest, RequestHandlerContext, SavedObjectsClientContract } from 'src/core/server';
import uuid from 'uuid';
import { AuthenticatedUser } from '../../../security/server';
import {
@ -25,6 +25,8 @@ import {
PackagePolicySOAttributes,
RegistryPackage,
CallESAsCurrentUser,
NewPackagePolicySchema,
UpdatePackagePolicySchema,
} from '../types';
import { agentPolicyService } from './agent_policy';
import { outputService } from './output';
@ -33,6 +35,8 @@ import { getPackageInfo, getInstallation, ensureInstalledPackage } from './epm/p
import { getAssetsData } from './epm/packages/assets';
import { compileTemplate } from './epm/agent/agent';
import { normalizeKuery } from './saved_object';
import { appContextService } from '.';
import { ExternalCallback } from '..';
const SAVED_OBJECT_TYPE = PACKAGE_POLICY_SAVED_OBJECT_TYPE;
@ -391,6 +395,32 @@ class PackagePolicyService {
return Promise.all(inputsPromises);
}
public async runExternalCallbacks(
externalCallbackType: ExternalCallback[0],
newPackagePolicy: NewPackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
): Promise<NewPackagePolicy> {
let newData = newPackagePolicy;
const externalCallbacks = appContextService.getExternalCallbacks(externalCallbackType);
if (externalCallbacks && externalCallbacks.size > 0) {
let updatedNewData: NewPackagePolicy = newData;
for (const callback of externalCallbacks) {
const result = await callback(updatedNewData, context, request);
if (externalCallbackType === 'packagePolicyCreate') {
updatedNewData = NewPackagePolicySchema.validate(result);
} else if (externalCallbackType === 'packagePolicyUpdate') {
updatedNewData = UpdatePackagePolicySchema.validate(result);
}
}
newData = updatedNewData;
}
return newData;
}
}
function assignStreamIdToInput(packagePolicyId: string, input: NewPackagePolicyInput) {

View file

@ -18,7 +18,10 @@ import {
PackagePolicyServiceInterface,
} from '../../../fleet/server';
import { PluginStartContract as AlertsPluginStartContract } from '../../../alerts/server';
import { getPackagePolicyCreateCallback } from './ingest_integration';
import {
getPackagePolicyCreateCallback,
getPackagePolicyUpdateCallback,
} from './ingest_integration';
import { ManifestManager } from './services/artifacts';
import { MetadataQueryStrategy } from './types';
import { MetadataQueryStrategyVersions } from '../../common/endpoint/types';
@ -30,6 +33,7 @@ import { ElasticsearchAssetType } from '../../../fleet/common/types/models';
import { metadataTransformPrefix } from '../../common/endpoint/constants';
import { AppClientFactory } from '../client';
import { ConfigType } from '../config';
import { LicenseService } from '../../common/license/license';
export interface MetadataService {
queryStrategy(
@ -85,6 +89,7 @@ export type EndpointAppContextServiceStartContract = Partial<
config: ConfigType;
registerIngestCallback?: FleetStartContract['registerExternalCallback'];
savedObjectsStart: SavedObjectsServiceStart;
licenseService: LicenseService;
};
/**
@ -119,6 +124,11 @@ export class EndpointAppContextService {
dependencies.alerts
)
);
dependencies.registerIngestCallback(
'packagePolicyUpdate',
getPackagePolicyUpdateCallback(dependencies.logger, dependencies.licenseService)
);
}
}

View file

@ -11,22 +11,43 @@ import {
getManifestManagerMock,
ManifestManagerMockType,
} from './services/artifacts/manifest_manager/manifest_manager.mock';
import { getPackagePolicyCreateCallback } from './ingest_integration';
import {
getPackagePolicyCreateCallback,
getPackagePolicyUpdateCallback,
} from './ingest_integration';
import { KibanaRequest, RequestHandlerContext } from 'kibana/server';
import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__';
import { EndpointAppContextServiceStartContract } from './endpoint_app_context_services';
import { createMockEndpointAppContextServiceStartContract } from './mocks';
import { licenseMock } from '../../../licensing/common/licensing.mock';
import { LicenseService } from '../../common/license/license';
import { Subject } from 'rxjs';
import { ILicense } from '../../../licensing/common/types';
import { EndpointDocGenerator } from '../../common/endpoint/generate_data';
import { ProtectionModes } from '../../common/endpoint/types';
describe('ingest_integration tests ', () => {
let endpointAppContextMock: EndpointAppContextServiceStartContract;
let req: KibanaRequest;
let ctx: RequestHandlerContext;
const maxTimelineImportExportSize = createMockConfig().maxTimelineImportExportSize;
let licenseEmitter: Subject<ILicense>;
let licenseService: LicenseService;
const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } });
const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' } });
const generator = new EndpointDocGenerator();
beforeEach(() => {
endpointAppContextMock = createMockEndpointAppContextServiceStartContract();
ctx = requestContextMock.createTools().context;
req = httpServerMock.createKibanaRequest();
licenseEmitter = new Subject();
licenseService = new LicenseService();
licenseService.start(licenseEmitter);
});
afterEach(() => {
licenseService.stop();
licenseEmitter.complete();
});
describe('ingest_integration sanity checks', () => {
@ -179,4 +200,45 @@ describe('ingest_integration tests ', () => {
);
});
});
describe('when the license is below platinum', () => {
beforeEach(() => {
licenseEmitter.next(Gold); // set license level to gold
});
it('returns an error if paid features are turned on in the policy', async () => {
const mockPolicy = policyConfigFactory();
mockPolicy.windows.popup.malware.message = 'paid feature';
const logger = loggingSystemMock.create().get('ingest_integration.test');
const callback = getPackagePolicyUpdateCallback(logger, licenseService);
const policyConfig = generator.generatePolicyPackagePolicy();
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
await expect(() => callback(policyConfig, ctx, req)).rejects.toThrow(
'Requires Platinum license'
);
});
it('updates successfully if no paid features are turned on in the policy', async () => {
const mockPolicy = policyConfigFactory();
mockPolicy.windows.malware.mode = ProtectionModes.detect;
const logger = loggingSystemMock.create().get('ingest_integration.test');
const callback = getPackagePolicyUpdateCallback(logger, licenseService);
const policyConfig = generator.generatePolicyPackagePolicy();
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
const updatedPolicyConfig = await callback(policyConfig, ctx, req);
expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy);
});
});
describe('when the license is at least platinum', () => {
beforeEach(() => {
licenseEmitter.next(Platinum); // set license level to platinum
});
it('updates successfully when paid features are turned on', async () => {
const mockPolicy = policyConfigFactory();
mockPolicy.windows.popup.malware.message = 'paid feature';
const logger = loggingSystemMock.create().get('ingest_integration.test');
const callback = getPackagePolicyUpdateCallback(logger, licenseService);
const policyConfig = generator.generatePolicyPackagePolicy();
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
const updatedPolicyConfig = await callback(policyConfig, ctx, req);
expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy);
});
});
});

View file

@ -8,7 +8,7 @@ import { PluginStartContract as AlertsStartContract } from '../../../alerts/serv
import { SecurityPluginSetup } from '../../../security/server';
import { ExternalCallback } from '../../../fleet/server';
import { KibanaRequest, Logger, RequestHandlerContext } from '../../../../../src/core/server';
import { NewPackagePolicy } from '../../../fleet/common/types/models';
import { NewPackagePolicy, UpdatePackagePolicy } from '../../../fleet/common/types/models';
import { factory as policyConfigFactory } from '../../common/endpoint/models/policy_config';
import { NewPolicyData } from '../../common/endpoint/types';
import { ManifestManager } from './services/artifacts';
@ -20,6 +20,8 @@ import { AppClientFactory } from '../client';
import { createDetectionIndex } from '../lib/detection_engine/routes/index/create_index_route';
import { createPrepackagedRules } from '../lib/detection_engine/routes/rules/add_prepackaged_rules_route';
import { buildFrameworkRequest } from '../lib/timeline/routes/utils/common';
import { isEndpointPolicyValidForLicense } from '../../common/license/policy_config';
import { LicenseService } from '../../common/license/license';
const getManifest = async (logger: Logger, manifestManager: ManifestManager): Promise<Manifest> => {
let manifest: Manifest | null = null;
@ -164,3 +166,32 @@ export const getPackagePolicyCreateCallback = (
return handlePackagePolicyCreate;
};
export const getPackagePolicyUpdateCallback = (
logger: Logger,
licenseService: LicenseService
): ExternalCallback[1] => {
const handlePackagePolicyUpdate = async (
newPackagePolicy: NewPackagePolicy,
context: RequestHandlerContext,
request: KibanaRequest
): Promise<UpdatePackagePolicy> => {
if (newPackagePolicy.package?.name !== 'endpoint') {
return newPackagePolicy;
}
if (
!isEndpointPolicyValidForLicense(
newPackagePolicy.inputs[0].config?.policy?.value,
licenseService.getLicenseInformation()
)
) {
logger.warn('Incorrect license tier for paid policy fields');
const licenseError: Error & { statusCode?: number } = new Error('Requires Platinum license');
licenseError.statusCode = 403;
throw licenseError;
}
return newPackagePolicy;
};
return handlePackagePolicyUpdate;
};

View file

@ -25,6 +25,8 @@ import { ManifestManager } from './services/artifacts/manifest_manager/manifest_
import { getManifestManagerMock } from './services/artifacts/manifest_manager/manifest_manager.mock';
import { EndpointAppContext } from './types';
import { MetadataRequestContext } from './routes/metadata/handlers';
// import { licenseMock } from '../../../licensing/common/licensing.mock';
import { LicenseService } from '../../common/license/license';
/**
* Creates a mocked EndpointAppContext.
@ -72,6 +74,7 @@ export const createMockEndpointAppContextServiceStartContract = (): jest.Mocked<
security: securityMock.createSetup(),
alerts: alertsMock.createStart(),
config,
licenseService: new LicenseService(),
registerIngestCallback: jest.fn<
ReturnType<FleetStartContract['registerExternalCallback']>,
Parameters<FleetStartContract['registerExternalCallback']>

View file

@ -359,6 +359,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
manifestManager,
registerIngestCallback,
savedObjectsStart: core.savedObjects,
licenseService,
});
if (exceptionListsStartEnabled() && this.manifestTask) {