From 2ae90abd0222f5ef253391c2b3ce78289980c2db Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 12:03:48 -0400 Subject: [PATCH 01/24] update fleet services factory to initialize certain clients lazyly --- .../fleet/endpoint_fleet_services_factory.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts index 1f3df9d6a67d3..e7c7cdde29cfd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts @@ -38,6 +38,9 @@ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactor packageService, } = this.fleetDependencies; + let internalSoClient: SavedObjectsClientContract; + let internalReadonlySoClient: SavedObjectsClientContract; + return { agent: agentService.asInternalUser, agentPolicy, @@ -46,8 +49,21 @@ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactor endpointPolicyKuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: "endpoint"`, - internalReadonlySoClient: createInternalReadonlySoClient(this.savedObjectsStart), - internalSoClient: createInternalSoClient(this.savedObjectsStart), + get internalReadonlySoClient() { + if (!internalReadonlySoClient) { + internalReadonlySoClient = createInternalReadonlySoClient(this.savedObjectsStart); + } + + return internalReadonlySoClient; + }, + + get internalSoClient() { + if (!internalSoClient) { + internalSoClient = createInternalSoClient(this.savedObjectsStart); + } + + return internalSoClient; + }, }; } } From 9c2b48eb3946098c55fd68ccfc43e563b894aedf Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 12:05:19 -0400 Subject: [PATCH 02/24] New saved objects factory service --- .../endpoint/services/saved_objects/index.ts | 8 ++ .../saved_objects_client_factory.ts | 111 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 x-pack/plugins/security_solution/server/endpoint/services/saved_objects/index.ts create mode 100644 x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/index.ts b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/index.ts new file mode 100644 index 0000000000000..80c0b7e1d5a69 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export * from './saved_objects_client_factory'; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts new file mode 100644 index 0000000000000..bf925835d5a4c --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts @@ -0,0 +1,111 @@ +/* + * 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. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any,max-classes-per-file */ + +import type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; +import { SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID } from '@kbn/core-saved-objects-server'; +import type { HttpServiceSetup } from '@kbn/core/server'; +import { CoreKibanaRequest, type SavedObjectsClientContract } from '@kbn/core/server'; +import { DEFAULT_SPACE_ID, addSpaceIdToPath } from '@kbn/spaces-plugin/common'; +import { EndpointError } from '../../../../common/endpoint/errors'; + +type SavedObjectsClientContractKeys = keyof SavedObjectsClientContract; + +const RESTRICTED_METHODS: readonly SavedObjectsClientContractKeys[] = [ + 'bulkCreate', + 'bulkUpdate', + 'create', + 'createPointInTimeFinder', + 'delete', + 'removeReferencesTo', + 'update', + 'updateObjectsSpaces', +]; + +export class InternalReadonlySoClientMethodNotAllowedError extends EndpointError {} + +/** + * Factory service for accessing saved object clients + */ +export class SavedObjectsClientFactory { + constructor( + private readonly savedObjectsServiceStart: SavedObjectsServiceStart, + private readonly httpServiceSetup: HttpServiceSetup + ) {} + + protected createFakeHttpRequest(spaceId: string = DEFAULT_SPACE_ID): CoreKibanaRequest { + const fakeRequest = CoreKibanaRequest.from({ + headers: {}, + path: '/', + route: { settings: {} }, + url: { href: {}, hash: '' } as URL, + raw: { req: { url: '/' } } as any, + }); + + if (spaceId && spaceId !== DEFAULT_SPACE_ID) { + this.httpServiceSetup.basePath.set(fakeRequest, addSpaceIdToPath('/', spaceId)); + } + + return fakeRequest; + } + + protected toReadonly(soClient: SavedObjectsClientContract): SavedObjectsClientContract { + return new Proxy(soClient, { + get( + target: SavedObjectsClientContract, + methodName: SavedObjectsClientContractKeys, + receiver: unknown + ): unknown { + if (RESTRICTED_METHODS.includes(methodName)) { + throw new InternalReadonlySoClientMethodNotAllowedError( + `Method [${methodName}] not allowed on internal readonly SO Client` + ); + } + + return Reflect.get(target, methodName, receiver); + }, + }) as SavedObjectsClientContract; + } + + /** + * Creates a SavedObjects client that is scoped to a space (default: `Default`) + */ + createInternalScopedSoClient( + spaceId: string = DEFAULT_SPACE_ID, + readonly: boolean = true + ): SavedObjectsClientContract { + const soClient = this.savedObjectsServiceStart.getScopedClient( + this.createFakeHttpRequest(spaceId), + { excludedExtensions: [SECURITY_EXTENSION_ID] } + ); + + if (readonly) { + return this.toReadonly(soClient); + } + + return soClient; + } + + /** + * Create a SavedObjects client that is un-scoped to a space and thus can access all + * data across all spaces. + * + * **WARNING:** Use with care! + */ + createInternalUnscopedSoClient(readonly: boolean = true): SavedObjectsClientContract { + const soClient = this.savedObjectsServiceStart.getScopedClient(this.createFakeHttpRequest(), { + excludedExtensions: [SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID], + }); + + if (readonly) { + return this.toReadonly(soClient); + } + + return soClient; + } +} From d4c5bcd3be38ee5c02cb1853febbac4c2a4b0845 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 12:07:18 -0400 Subject: [PATCH 03/24] Enhance Endpoint app context service to provide access to new SO client factory --- .../endpoint/endpoint_app_context_services.ts | 30 +++++++++++++++++-- .../security_solution/server/plugin.ts | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index d7181b3ce49c6..0ea445627b253 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -7,10 +7,12 @@ import type { ElasticsearchClient, + HttpServiceSetup, KibanaRequest, Logger, LoggerFactory, SavedObjectsClientContract, + SavedObjectsServiceStart, SecurityServiceStart, } from '@kbn/core/server'; import type { ExceptionListClient, ListsServerExtensionRegistrar } from '@kbn/lists-plugin/server'; @@ -24,6 +26,7 @@ import type { PluginStartContract as AlertsPluginStartContract } from '@kbn/aler import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { FleetActionsClientInterface } from '@kbn/fleet-plugin/server/services/actions/types'; import type { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; +import { SavedObjectsClientFactory } from './services/saved_objects'; import type { ResponseActionsClient } from './services'; import { getResponseActionsClient, NormalizedExternalConnectorClient } from './services'; import { @@ -54,10 +57,12 @@ import type { FeatureUsageService } from './services/feature_usage/service'; import type { ExperimentalFeatures } from '../../common/experimental_features'; import type { ProductFeaturesService } from '../lib/product_features_service/product_features_service'; import type { ResponseActionAgentType } from '../../common/endpoint/service/response_actions/constants'; + export interface EndpointAppContextServiceSetupContract { securitySolutionRequestContextFactory: IRequestContextFactory; cloud: CloudSetup; loggerFactory: LoggerFactory; + httpServiceSetup: HttpServiceSetup; } export interface EndpointAppContextServiceStartContract { @@ -81,7 +86,8 @@ export interface EndpointAppContextServiceStartContract { messageSigningService: MessageSigningServiceInterface | undefined; esClient: ElasticsearchClient; productFeaturesService: ProductFeaturesService; - savedObjectsClient: SavedObjectsClientContract; + savedObjectsClient: SavedObjectsClientContract; // FIXME:PT can we delete this? need to check + savedObjectsServiceStart: SavedObjectsServiceStart; connectorActions: ActionsPluginStartContract; } @@ -93,6 +99,8 @@ export class EndpointAppContextService { private setupDependencies: EndpointAppContextServiceSetupContract | null = null; private startDependencies: EndpointAppContextServiceStartContract | null = null; private fleetServicesFactory: EndpointFleetServicesFactoryInterface | null = null; + private savedObjectsFactoryService: SavedObjectsClientFactory | null = null; + public security: SecurityServiceStart | undefined; public setup(dependencies: EndpointAppContextServiceSetupContract) { @@ -107,6 +115,10 @@ export class EndpointAppContextService { this.startDependencies = dependencies; this.security = dependencies.security; this.fleetServicesFactory = dependencies.endpointFleetServicesFactory; + this.savedObjectsFactoryService = new SavedObjectsClientFactory( + dependencies.savedObjectsServiceStart, + this.setupDependencies.httpServiceSetup + ); if (dependencies.registerIngestCallback && dependencies.manifestManager) { const { @@ -177,7 +189,21 @@ export class EndpointAppContextService { } } - public stop() {} + public stop() { + this.startDependencies = null; + this.savedObjectsFactoryService = null; + } + + /** + * Property providing access to saved objects client factory + */ + public get savedObjects(): SavedObjectsClientFactory { + if (!this.savedObjectsFactoryService) { + throw new EndpointAppContentServicesNotStartedError(); + } + + return this.savedObjectsFactoryService; + } private getFleetAuthzService(): FleetStartContract['authz'] { if (!this.startDependencies?.fleetAuthzService) { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index c24e70baa5db8..a60f3d2a04307 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -238,6 +238,7 @@ export class Plugin implements ISecuritySolutionPlugin { securitySolutionRequestContextFactory: requestContextFactory, cloud: plugins.cloud, loggerFactory: this.pluginContext.logger, + httpServiceSetup: core.http, }); initUsageCollectors({ @@ -640,6 +641,7 @@ export class Plugin implements ISecuritySolutionPlugin { esClient: core.elasticsearch.client.asInternalUser, productFeaturesService, savedObjectsClient, + savedObjectsServiceStart: core.savedObjects, connectorActions: plugins.actions, }); From 6e3792bd1def372ec31cf76695f24b3a8133ab0f Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 12:12:02 -0400 Subject: [PATCH 04/24] Add TODO reminders to delete old SO modules --- .../server/endpoint/utils/create_internal_readonly_so_client.ts | 2 ++ .../server/endpoint/utils/create_internal_so_client.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts index b621222e79c0a..11c2aabd301d9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts @@ -26,6 +26,8 @@ const RESTRICTED_METHODS: readonly SavedObjectsClientContractKeys[] = [ export class InternalReadonlySoClientMethodNotAllowedError extends EndpointError {} +// FIXME:PT find usages of createInternalReadonlySoClient() and delete them. Use EndpontAppContextService instead + /** * Creates an internal (system user) Saved Objects client (permissions turned off) that can only perform READ * operations. diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts index 88e0d7a70a4c3..1570715b59e54 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts @@ -9,6 +9,8 @@ import type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import type { KibanaRequest, SavedObjectsClientContract } from '@kbn/core/server'; +// FIXME:PT find usages of createInternalSoClient() and delete them. Use EndpontAppContextService instead + export const createInternalSoClient = ( savedObjectsServiceStart: SavedObjectsServiceStart ): SavedObjectsClientContract => { From 29cf484ab74b815ede6f3b7fce9491d2d31b96eb Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 17:18:23 -0400 Subject: [PATCH 05/24] Refactor EndpointMetadataService construction arguments --- .../metadata/endpoint_metadata_service.ts | 58 ++++--------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts index f5735f653efd1..4346ecc98898e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts @@ -5,16 +5,10 @@ * 2.0. */ import { uniq } from 'lodash'; -import type { - ElasticsearchClient, - Logger, - SavedObjectsClientContract, - SavedObjectsServiceStart, -} from '@kbn/core/server'; +import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; import type { SearchResponse, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import type { Agent, AgentPolicy, PackagePolicy } from '@kbn/fleet-plugin/common'; -import type { AgentPolicyServiceInterface, PackagePolicyClient } from '@kbn/fleet-plugin/server'; import { AgentNotFoundError } from '@kbn/fleet-plugin/server'; import type { HostInfo, @@ -48,7 +42,6 @@ import { fleetAgentStatusToEndpointHostStatus, wrapErrorIfNeeded, } from '../../utils'; -import { createInternalReadonlySoClient } from '../../utils/create_internal_readonly_so_client'; import { getAllEndpointPackagePolicies } from '../../routes/metadata/support/endpoint_package_policies'; import type { GetMetadataListRequestQuery } from '../../../../common/api/endpoint'; import { EndpointError } from '../../../../common/endpoint/errors'; @@ -65,39 +58,13 @@ const isAgentPolicyWithPackagePolicies = ( }; export class EndpointMetadataService { - /** - * For internal use only by the `this.DANGEROUS_INTERNAL_SO_CLIENT` - * @deprecated - */ - private __DANGEROUS_INTERNAL_SO_CLIENT: SavedObjectsClientContract | undefined; - constructor( - private savedObjectsStart: SavedObjectsServiceStart, - private readonly agentPolicyService: AgentPolicyServiceInterface, - private readonly packagePolicyService: PackagePolicyClient, + private readonly esClient: ElasticsearchClient, + private readonly soClient: SavedObjectsClientContract, + private readonly fleetServices: EndpointFleetServicesInterface, private readonly logger?: Logger ) {} - /** - * An INTERNAL Saved Object client that is effectively the system user and has all privileges and permissions and - * can access any saved object. Used primarly to retrieve fleet data for endpoint enrichment (so that users are - * not required to have superuser role) - * - * **IMPORTANT: SHOULD BE USED ONLY FOR READ-ONLY ACCESS AND WITH DISCRETION** - * - * @private - */ - private get DANGEROUS_INTERNAL_SO_CLIENT() { - // The INTERNAL SO client must be created during the first time its used. This is because creating it during - // instance initialization (in `constructor(){}`) causes the SO Client to be invalid (perhaps because this - // instantiation is happening during the plugin's the start phase) - if (!this.__DANGEROUS_INTERNAL_SO_CLIENT) { - this.__DANGEROUS_INTERNAL_SO_CLIENT = createInternalReadonlySoClient(this.savedObjectsStart); - } - - return this.__DANGEROUS_INTERNAL_SO_CLIENT; - } - /** * Retrieve a single endpoint host metadata. Note that the return endpoint document, if found, * could be associated with a Fleet Agent that is no longer active. If wanting to ensure the @@ -328,8 +295,8 @@ export class EndpointMetadataService { * @throws */ async getFleetAgentPolicy(agentPolicyId: string): Promise { - const agentPolicy = await this.agentPolicyService - .get(this.DANGEROUS_INTERNAL_SO_CLIENT, agentPolicyId, true) + const agentPolicy = await this.fleetServices.agentPolicy + .get(this.soClient, agentPolicyId, true) .catch(catchAndWrapError); if (agentPolicy) { @@ -347,8 +314,8 @@ export class EndpointMetadataService { * @throws */ async getFleetEndpointPackagePolicy(endpointPolicyId: string): Promise { - const endpointPackagePolicy = await this.packagePolicyService - .get(this.DANGEROUS_INTERNAL_SO_CLIENT, endpointPolicyId) + const endpointPackagePolicy = await this.fleetServices.packagePolicy + .get(this.soClient, endpointPolicyId) .catch(catchAndWrapError); if (!endpointPackagePolicy) { @@ -404,8 +371,8 @@ export class EndpointMetadataService { const agentPolicyIds: string[] = docs.map((doc) => doc._source?.united?.agent?.policy_id ?? ''); const agentPolicies = - (await this.agentPolicyService - .getByIds(this.DANGEROUS_INTERNAL_SO_CLIENT, agentPolicyIds) + (await this.fleetServices.agentPolicy + .getByIds(this.soClient, agentPolicyIds) .catch(catchAndWrapError)) ?? []; const agentPoliciesMap = agentPolicies.reduce>( @@ -463,10 +430,7 @@ export class EndpointMetadataService { } async getAllEndpointPackagePolicies() { - return getAllEndpointPackagePolicies( - this.packagePolicyService, - this.DANGEROUS_INTERNAL_SO_CLIENT - ); + return getAllEndpointPackagePolicies(this.fleetServices.packagePolicy, this.soClient); } async getMetadataForEndpoints( From 2e82049136490c817df6a8c35398f64e4f8ecf3e Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 17:19:22 -0400 Subject: [PATCH 06/24] Refactor EndpointFleetServicesFactory constructor arguments to use new SavedObjectsClientFactory --- .../fleet/endpoint_fleet_services_factory.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts index e7c7cdde29cfd..c9c18c2329fca 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SavedObjectsClientContract, SavedObjectsServiceStart } from '@kbn/core/server'; +import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { AgentClient, AgentPolicyServiceInterface, @@ -16,6 +16,7 @@ import type { import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import { createInternalSoClient } from '../../utils/create_internal_so_client'; import { createInternalReadonlySoClient } from '../../utils/create_internal_readonly_so_client'; +import type { SavedObjectsClientFactory } from '../saved_objects'; export interface EndpointFleetServicesFactoryInterface { asInternalUser(): EndpointInternalFleetServicesInterface; @@ -23,11 +24,8 @@ export interface EndpointFleetServicesFactoryInterface { export class EndpointFleetServicesFactory implements EndpointFleetServicesFactoryInterface { constructor( - private readonly fleetDependencies: Pick< - FleetStartContract, - 'agentService' | 'packageService' | 'packagePolicyService' | 'agentPolicyService' - >, - private savedObjectsStart: SavedObjectsServiceStart + private readonly fleetDependencies: FleetStartContract, + public readonly savedObjects: SavedObjectsClientFactory ) {} asInternalUser(): EndpointInternalFleetServicesInterface { @@ -44,11 +42,13 @@ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactor return { agent: agentService.asInternalUser, agentPolicy, + packages: packageService.asInternalUser, packagePolicy, endpointPolicyKuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: "endpoint"`, + // FIXME:PT remove this property get internalReadonlySoClient() { if (!internalReadonlySoClient) { internalReadonlySoClient = createInternalReadonlySoClient(this.savedObjectsStart); @@ -57,6 +57,7 @@ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactor return internalReadonlySoClient; }, + // FIXME:PT remove this property get internalSoClient() { if (!internalSoClient) { internalSoClient = createInternalSoClient(this.savedObjectsStart); From e898fab52298290cffdb1d03b28ff473240a54d1 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 19 Sep 2024 17:21:42 -0400 Subject: [PATCH 07/24] Refactor EndpointAppContextServices `.start()` contract --- .../endpoint/endpoint_app_context_services.ts | 198 ++++++++++-------- .../security_solution/server/plugin.ts | 119 ++++------- 2 files changed, 151 insertions(+), 166 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 0ea445627b253..8a5856d43f1ab 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -9,9 +9,7 @@ import type { ElasticsearchClient, HttpServiceSetup, KibanaRequest, - Logger, LoggerFactory, - SavedObjectsClientContract, SavedObjectsServiceStart, SecurityServiceStart, } from '@kbn/core/server'; @@ -26,6 +24,7 @@ import type { PluginStartContract as AlertsPluginStartContract } from '@kbn/aler import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { FleetActionsClientInterface } from '@kbn/fleet-plugin/server/services/actions/types'; import type { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { SavedObjectsClientFactory } from './services/saved_objects'; import type { ResponseActionsClient } from './services'; import { getResponseActionsClient, NormalizedExternalConnectorClient } from './services'; @@ -41,7 +40,7 @@ import type { ManifestManager } from './services/artifacts'; import type { ConfigType } from '../config'; import type { IRequestContextFactory } from '../request_context_factory'; import type { LicenseService } from '../../common/license'; -import type { EndpointMetadataService } from './services/metadata'; +import { EndpointMetadataService } from './services/metadata'; import { EndpointAppContentServicesNotSetUpError, EndpointAppContentServicesNotStartedError, @@ -50,6 +49,7 @@ import type { EndpointFleetServicesFactoryInterface, EndpointInternalFleetServicesInterface, } from './services/fleet/endpoint_fleet_services_factory'; +import { EndpointFleetServicesFactory } from './services/fleet/endpoint_fleet_services_factory'; import { registerListsPluginEndpointExtensionPoints } from '../lists_integration'; import type { EndpointAuthz } from '../../common/endpoint/types/authz'; import { calculateEndpointAuthz } from '../../common/endpoint/service/authz'; @@ -66,17 +66,11 @@ export interface EndpointAppContextServiceSetupContract { } export interface EndpointAppContextServiceStartContract { - fleetAuthzService?: FleetStartContract['authz']; - createFleetFilesClient: FleetStartContract['createFilesClient']; - createFleetActionsClient: FleetStartContract['createFleetActionsClient']; - logger: Logger; - endpointMetadataService: EndpointMetadataService; - endpointFleetServicesFactory: EndpointFleetServicesFactoryInterface; - manifestManager?: ManifestManager; + fleetStartServices: FleetStartContract; + manifestManager: ManifestManager; security: SecurityServiceStart; alerting: AlertsPluginStartContract; config: ConfigType; - registerIngestCallback?: FleetStartContract['registerExternalCallback']; registerListsServerExtension?: ListsServerExtensionRegistrar; licenseService: LicenseService; exceptionListsClient: ExceptionListClient | undefined; @@ -84,9 +78,9 @@ export interface EndpointAppContextServiceStartContract { featureUsageService: FeatureUsageService; experimentalFeatures: ExperimentalFeatures; messageSigningService: MessageSigningServiceInterface | undefined; + /** An internal ES client */ esClient: ElasticsearchClient; productFeaturesService: ProductFeaturesService; - savedObjectsClient: SavedObjectsClientContract; // FIXME:PT can we delete this? need to check savedObjectsServiceStart: SavedObjectsServiceStart; connectorActions: ActionsPluginStartContract; } @@ -112,86 +106,104 @@ export class EndpointAppContextService { throw new EndpointAppContentServicesNotSetUpError(); } - this.startDependencies = dependencies; - this.security = dependencies.security; - this.fleetServicesFactory = dependencies.endpointFleetServicesFactory; - this.savedObjectsFactoryService = new SavedObjectsClientFactory( + const savedObjectsFactory = new SavedObjectsClientFactory( dependencies.savedObjectsServiceStart, this.setupDependencies.httpServiceSetup ); - if (dependencies.registerIngestCallback && dependencies.manifestManager) { - const { - registerIngestCallback, - logger, - manifestManager, - alerting, - licenseService, - exceptionListsClient, - featureUsageService, - endpointMetadataService, - esClient, - productFeaturesService, - savedObjectsClient, - } = dependencies; - - registerIngestCallback( - 'agentPolicyCreate', - getAgentPolicyCreateCallback(logger, productFeaturesService) - ); - registerIngestCallback( - 'agentPolicyUpdate', - getAgentPolicyUpdateCallback(logger, productFeaturesService) - ); + this.startDependencies = dependencies; + this.security = dependencies.security; + this.savedObjectsFactoryService = savedObjectsFactory; + this.fleetServicesFactory = new EndpointFleetServicesFactory( + dependencies.fleetStartServices, + savedObjectsFactory + ); - registerIngestCallback( - 'packagePolicyCreate', - getPackagePolicyCreateCallback( - logger, - manifestManager, - this.setupDependencies.securitySolutionRequestContextFactory, - alerting, - licenseService, - exceptionListsClient, - this.setupDependencies.cloud, - productFeaturesService - ) - ); + this.registerFleetExtensions(); + this.registerListsExtensions(); + } - registerIngestCallback( - 'packagePolicyPostCreate', - getPackagePolicyPostCreateCallback(logger, exceptionListsClient) - ); + public stop() { + this.startDependencies = null; + this.savedObjectsFactoryService = null; + } - registerIngestCallback( - 'packagePolicyUpdate', - getPackagePolicyUpdateCallback( - logger, - licenseService, - featureUsageService, - endpointMetadataService, - this.setupDependencies.cloud, - esClient, - productFeaturesService - ) + private registerListsExtensions() { + if (this.startDependencies?.registerListsServerExtension) { + registerListsPluginEndpointExtensionPoints( + this.startDependencies?.registerListsServerExtension, + this ); + } + } - registerIngestCallback( - 'packagePolicyPostDelete', - getPackagePolicyDeleteCallback(exceptionListsClient, savedObjectsClient) - ); + private registerFleetExtensions() { + if (!this.setupDependencies) { + throw new EndpointAppContentServicesNotSetUpError(); + } + if (!this.startDependencies) { + throw new EndpointAppContentServicesNotStartedError(); } - if (this.startDependencies.registerListsServerExtension) { - const { registerListsServerExtension } = this.startDependencies; + const { + fleetStartServices: { registerExternalCallback: registerFleetCallback }, + manifestManager, + alerting, + licenseService, + exceptionListsClient, + featureUsageService, + esClient, + productFeaturesService, + } = this.startDependencies; + const endpointMetadataService = this.getEndpointMetadataService(); + const soClient = this.savedObjects.createInternalScopedSoClient(undefined, false); + const logger = this.createLogger('endpointFleetExtension'); + + registerFleetCallback( + 'agentPolicyCreate', + getAgentPolicyCreateCallback(logger, productFeaturesService) + ); + registerFleetCallback( + 'agentPolicyUpdate', + getAgentPolicyUpdateCallback(logger, productFeaturesService) + ); - registerListsPluginEndpointExtensionPoints(registerListsServerExtension, this); - } - } + registerFleetCallback( + 'packagePolicyCreate', + getPackagePolicyCreateCallback( + logger, + manifestManager, + this.setupDependencies.securitySolutionRequestContextFactory, + alerting, + licenseService, + exceptionListsClient, + this.setupDependencies.cloud, + productFeaturesService + ) + ); - public stop() { - this.startDependencies = null; - this.savedObjectsFactoryService = null; + registerFleetCallback( + 'packagePolicyPostCreate', + getPackagePolicyPostCreateCallback(logger, exceptionListsClient) + ); + + registerFleetCallback( + 'packagePolicyUpdate', + getPackagePolicyUpdateCallback( + logger, + licenseService, + featureUsageService, + endpointMetadataService, + this.setupDependencies.cloud, + esClient, + productFeaturesService + ) + ); + + registerFleetCallback( + 'packagePolicyPostDelete', + getPackagePolicyDeleteCallback(exceptionListsClient, soClient) + ); } /** @@ -206,16 +218,16 @@ export class EndpointAppContextService { } private getFleetAuthzService(): FleetStartContract['authz'] { - if (!this.startDependencies?.fleetAuthzService) { + if (!this.startDependencies?.fleetStartServices) { throw new EndpointAppContentServicesNotStartedError(); } - return this.startDependencies.fleetAuthzService; + return this.startDependencies.fleetStartServices.authz; } public createLogger(...contextParts: string[]) { if (!this.setupDependencies?.loggerFactory) { - throw new EndpointAppContentServicesNotStartedError(); + throw new EndpointAppContentServicesNotSetUpError(); } return this.setupDependencies.loggerFactory.get(...contextParts); @@ -235,11 +247,17 @@ export class EndpointAppContextService { ); } - public getEndpointMetadataService(): EndpointMetadataService { + public getEndpointMetadataService(spaceId: string = DEFAULT_SPACE_ID): EndpointMetadataService { if (this.startDependencies == null) { throw new EndpointAppContentServicesNotStartedError(); } - return this.startDependencies.endpointMetadataService; + + return new EndpointMetadataService( + this.startDependencies.esClient, + this.savedObjects.createInternalScopedSoClient(spaceId, false), + this.getInternalFleetServices(), + this.createLogger('endpointMetadata') + ); } public getInternalFleetServices(): EndpointInternalFleetServicesInterface { @@ -340,29 +358,29 @@ export class EndpointAppContextService { } public async getFleetToHostFilesClient() { - if (!this.startDependencies?.createFleetFilesClient) { + if (!this.startDependencies?.fleetStartServices) { throw new EndpointAppContentServicesNotStartedError(); } - return this.startDependencies.createFleetFilesClient.toHost( + return this.startDependencies.fleetStartServices.createFilesClient.toHost( 'endpoint', this.startDependencies.config.maxUploadResponseActionFileBytes ); } public async getFleetFromHostFilesClient(): Promise { - if (!this.startDependencies?.createFleetFilesClient) { + if (!this.startDependencies?.fleetStartServices) { throw new EndpointAppContentServicesNotStartedError(); } - return this.startDependencies.createFleetFilesClient.fromHost('endpoint'); + return this.startDependencies.fleetStartServices.createFilesClient.fromHost('endpoint'); } public async getFleetActionsClient(): Promise { - if (!this.startDependencies?.createFleetActionsClient) { + if (!this.startDependencies?.fleetStartServices) { throw new EndpointAppContentServicesNotStartedError(); } - return this.startDependencies.createFleetActionsClient('endpoint'); + return this.startDependencies.fleetStartServices.createFleetActionsClient('endpoint'); } } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index b11715e78fbff..d5ffceb2ec7b7 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -76,7 +76,6 @@ import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; import previewPolicy from './lib/detection_engine/routes/index/preview_policy.json'; import type { IRuleMonitoringService } from './lib/detection_engine/rule_monitoring'; import { createRuleMonitoringService } from './lib/detection_engine/rule_monitoring'; -import { EndpointMetadataService } from './endpoint/services/metadata'; import type { CreateRuleAdditionalOptions, CreateRuleOptions, @@ -103,7 +102,6 @@ import type { SecuritySolutionPluginStart, SecuritySolutionPluginStartDependencies, } from './plugin_contract'; -import { EndpointFleetServicesFactory } from './endpoint/services/fleet'; import { featureUsageService } from './endpoint/services/feature_usage'; import { setIsElasticCloudDeployment } from './lib/telemetry/helpers'; import { artifactService } from './lib/telemetry/artifact'; @@ -526,27 +524,10 @@ export class Plugin implements ISecuritySolutionPlugin { // from where authz can be derived) false ); - const { - authz, - agentService, - packageService, - packagePolicyService, - agentPolicyService, - createFilesClient, - createFleetActionsClient, - } = - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - plugins.fleet!; - let manifestManager: ManifestManager | undefined; - const endpointFleetServicesFactory = new EndpointFleetServicesFactory( - { - agentService, - packageService, - packagePolicyService, - agentPolicyService, - }, - core.savedObjects - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const fleetStartServices = plugins.fleet!; + + const { packageService } = fleetStartServices; this.licensing$ = plugins.licensing.license$; @@ -564,26 +545,43 @@ export class Plugin implements ISecuritySolutionPlugin { assistantModelEvaluation: config.experimentalFeatures.assistantModelEvaluation, }); + const manifestManager = new ManifestManager({ + savedObjectsClient, + exceptionListClient, + artifactClient: new EndpointArtifactClient( + fleetStartServices.createArtifactsClient('endpoint') + ), + packagePolicyService: fleetStartServices.packagePolicyService, + logger: this.pluginContext.logger.get('ManifestManager'), + experimentalFeatures: config.experimentalFeatures, + packagerTaskPackagePolicyUpdateBatchSize: config.packagerTaskPackagePolicyUpdateBatchSize, + esClient: core.elasticsearch.client.asInternalUser, + productFeaturesService, + }); + + this.endpointAppContextService.start({ + fleetStartServices, + security: core.security, + alerting: plugins.alerting, + config, + cases: plugins.cases, + manifestManager, + licenseService, + exceptionListsClient: exceptionListClient, + registerListsServerExtension: this.lists?.registerExtension, + featureUsageService, + experimentalFeatures: config.experimentalFeatures, + messageSigningService: plugins.fleet?.messageSigningService, + esClient: core.elasticsearch.client.asInternalUser, + productFeaturesService, + savedObjectsServiceStart: core.savedObjects, + connectorActions: plugins.actions, + }); + if (this.lists && plugins.taskManager && plugins.fleet) { // Exceptions, Artifacts and Manifests start const taskManager = plugins.taskManager; - const artifactClient = new EndpointArtifactClient( - plugins.fleet.createArtifactsClient('endpoint') - ); - manifestManager = new ManifestManager({ - savedObjectsClient, - artifactClient, - exceptionListClient, - packagePolicyService: plugins.fleet.packagePolicyService, - logger: this.pluginContext.logger.get('ManifestManager'), - experimentalFeatures: config.experimentalFeatures, - packagerTaskPackagePolicyUpdateBatchSize: config.packagerTaskPackagePolicyUpdateBatchSize, - esClient: core.elasticsearch.client.asInternalUser, - productFeaturesService, - }); - - // Migrate artifacts to fleet and then start the manifest task after that is done plugins.fleet .fleetSetupCompleted() .then(async () => { @@ -596,24 +594,24 @@ export class Plugin implements ISecuritySolutionPlugin { logger.error(new Error('User artifacts task not available.')); } + const fleetServices = this.endpointAppContextService.getInternalFleetServices(); + await turnOffPolicyProtectionsIfNotSupported( core.elasticsearch.client.asInternalUser, - endpointFleetServicesFactory.asInternalUser(), + fleetServices, productFeaturesService, logger ); - await turnOffAgentPolicyFeatures( - endpointFleetServicesFactory.asInternalUser(), - productFeaturesService, - logger - ); + await turnOffAgentPolicyFeatures(fleetServices, productFeaturesService, logger); }) .catch(() => {}); // License related start licenseService.start(this.licensing$); + featureUsageService.start(plugins.licensing); + this.policyWatcher = new PolicyWatcher( plugins.fleet.packagePolicyService, core.savedObjects, @@ -623,37 +621,6 @@ export class Plugin implements ISecuritySolutionPlugin { this.policyWatcher.start(licenseService); } - this.endpointAppContextService.start({ - fleetAuthzService: authz, - createFleetFilesClient: createFilesClient, - endpointMetadataService: new EndpointMetadataService( - core.savedObjects, - agentPolicyService, - packagePolicyService, - logger - ), - endpointFleetServicesFactory, - security: core.security, - alerting: plugins.alerting, - config, - cases: plugins.cases, - logger, - manifestManager, - registerIngestCallback, - licenseService, - exceptionListsClient: exceptionListClient, - registerListsServerExtension: this.lists?.registerExtension, - featureUsageService, - experimentalFeatures: config.experimentalFeatures, - messageSigningService: plugins.fleet?.messageSigningService, - createFleetActionsClient, - esClient: core.elasticsearch.client.asInternalUser, - productFeaturesService, - savedObjectsClient, - savedObjectsServiceStart: core.savedObjects, - connectorActions: plugins.actions, - }); - if (plugins.taskManager) { this.completeExternalResponseActionsTask .start({ From 360e1adeb4b34df54a3f6a408e98cadcdcb688d0 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Fri, 20 Sep 2024 11:22:02 -0400 Subject: [PATCH 08/24] Update Fleet server mocks and add new one for FleetStartContract (plugin#start) --- x-pack/plugins/fleet/server/mocks/index.ts | 39 +++++++++++++++++-- x-pack/plugins/fleet/server/plugin.ts | 10 ++++- .../services/agents/agent_service.mock.ts | 4 +- .../fleet/server/services/artifacts/mocks.ts | 4 +- .../services/epm/package_service.mock.ts | 6 ++- .../fleet/server/services/files/mocks.ts | 10 +++++ 6 files changed, 64 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 7b11654f8def4..282aa5cd7f8cf 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -22,9 +22,17 @@ import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; import { SPACES_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; + +import { createFleetActionsClientMock } from '../services/actions/mocks'; + +import { createFleetFilesClientFactoryMock } from '../services/files/mocks'; + +import { createArtifactsClientMock } from '../services/artifacts/mocks'; + import type { PackagePolicyClient } from '../services/package_policy_service'; import type { AgentPolicyServiceInterface } from '../services'; -import type { FleetAppContext } from '../plugin'; +import type { FleetAppContext, FleetStartContract } from '../plugin'; import { createMockTelemetryEventsSender } from '../telemetry/__mocks__'; import type { FleetConfigType } from '../../common/types'; import type { ExperimentalFeatures } from '../../common/experimental_features'; @@ -35,6 +43,8 @@ import { packageServiceMock } from '../services/epm/package_service.mock'; import type { UninstallTokenServiceInterface } from '../services/security/uninstall_token_service'; import type { MessageSigningServiceInterface } from '../services/security'; +import { getPackageSpecTagId } from '../services/epm/kibana/assets/tag_assets'; + import { PackagePolicyMocks } from './package_policy.mocks'; // Export all mocks from artifacts @@ -238,7 +248,7 @@ export const createMockAgentClient = () => agentServiceMock.createClient(); */ export const createMockPackageService = () => packageServiceMock.create(); -export function createMessageSigningServiceMock(): MessageSigningServiceInterface { +export function createMessageSigningServiceMock(): jest.Mocked { return { isEncryptionAvailable: true, generateKeyPair: jest.fn(), @@ -253,7 +263,7 @@ export function createMessageSigningServiceMock(): MessageSigningServiceInterfac }; } -export function createUninstallTokenServiceMock(): UninstallTokenServiceInterface { +export function createUninstallTokenServiceMock(): DeeplyMockedKeys { return { getToken: jest.fn(), getTokenMetadata: jest.fn(), @@ -269,3 +279,26 @@ export function createUninstallTokenServiceMock(): UninstallTokenServiceInterfac scoped: jest.fn().mockImplementation(() => createUninstallTokenServiceMock()), }; } + +export const createFleetStartContractMock = (): DeeplyMockedKeys => { + const fleetAuthzMock = createFleetAuthzMock(); + const fleetArtifactsClient = createArtifactsClientMock(); + + const startContract: DeeplyMockedKeys = { + fleetSetupCompleted: jest.fn(async () => {}), + authz: { fromRequest: jest.fn(async (_) => fleetAuthzMock) }, + packageService: createMockPackageService(), + agentService: createMockAgentService(), + packagePolicyService: createPackagePolicyServiceMock(), + agentPolicyService: createMockAgentPolicyService(), + registerExternalCallback: jest.fn(), + createArtifactsClient: jest.fn((_) => fleetArtifactsClient), + createFilesClient: createFleetFilesClientFactoryMock(), + messageSigningService: createMessageSigningServiceMock(), + uninstallTokenService: createUninstallTokenServiceMock(), + createFleetActionsClient: jest.fn((_) => createFleetActionsClientMock()), + getPackageSpecTagId: jest.fn(getPackageSpecTagId), + }; + + return startContract; +}; diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 21c3f1bf97f12..c767424cef36a 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -72,6 +72,7 @@ import { AGENT_POLICY_SAVED_OBJECT_TYPE, LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, } from '../common/constants'; + import { getFilesClientFactory } from './services/files/get_files_client_factory'; import type { MessageSigningServiceInterface } from './services/security'; @@ -98,7 +99,12 @@ import { registerEncryptedSavedObjects, registerSavedObjects } from './saved_obj import { registerRoutes } from './routes'; import type { ExternalCallback, FleetRequestHandlerContext } from './types'; -import type { AgentPolicyServiceInterface, AgentService, PackageService } from './services'; +import type { + AgentPolicyServiceInterface, + AgentService, + ArtifactsClientInterface, + PackageService, +} from './services'; import { agentPolicyService, AgentServiceImpl, @@ -236,7 +242,7 @@ export interface FleetStartContract { * Create a Fleet Artifact Client instance * @param packageName */ - createArtifactsClient: (packageName: string) => FleetArtifactsClient; + createArtifactsClient: (packageName: string) => ArtifactsClientInterface; /** * Create a Fleet Files client instance diff --git a/x-pack/plugins/fleet/server/services/agents/agent_service.mock.ts b/x-pack/plugins/fleet/server/services/agents/agent_service.mock.ts index 9bebdc4c1b24a..d6d6922e1bdf9 100644 --- a/x-pack/plugins/fleet/server/services/agents/agent_service.mock.ts +++ b/x-pack/plugins/fleet/server/services/agents/agent_service.mock.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; + import type { AgentClient, AgentService } from './agent_service'; const createClientMock = (): jest.Mocked => ({ @@ -15,7 +17,7 @@ const createClientMock = (): jest.Mocked => ({ getLatestAgentAvailableVersion: jest.fn(), }); -const createServiceMock = (): jest.Mocked => ({ +const createServiceMock = (): DeeplyMockedKeys => ({ asInternalUser: createClientMock(), asScoped: jest.fn().mockReturnValue(createClientMock()), }); diff --git a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts index 4e5d8c93f0643..166420c9aee43 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts @@ -12,6 +12,8 @@ import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import type { SearchHit, ESSearchResponse } from '@kbn/es-types'; +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; + import type { Artifact, ArtifactElasticsearchProperties, @@ -20,7 +22,7 @@ import type { } from './types'; import { newArtifactToElasticsearchProperties } from './mappings'; -export const createArtifactsClientMock = (): jest.Mocked => { +export const createArtifactsClientMock = (): DeeplyMockedKeys => { return { getArtifact: jest.fn().mockResolvedValue(generateArtifactMock()), createArtifact: jest.fn().mockResolvedValue(generateArtifactMock()), diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts index e07b131a9e806..39d0451687de5 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; + import type { PackageClient, PackageService } from './package_service'; const createClientMock = (): jest.Mocked => ({ @@ -21,8 +23,8 @@ const createClientMock = (): jest.Mocked => ({ reinstallEsAssets: jest.fn(), }); -const createServiceMock = (): PackageService => ({ - asScoped: jest.fn(createClientMock), +const createServiceMock = (): DeeplyMockedKeys => ({ + asScoped: jest.fn((_) => createClientMock()), asInternalUser: createClientMock(), }); diff --git a/x-pack/plugins/fleet/server/services/files/mocks.ts b/x-pack/plugins/fleet/server/services/files/mocks.ts index 2000f8eefc02b..091dc207b33bc 100644 --- a/x-pack/plugins/fleet/server/services/files/mocks.ts +++ b/x-pack/plugins/fleet/server/services/files/mocks.ts @@ -9,7 +9,10 @@ import { Readable } from 'stream'; import type { estypes } from '@elastic/elasticsearch'; +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; + import type { + FilesClientFactory, FleetFile, FleetFromHostFileClientInterface, FleetToHostFileClientInterface, @@ -153,3 +156,10 @@ export const createHapiReadableStreamMock = (): HapiReadableStream => { return readable; }; + +export const createFleetFilesClientFactoryMock = (): DeeplyMockedKeys => { + return { + toHost: jest.fn((_) => createFleetToHostFilesClientMock()), + fromHost: jest.fn((_) => createFleetFromHostFilesClientMock()), + }; +}; From 11124e4b2fda3dfca2b8d201001729bfbbbbd4ca Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Fri, 20 Sep 2024 11:59:47 -0400 Subject: [PATCH 09/24] Update mocks for Endpoint App Context services --- .../endpoint/endpoint_app_context_services.ts | 5 +- .../server/endpoint/mocks/mocks.ts | 92 ++++++------------- .../security_solution/server/plugin.ts | 1 - 3 files changed, 28 insertions(+), 70 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 8a5856d43f1ab..1c6cdf87c0e6b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -77,7 +77,6 @@ export interface EndpointAppContextServiceStartContract { cases: CasesServerStart | undefined; featureUsageService: FeatureUsageService; experimentalFeatures: ExperimentalFeatures; - messageSigningService: MessageSigningServiceInterface | undefined; /** An internal ES client */ esClient: ElasticsearchClient; productFeaturesService: ProductFeaturesService; @@ -310,11 +309,11 @@ export class EndpointAppContextService { } public getMessageSigningService(): MessageSigningServiceInterface { - if (!this.startDependencies?.messageSigningService) { + if (!this.startDependencies?.fleetStartServices.messageSigningService) { throw new EndpointAppContentServicesNotStartedError(); } - return this.startDependencies.messageSigningService; + return this.startDependencies?.fleetStartServices.messageSigningService; } public getInternalResponseActionsClient({ diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts index 141a5ebb440f6..de21bf51c8175 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts @@ -16,6 +16,7 @@ import { savedObjectsClientMock, savedObjectsServiceMock, securityServiceMock, + coreMock, } from '@kbn/core/server/mocks'; import type { IRouter, @@ -24,23 +25,20 @@ import type { RouteConfig, RouteMethod, SavedObjectsClientContract, + SecurityServiceStart, } from '@kbn/core/server'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { cloudMock } from '@kbn/cloud-plugin/server/mocks'; -import type { FleetStartContract } from '@kbn/fleet-plugin/server'; import { createFleetActionsClientMock, createFleetFromHostFilesClientMock, + createFleetStartContractMock, createFleetToHostFilesClientMock, createMessageSigningServiceMock, - createMockAgentPolicyService, - createMockAgentService, - createMockPackageService, createPackagePolicyServiceMock, } from '@kbn/fleet-plugin/server/mocks'; -import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks'; import type { RequestFixtureOptions, RouterMock } from '@kbn/core-http-router-server-mocks'; import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; @@ -48,8 +46,10 @@ import { casesPluginMock } from '@kbn/cases-plugin/server/mocks'; import { createCasesClientMock } from '@kbn/cases-plugin/server/client/mocks'; import type { AddVersionOpts, VersionedRouteConfig } from '@kbn/core-http-server'; import { unsecuredActionsClientMock } from '@kbn/actions-plugin/server/unsecured_actions_client/unsecured_actions_client.mock'; -import type { PluginStartContract } from '@kbn/actions-plugin/server'; +import type { PluginStartContract as ActionPluginStartContract } from '@kbn/actions-plugin/server'; import type { Mutable } from 'utility-types'; +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; +import type { ProductFeaturesService } from '../../lib/product_features_service'; import { responseActionsClientMock } from '../services/actions/clients/mocks'; import { getEndpointAuthzInitialStateMock } from '../../../common/endpoint/service/authz/mocks'; import { createMockConfig, requestContextMock } from '../../lib/detection_engine/routes/__mocks__'; @@ -66,15 +66,14 @@ import { parseExperimentalConfigValue, } from '../../../common/experimental_features'; import { requestContextFactoryMock } from '../../request_context_factory.mock'; -import { EndpointMetadataService } from '../services/metadata'; import type { SecuritySolutionRequestHandlerContextMock } from '../../lib/detection_engine/routes/__mocks__/request_context'; import { createMockClients } from '../../lib/detection_engine/routes/__mocks__/request_context'; import { createEndpointMetadataServiceTestContextMock } from '../services/metadata/mocks'; import type { EndpointAuthz } from '../../../common/endpoint/types/authz'; -import { EndpointFleetServicesFactory } from '../services/fleet'; import { createLicenseServiceMock } from '../../../common/license/mocks'; import { createFeatureUsageServiceMock } from '../services/feature_usage/mocks'; import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks'; + /** * Creates a mocked EndpointAppContext. */ @@ -143,6 +142,7 @@ export const createMockEndpointAppContextServiceSetupContract = securitySolutionRequestContextFactory: requestContextFactoryMock.create(), cloud: cloudMock.createSetup(), loggerFactory: loggingSystemMock.create(), + httpServiceSetup: coreMock.createSetup().http, }; }; @@ -150,38 +150,13 @@ export const createMockEndpointAppContextServiceSetupContract = * Creates a mocked input contract for the `EndpointAppContextService#start()` method */ export const createMockEndpointAppContextServiceStartContract = - (): jest.Mocked => { + (): DeeplyMockedKeys => { const config = createMockConfig(); const logger = loggingSystemMock.create().get('mock_endpoint_app_context'); - const savedObjectsStart = savedObjectsServiceMock.createStartContract(); - const security = securityServiceMock.createStart(); - const agentService = createMockAgentService(); - const agentPolicyService = createMockAgentPolicyService(); + const security = + securityServiceMock.createStart() as unknown as DeeplyMockedKeys; const packagePolicyService = createPackagePolicyServiceMock(); - const packageService = createMockPackageService(); - const endpointMetadataService = new EndpointMetadataService( - savedObjectsStart, - agentPolicyService, - packagePolicyService, - logger - ); - const endpointFleetServicesFactory = new EndpointFleetServicesFactory( - { - packageService, - packagePolicyService, - agentPolicyService, - agentService, - }, - savedObjectsStart - ); - const experimentalFeatures = config.experimentalFeatures; - const productFeaturesService = createProductFeaturesServiceMock( - undefined, - experimentalFeatures, - undefined, - logger - ); packagePolicyService.list.mockImplementation(async (_, options) => { return { @@ -197,47 +172,32 @@ export const createMockEndpointAppContextServiceStartContract = securityMock.createMockAuthenticatedUser({ roles: ['superuser'] }) ); - const casesMock = casesPluginMock.createStartContract(); - const fleetActionsClientMock = createFleetActionsClientMock(); - - return { - endpointMetadataService, - endpointFleetServicesFactory, - logger, - fleetAuthzService: createFleetAuthzServiceMock(), - createFleetFilesClient: { - fromHost: jest.fn((..._) => createFleetFromHostFilesClientMock()), - toHost: jest.fn((..._) => createFleetToHostFilesClientMock()), - }, - manifestManager: getManifestManagerMock(), + const startContract: DeeplyMockedKeys = { security, - alerting: alertsMock.createStart(), config, + productFeaturesService: createProductFeaturesServiceMock( + undefined, + config.experimentalFeatures, + undefined, + logger + ) as DeeplyMockedKeys, + experimentalFeatures: config.experimentalFeatures, + fleetStartServices: createFleetStartContractMock(), + cases: casesPluginMock.createStartContract(), + manifestManager: getManifestManagerMock() as DeeplyMockedKeys, + alerting: alertsMock.createStart(), licenseService: createLicenseServiceMock(), - registerIngestCallback: jest.fn< - ReturnType, - Parameters - >(), exceptionListsClient: listMock.getExceptionListClient(), - cases: casesMock, featureUsageService: createFeatureUsageServiceMock(), - experimentalFeatures, - messageSigningService: createMessageSigningServiceMock(), - createFleetActionsClient: jest.fn((_) => fleetActionsClientMock), esClient: elasticsearchClientMock.createElasticsearchClient(), - productFeaturesService, - savedObjectsClient: savedObjectsClientMock.create(), + savedObjectsServiceStart: savedObjectsServiceMock.createStartContract(), connectorActions: { getUnsecuredActionsClient: jest.fn().mockReturnValue(unsecuredActionsClientMock.create()), - } as unknown as jest.Mocked, + } as unknown as jest.Mocked, }; - }; -export const createFleetAuthzServiceMock = (): jest.Mocked => { - return { - fromRequest: jest.fn(async (_) => createFleetAuthzMock()), + return startContract; }; -}; export function createRouteHandlerContext( dataClient: ScopedClusterClientMock, diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index d5ffceb2ec7b7..7701db0fb8354 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -571,7 +571,6 @@ export class Plugin implements ISecuritySolutionPlugin { registerListsServerExtension: this.lists?.registerExtension, featureUsageService, experimentalFeatures: config.experimentalFeatures, - messageSigningService: plugins.fleet?.messageSigningService, esClient: core.elasticsearch.client.asInternalUser, productFeaturesService, savedObjectsServiceStart: core.savedObjects, From fe2644abcfbdcad877f6d814c66c040f62e39fe7 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Fri, 20 Sep 2024 12:40:19 -0400 Subject: [PATCH 10/24] Refactor EndpointFleetServicesFactory and remove SO client property and add savedObject client factory --- .../fleet/endpoint_fleet_services_factory.ts | 67 ++++++------------- .../api/get_all_integrations/route.ts | 2 +- .../api/get_installed_integrations/route.ts | 2 +- .../endpoint/validators/base_validator.ts | 6 +- 4 files changed, 26 insertions(+), 51 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts index c9c18c2329fca..27df7645b7fc2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { AgentClient, AgentPolicyServiceInterface, @@ -14,18 +13,35 @@ import type { PackageClient, } from '@kbn/fleet-plugin/server'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import { createInternalSoClient } from '../../utils/create_internal_so_client'; -import { createInternalReadonlySoClient } from '../../utils/create_internal_readonly_so_client'; import type { SavedObjectsClientFactory } from '../saved_objects'; +/** + * The set of Fleet services used by Endpoint + */ +export interface EndpointFleetServicesInterface { + agent: AgentClient; + agentPolicy: AgentPolicyServiceInterface; + packages: PackageClient; + packagePolicy: PackagePolicyClient; + /** The `kuery` that can be used to filter for Endpoint integration policies */ + endpointPolicyKuery: string; +} + +export interface EndpointInternalFleetServicesInterface extends EndpointFleetServicesInterface { + savedObjects: SavedObjectsClientFactory; +} + export interface EndpointFleetServicesFactoryInterface { asInternalUser(): EndpointInternalFleetServicesInterface; } +/** + * Provides centralized way to get all services for Fleet and access internal saved object clients + */ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactoryInterface { constructor( private readonly fleetDependencies: FleetStartContract, - public readonly savedObjects: SavedObjectsClientFactory + private readonly savedObjects: SavedObjectsClientFactory ) {} asInternalUser(): EndpointInternalFleetServicesInterface { @@ -36,9 +52,6 @@ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactor packageService, } = this.fleetDependencies; - let internalSoClient: SavedObjectsClientContract; - let internalReadonlySoClient: SavedObjectsClientContract; - return { agent: agentService.asInternalUser, agentPolicy, @@ -48,45 +61,7 @@ export class EndpointFleetServicesFactory implements EndpointFleetServicesFactor endpointPolicyKuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: "endpoint"`, - // FIXME:PT remove this property - get internalReadonlySoClient() { - if (!internalReadonlySoClient) { - internalReadonlySoClient = createInternalReadonlySoClient(this.savedObjectsStart); - } - - return internalReadonlySoClient; - }, - - // FIXME:PT remove this property - get internalSoClient() { - if (!internalSoClient) { - internalSoClient = createInternalSoClient(this.savedObjectsStart); - } - - return internalSoClient; - }, + savedObjects: this.savedObjects, }; } } - -/** - * The set of Fleet services used by Endpoint - */ -export interface EndpointFleetServicesInterface { - agent: AgentClient; - agentPolicy: AgentPolicyServiceInterface; - packages: PackageClient; - packagePolicy: PackagePolicyClient; - /** The `kuery` that can be used to filter for Endpoint integration policies */ - endpointPolicyKuery: string; -} - -export interface EndpointInternalFleetServicesInterface extends EndpointFleetServicesInterface { - /** - * An internal SO client (readonly) that can be used with the Fleet services that require it - */ - internalReadonlySoClient: SavedObjectsClientContract; - - /** Internal SO client. USE ONLY WHEN ABSOLUTELY NEEDED. Else, use the `internalReadonlySoClient` */ - internalSoClient: SavedObjectsClientContract; -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_all_integrations/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_all_integrations/route.ts index e5bae99052803..5b4eab27f71ab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_all_integrations/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_all_integrations/route.ts @@ -41,7 +41,7 @@ export const getAllIntegrationsRoute = (router: SecuritySolutionPluginRouter) => const [packages, packagePolicies] = await Promise.all([ fleet.packages.getPackages(), - fleet.packagePolicy.list(fleet.internalReadonlySoClient, {}), + fleet.packagePolicy.list(fleet.savedObjects.createInternalScopedSoClient(), {}), ]); // Elastic prebuilt rules is a special package and should be skipped const packagesWithoutPrebuiltSecurityRules = packages.filter( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_installed_integrations/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_installed_integrations/route.ts index 407c7d54adc52..3a3d159d1337f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_installed_integrations/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/fleet_integrations/api/get_installed_integrations/route.ts @@ -46,7 +46,7 @@ export const getInstalledIntegrationsRoute = (router: SecuritySolutionPluginRout }); const packagePolicies = await fleet.packagePolicy.list( - fleet.internalReadonlySoClient, + fleet.savedObjects.createInternalScopedSoClient(), {} ); packagePolicies.items.forEach((policy) => { diff --git a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.ts b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.ts index 4d92f5f2c9dd8..4ad1d3f699990 100644 --- a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.ts +++ b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.ts @@ -140,15 +140,15 @@ export class BaseValidator { */ protected async validateByPolicyItem(item: ExceptionItemLikeOptions): Promise { if (this.isItemByPolicy(item)) { - const { packagePolicy, internalReadonlySoClient } = - this.endpointAppContext.getInternalFleetServices(); + const { packagePolicy, savedObjects } = this.endpointAppContext.getInternalFleetServices(); const policyIds = getPolicyIdsFromArtifact(item); + const soClient = savedObjects.createInternalScopedSoClient(); if (policyIds.length === 0) { return; } - const policiesFromFleet = await packagePolicy.getByIDs(internalReadonlySoClient, policyIds, { + const policiesFromFleet = await packagePolicy.getByIDs(soClient, policyIds, { ignoreMissing: true, }); From d36cf2376bb3a32d53f33b7d3d824651153a0ba3 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Fri, 20 Sep 2024 15:33:39 -0400 Subject: [PATCH 11/24] Delete internal so client utilities (old) --- ...create_internal_readonly_so_client.test.ts | 53 ------------------ .../create_internal_readonly_so_client.ts | 55 ------------------- .../utils/create_internal_so_client.ts | 29 ---------- 3 files changed, 137 deletions(-) delete mode 100644 x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.test.ts delete mode 100644 x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts delete mode 100644 x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.test.ts deleted file mode 100644 index 56d5396954536..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { savedObjectsServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; -import type { SavedObjectsClientContract } from '@kbn/core/server'; -import { - createInternalReadonlySoClient, - InternalReadonlySoClientMethodNotAllowedError, -} from './create_internal_readonly_so_client'; - -describe('When using the `createInternalReadonlySoClient`', () => { - let realSoClientMock: ReturnType; - let readonlySoClient: ReturnType; - - beforeEach(() => { - const soStartContract = savedObjectsServiceMock.createStartContract(); - realSoClientMock = savedObjectsClientMock.create(); - soStartContract.getScopedClient.mockReturnValue(realSoClientMock); - readonlySoClient = createInternalReadonlySoClient(soStartContract); - }); - - it.each([ - 'get', - 'bulkGet', - 'checkConflicts', - 'collectMultiNamespaceReferences', - 'find', - 'resolve', - ])('should allow usage of %s() method', (methodName) => { - expect(() => readonlySoClient[methodName]).not.toThrow(); - }); - - it.each([ - 'bulkCreate', - 'bulkUpdate', - 'create', - 'createPointInTimeFinder', - 'delete', - 'removeReferencesTo', - 'openPointInTimeForType', - 'closePointInTime', - 'update', - 'updateObjectsSpaces', - ])('should throw if usage of %s() is attempted', (methodName) => { - expect(() => readonlySoClient[methodName]).toThrow( - InternalReadonlySoClientMethodNotAllowedError - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts deleted file mode 100644 index 11c2aabd301d9..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_readonly_so_client.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 type { SavedObjectsClientContract, SavedObjectsServiceStart } from '@kbn/core/server'; -import { createInternalSoClient } from './create_internal_so_client'; -import { EndpointError } from '../../../common/endpoint/errors'; - -type SavedObjectsClientContractKeys = keyof SavedObjectsClientContract; - -const RESTRICTED_METHODS: readonly SavedObjectsClientContractKeys[] = [ - 'bulkCreate', - 'bulkUpdate', - 'create', - 'createPointInTimeFinder', - 'delete', - 'removeReferencesTo', - 'openPointInTimeForType', - 'closePointInTime', - 'update', - 'updateObjectsSpaces', -]; - -export class InternalReadonlySoClientMethodNotAllowedError extends EndpointError {} - -// FIXME:PT find usages of createInternalReadonlySoClient() and delete them. Use EndpontAppContextService instead - -/** - * Creates an internal (system user) Saved Objects client (permissions turned off) that can only perform READ - * operations. - */ -export const createInternalReadonlySoClient = ( - savedObjectsServiceStart: SavedObjectsServiceStart -): SavedObjectsClientContract => { - const internalSoClient = createInternalSoClient(savedObjectsServiceStart); - - return new Proxy(internalSoClient, { - get( - target: SavedObjectsClientContract, - methodName: SavedObjectsClientContractKeys, - receiver: unknown - ): unknown { - if (RESTRICTED_METHODS.includes(methodName)) { - throw new InternalReadonlySoClientMethodNotAllowedError( - `Method [${methodName}] not allowed on internal readonly SO Client` - ); - } - - return Reflect.get(target, methodName, receiver); - }, - }) as SavedObjectsClientContract; -}; diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts b/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts deleted file mode 100644 index 1570715b59e54..0000000000000 --- a/x-pack/plugins/security_solution/server/endpoint/utils/create_internal_so_client.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; -import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; -import type { KibanaRequest, SavedObjectsClientContract } from '@kbn/core/server'; - -// FIXME:PT find usages of createInternalSoClient() and delete them. Use EndpontAppContextService instead - -export const createInternalSoClient = ( - savedObjectsServiceStart: SavedObjectsServiceStart -): SavedObjectsClientContract => { - const fakeRequest = { - headers: {}, - getBasePath: () => '', - path: '/', - route: { settings: {} }, - url: { href: {} }, - raw: { req: { url: '/' } }, - } as unknown as KibanaRequest; - - return savedObjectsServiceStart.getScopedClient(fakeRequest, { - excludedExtensions: [SECURITY_EXTENSION_ID], - }); -}; From a133744d9c3e31219c8ff7a9608c2139e0f209b7 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Mon, 23 Sep 2024 14:23:30 -0400 Subject: [PATCH 12/24] Refactor Metadata service - move all dependencies to constructor and simplify method call signatures --- .../server/endpoint/mocks/mocks.ts | 2 +- .../endpoint/routes/metadata/handlers.ts | 17 +-- .../endpoint/endpoint_actions_client.ts | 2 +- .../actions/pending_actions_summary.ts | 2 +- .../endpoint/services/actions/utils/utils.ts | 4 +- .../endpoint/endpoint_agent_status_client.ts | 15 +-- .../endpoint_fleet_services_factory.mocks.ts | 49 +++++++++ .../endpoint_metadata_service.test.ts | 54 ++++------ .../metadata/endpoint_metadata_service.ts | 65 ++++------- .../endpoint/services/metadata/mocks.ts | 101 ++++++++---------- .../factory/hosts/details/helpers.ts | 3 +- 11 files changed, 144 insertions(+), 170 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts index de21bf51c8175..6d4ef03c174bd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts @@ -97,7 +97,7 @@ export const createMockEndpointAppContext = ( export const createMockEndpointAppContextService = ( mockManifestManager?: ManifestManager ): jest.Mocked => { - const mockEndpointMetadataContext = createEndpointMetadataServiceTestContextMock(); + const mockEndpointMetadataContext = createEndpointMetadataServiceTestContextMock(); // FIXME:PT remove this const casesClientMock = createCasesClientMock(); const fleetFromHostFilesClientMock = createFleetFromHostFilesClientMock(); const fleetToHostFilesClientMock = createFleetToHostFilesClientMock(); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index 6641a148d49a1..90eb56fbc83f2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -46,17 +46,9 @@ export function getMetadataListRequestHandler( > { return async (context, request, response) => { const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService(); - const fleetServices = endpointAppContext.service.getInternalFleetServices(); - const esClient = (await context.core).elasticsearch.client.asInternalUser; - const soClient = (await context.core).savedObjects.client; try { - const { data, total } = await endpointMetadataService.getHostMetadataList( - esClient, - soClient, - fleetServices, - request.query - ); + const { data, total } = await endpointMetadataService.getHostMetadataList(request.query); const body: MetadataListResponse = { data, @@ -88,13 +80,8 @@ export const getMetadataRequestHandler = function ( const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService(); try { - const esClient = (await context.core).elasticsearch.client; return response.ok({ - body: await endpointMetadataService.getEnrichedHostMetadata( - esClient.asInternalUser, - endpointAppContext.service.getInternalFleetServices(), - request.params.id - ), + body: await endpointMetadataService.getEnrichedHostMetadata(request.params.id), }); } catch (error) { return errorHandler(logger, response, error); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts index df2b3c323ee08..0aeedb51bc512 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts @@ -73,7 +73,7 @@ export class EndpointActionsClient extends ResponseActionsClientImpl { const uniqueIds = [...new Set(ids)]; const foundEndpointHosts = await this.options.endpointService .getEndpointMetadataService() - .getMetadataForEndpoints(this.options.esClient, uniqueIds); + .getMetadataForEndpoints(uniqueIds); const validIds = foundEndpointHosts.map((endpoint: HostMetadata) => endpoint.elastic.agent.id); const invalidIds = ids.filter((id) => !validIds.includes(id)); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts index 543612bb5e39e..ba7c063fa2937 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts @@ -85,7 +85,7 @@ export const getPendingActionsSummary = async ( // If the metadata documents for all agents has not yet been retrieved, do it now if (!endpointMetadataLastUpdated) { endpointMetadataLastUpdated = ( - await metadataService.findHostMetadataForFleetAgents(esClient, agentIDs) + await metadataService.findHostMetadataForFleetAgents(agentIDs) ).reduce((acc, endpointMetadata) => { acc[endpointMetadata.elastic.agent.id] = new Date(endpointMetadata.event.created); return acc; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts index 9c9f8e5b62cac..42805e49f894f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts @@ -550,9 +550,7 @@ export const getAgentHostNamesWithIds = async ({ metadataService: EndpointMetadataService; }): Promise<{ [id: string]: string }> => { // get host metadata docs with queried agents - const metaDataDocs = await metadataService.findHostMetadataForFleetAgents(esClient, [ - ...new Set(agentIds), - ]); + const metaDataDocs = await metadataService.findHostMetadataForFleetAgents([...new Set(agentIds)]); // agent ids and names from metadata // map this into an object as {id1: name1, id2: name2} etc const agentsMetadataInfo = agentIds.reduce<{ [id: string]: string }>((acc, id) => { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts index c7b060833a64b..65486f8d1b38f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts @@ -23,16 +23,11 @@ export class EndpointAgentStatusClient extends AgentStatusClient { try { const agentIdsKql = agentIds.map((agentId) => `agent.id: ${agentId}`).join(' or '); const [{ data: hostInfoForAgents }, allPendingActions] = await Promise.all([ - metadataService.getHostMetadataList( - esClient, - soClient, - this.options.endpointService.getInternalFleetServices(), - { - page: 0, - pageSize: 1000, - kuery: agentIdsKql, - } - ), + metadataService.getHostMetadataList({ + page: 0, + pageSize: 1000, + kuery: agentIdsKql, + }), getPendingActionsSummary(esClient, metadataService, this.log, agentIds), ]).catch(catchAndWrapError); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts new file mode 100644 index 0000000000000..470521804b95c --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts @@ -0,0 +1,49 @@ +/* + * 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 type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; +import type { FleetStartContract } from '@kbn/fleet-plugin/server'; +import { createFleetStartContractMock } from '@kbn/fleet-plugin/server/mocks'; +import { coreMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; +import { SavedObjectsClientFactory } from '../saved_objects'; +import type { EndpointFleetServicesFactoryInterface } from './endpoint_fleet_services_factory'; +import { EndpointFleetServicesFactory } from './endpoint_fleet_services_factory'; + +interface EndpointFleetServicesFactoryInterfaceMocked + extends EndpointFleetServicesFactoryInterface { + asInternalUser: () => DeeplyMockedKeys< + ReturnType + >; +} + +interface CreateEndpointFleetServicesFactoryMockOptions { + fleetDependencies: DeeplyMockedKeys; + savedObjects: SavedObjectsClientFactory; +} + +export const createEndpointFleetServicesFactoryMock = ( + dependencies: Partial = {} +): { + service: EndpointFleetServicesFactoryInterfaceMocked; + dependencies: CreateEndpointFleetServicesFactoryMockOptions; +} => { + const { + fleetDependencies = createFleetStartContractMock(), + savedObjects = new SavedObjectsClientFactory( + savedObjectsServiceMock.createStartContract(), + coreMock.createSetup().http + ), + } = dependencies; + + return { + service: new EndpointFleetServicesFactory( + fleetDependencies, + savedObjects + ) as unknown as EndpointFleetServicesFactoryInterfaceMocked, + dependencies: { fleetDependencies, savedObjects }, + }; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts index 750e746761e7a..d69ea7e9924c2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts @@ -58,7 +58,7 @@ describe('EndpointMetadataService', () => { }); it('should call elasticsearch with proper filter', async () => { - await metadataService.findHostMetadataForFleetAgents(esClient, fleetAgentIds); + await metadataService.findHostMetadataForFleetAgents(fleetAgentIds); expect(esClient.search).toHaveBeenCalledWith( { ...getESQueryHostMetadataByFleetAgentIds(fleetAgentIds), size: fleetAgentIds.length }, { ignore: [404] } @@ -67,16 +67,13 @@ describe('EndpointMetadataService', () => { it('should throw a wrapped elasticsearch Error when one occurs', async () => { esClient.search.mockRejectedValue(new Error('foo bar')); - await expect( - metadataService.findHostMetadataForFleetAgents(esClient, fleetAgentIds) - ).rejects.toThrow(EndpointError); + await expect(metadataService.findHostMetadataForFleetAgents(fleetAgentIds)).rejects.toThrow( + EndpointError + ); }); it('should return an array of Host Metadata documents', async () => { - const response = await metadataService.findHostMetadataForFleetAgents( - esClient, - fleetAgentIds - ); + const response = await metadataService.findHostMetadataForFleetAgents(fleetAgentIds); expect(response).toEqual([endpointMetadataDoc]); }); }); @@ -91,17 +88,12 @@ describe('EndpointMetadataService', () => { it('should throw wrapped error if es error', async () => { esClient.search.mockRejectedValue({}); - const metadataListResponse = metadataService.getHostMetadataList( - esClient, - soClient, - testMockedContext.fleetServices, - { - page: 0, - pageSize: 10, - kuery: '', - hostStatuses: [], - } - ); + const metadataListResponse = metadataService.getHostMetadataList({ + page: 0, + pageSize: 10, + kuery: '', + hostStatuses: [], + }); await expect(metadataListResponse).rejects.toThrow(EndpointError); }); @@ -109,17 +101,12 @@ describe('EndpointMetadataService', () => { esClient.search.mockRejectedValue({ meta: { body: { error: { type: 'index_not_found_exception' } } }, }); - const metadataListResponse = await metadataService.getHostMetadataList( - esClient, - soClient, - testMockedContext.fleetServices, - { - page: 0, - pageSize: 10, - kuery: '', - hostStatuses: [], - } - ); + const metadataListResponse = await metadataService.getHostMetadataList({ + page: 0, + pageSize: 10, + kuery: '', + hostStatuses: [], + }); expect(metadataListResponse).toEqual({ data: [], @@ -174,12 +161,7 @@ describe('EndpointMetadataService', () => { ); const queryOptions = { page: 1, pageSize: 10, kuery: '', hostStatuses: [] }; - const metadataListResponse = await metadataService.getHostMetadataList( - esClient, - soClient, - testMockedContext.fleetServices, - queryOptions - ); + const metadataListResponse = await metadataService.getHostMetadataList(queryOptions); const unitedIndexQuery = await buildUnitedIndexQuery( soClient, queryOptions, diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts index 4346ecc98898e..3f3d756c70aab 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts @@ -70,14 +70,13 @@ export class EndpointMetadataService { * could be associated with a Fleet Agent that is no longer active. If wanting to ensure the * endpoint is associated with an active Fleet Agent, then use `getEnrichedHostMetadata()` instead * - * @param esClient Elasticsearch Client (usually scoped to the user's context) * @param endpointId the endpoint id (from `agent.id`) * * @throws */ - async getHostMetadata(esClient: ElasticsearchClient, endpointId: string): Promise { + async getHostMetadata(endpointId: string): Promise { const query = getESQueryHostMetadataByID(endpointId); - const queryResult = await esClient.search(query).catch(catchAndWrapError); + const queryResult = await this.esClient.search(query).catch(catchAndWrapError); const endpointMetadata = queryResponseToHostResult(queryResult).result; if (endpointMetadata) { @@ -89,19 +88,15 @@ export class EndpointMetadataService { /** * Find a list of Endpoint Host Metadata document associated with a given list of Fleet Agent Ids - * @param esClient * @param fleetAgentIds */ - async findHostMetadataForFleetAgents( - esClient: ElasticsearchClient, - fleetAgentIds: string[] - ): Promise { + async findHostMetadataForFleetAgents(fleetAgentIds: string[]): Promise { const query = getESQueryHostMetadataByFleetAgentIds(fleetAgentIds); // @ts-expect-error `size` not defined as top level property when using `typesWithBodyKey` query.size = fleetAgentIds.length; - const searchResult = await esClient + const searchResult = await this.esClient .search(query, { ignore: [404] }) .catch(catchAndWrapError); @@ -111,18 +106,12 @@ export class EndpointMetadataService { /** * Retrieve a single endpoint host metadata along with fleet information * - * @param esClient Elasticsearch Client (usually scoped to the user's context) - * @param fleetServices * @param endpointId the endpoint id (from `agent.id`) * * @throws */ - async getEnrichedHostMetadata( - esClient: ElasticsearchClient, - fleetServices: EndpointFleetServicesInterface, - endpointId: string - ): Promise { - const endpointMetadata = await this.getHostMetadata(esClient, endpointId); + async getEnrichedHostMetadata(endpointId: string): Promise { + const endpointMetadata = await this.getHostMetadata(endpointId); let fleetAgentId = endpointMetadata.elastic.agent.id; let fleetAgent: Agent | undefined; @@ -134,7 +123,7 @@ export class EndpointMetadataService { this.logger?.warn(`Missing elastic agent id, using host id instead ${fleetAgentId}`); } - fleetAgent = await this.getFleetAgent(fleetServices.agent, fleetAgentId); + fleetAgent = await this.getFleetAgent(fleetAgentId); } catch (error) { if (error instanceof FleetAgentNotFoundError) { this.logger?.debug(`agent with id ${fleetAgentId} not found`); @@ -150,12 +139,12 @@ export class EndpointMetadataService { ); } - return this.enrichHostMetadata(fleetServices, endpointMetadata, fleetAgent); + return this.enrichHostMetadata(endpointMetadata, fleetAgent); } /** * Enriches a host metadata document with data from fleet - * @param fleetServices + * * @param endpointMetadata * @param _fleetAgent * @param _fleetAgentPolicy @@ -164,7 +153,6 @@ export class EndpointMetadataService { */ // eslint-disable-next-line complexity private async enrichHostMetadata( - fleetServices: EndpointFleetServicesInterface, endpointMetadata: HostMetadata, /** * If undefined, it will be retrieved from Fleet using the ID in the endpointMetadata. @@ -200,7 +188,7 @@ export class EndpointMetadataService { ); } - fleetAgent = await this.getFleetAgent(fleetServices.agent, fleetAgentId); + fleetAgent = await this.getFleetAgent(fleetAgentId); } catch (error) { if (error instanceof FleetAgentNotFoundError) { this.logger?.warn(`Agent with id ${fleetAgentId} not found`); @@ -269,15 +257,11 @@ export class EndpointMetadataService { /** * Retrieve a single Fleet Agent data * - * @param fleetAgentService * @param agentId The elastic agent id (`from `elastic.agent.id`) */ - async getFleetAgent( - fleetAgentService: EndpointFleetServicesInterface['agent'], - agentId: string - ): Promise { + async getFleetAgent(agentId: string): Promise { try { - return await fleetAgentService.getAgent(agentId); + return await this.fleetServices.agent.getAgent(agentId); } catch (error) { if (error instanceof AgentNotFoundError) { throw new FleetAgentNotFoundError(`agent with id ${agentId} not found`, error); @@ -330,27 +314,25 @@ export class EndpointMetadataService { /** * Retrieve list of host metadata. Only supports new united index. * - * @param esClient * @param queryOptions - * @param soClient - * @param fleetServices * * @throws */ async getHostMetadataList( - esClient: ElasticsearchClient, - soClient: SavedObjectsClientContract, - fleetServices: EndpointFleetServicesInterface, queryOptions: GetMetadataListRequestQuery ): Promise> { const endpointPolicies = await this.getAllEndpointPackagePolicies(); const endpointPolicyIds = uniq(endpointPolicies.flatMap((policy) => policy.policy_ids)); - const unitedIndexQuery = await buildUnitedIndexQuery(soClient, queryOptions, endpointPolicyIds); + const unitedIndexQuery = await buildUnitedIndexQuery( + this.soClient, + queryOptions, + endpointPolicyIds + ); let unitedMetadataQueryResponse: SearchResponse; try { - unitedMetadataQueryResponse = await esClient.search( + unitedMetadataQueryResponse = await this.esClient.search( unitedIndexQuery ); } catch (error) { @@ -417,9 +399,7 @@ export class EndpointMetadataService { ...runtimeFields, }; - hosts.push( - await this.enrichHostMetadata(fleetServices, metadata, agent, agentPolicy, endpointPolicy) - ); + hosts.push(await this.enrichHostMetadata(metadata, agent, agentPolicy, endpointPolicy)); } } @@ -433,12 +413,9 @@ export class EndpointMetadataService { return getAllEndpointPackagePolicies(this.fleetServices.packagePolicy, this.soClient); } - async getMetadataForEndpoints( - esClient: ElasticsearchClient, - endpointIDs: string[] - ): Promise { + async getMetadataForEndpoints(endpointIDs: string[]): Promise { const query = getESQueryHostMetadataByIDs(endpointIDs); - const { body } = await esClient.search(query, { + const { body } = await this.esClient.search(query, { meta: true, }); const hosts = queryResponseToHostListResult(body); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts index ac97b51265d4e..470ec5893d896 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts @@ -6,30 +6,14 @@ */ import type { SavedObjectsServiceStart } from '@kbn/core/server'; -import { loggingSystemMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; -import { - createMockAgentPolicyService, - createMockAgentService, - createMockPackageService, - createPackagePolicyServiceMock, -} from '@kbn/fleet-plugin/server/mocks'; +import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import type { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks'; import type { AgentPolicyServiceInterface, AgentService } from '@kbn/fleet-plugin/server'; +import { createEndpointFleetServicesFactoryMock } from '../fleet/endpoint_fleet_services_factory.mocks'; +import { createMockEndpointAppContextServiceStartContract } from '../../mocks'; import { EndpointMetadataService } from './endpoint_metadata_service'; import type { EndpointInternalFleetServicesInterface } from '../fleet/endpoint_fleet_services_factory'; -import { EndpointFleetServicesFactory } from '../fleet/endpoint_fleet_services_factory'; - -const createCustomizedPackagePolicyService = () => { - const service = createPackagePolicyServiceMock(); - service.list.mockImplementation(async (_, options) => { - return { - items: [], - total: 0, - page: options.page ?? 1, - perPage: options.perPage ?? 10, - }; - }); - return service; -}; +import { SavedObjectsClientFactory } from '../saved_objects'; /** * Endpoint Metadata Service test context. Includes an instance of `EndpointMetadataService` along with the @@ -45,42 +29,45 @@ export interface EndpointMetadataServiceTestContextMock { logger: ReturnType['get']>; } -export const createEndpointMetadataServiceTestContextMock = ( - savedObjectsStart: jest.Mocked = savedObjectsServiceMock.createStartContract(), - agentService: jest.Mocked = createMockAgentService(), - agentPolicyService: jest.Mocked = createMockAgentPolicyService(), - packagePolicyService: ReturnType< - typeof createPackagePolicyServiceMock - > = createCustomizedPackagePolicyService(), - packageService: ReturnType = createMockPackageService(), - logger: ReturnType['get']> = loggingSystemMock - .create() - .get() -): EndpointMetadataServiceTestContextMock => { - const fleetServices = new EndpointFleetServicesFactory( - { - agentService, - packageService, - packagePolicyService, - agentPolicyService, - }, - savedObjectsStart - ).asInternalUser(); +export const createEndpointMetadataServiceTestContextMock = + (): EndpointMetadataServiceTestContextMock => { + const logger = loggingSystemMock.create().get(); + const { esClient, fleetStartServices, savedObjectsServiceStart } = + createMockEndpointAppContextServiceStartContract(); + const savedObjectsServiceFactory = new SavedObjectsClientFactory( + savedObjectsServiceStart, + coreMock.createSetup().http + ); + const fleetServices = createEndpointFleetServicesFactoryMock({ + fleetDependencies: fleetStartServices, + savedObjects: savedObjectsServiceFactory, + }).service.asInternalUser(); + const endpointMetadataService = new EndpointMetadataService( + esClient, + savedObjectsServiceFactory.createInternalScopedSoClient(undefined, false), + fleetServices, + logger + ); - const endpointMetadataService = new EndpointMetadataService( - savedObjectsStart, - agentPolicyService, - packagePolicyService, - logger - ); + fleetServices.packagePolicy.list.mockImplementation(async (_, options) => { + return { + items: [], + total: 0, + page: options.page ?? 1, + perPage: options.perPage ?? 10, + }; + }); - return { - savedObjectsStart, - agentService, - agentPolicyService, - packagePolicyService, - endpointMetadataService, - fleetServices, - logger, + return { + savedObjectsStart: savedObjectsServiceStart, + agentService: { + asInternalUser: fleetServices.agent, + asScoped: jest.fn().mockReturnValue(fleetServices.agent), + }, + agentPolicyService: fleetServices.agentPolicy, + packagePolicyService: fleetServices.packagePolicy, + logger, + endpointMetadataService, + fleetServices, + }; }; -}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts index 4472fa3c0d691..94d45b2b63e0e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts @@ -170,14 +170,13 @@ export const getHostEndpoint = async ( const logger = endpointContext.logFactory.get('metadata'); try { - const fleetServices = endpointContext.service.getInternalFleetServices(); const endpointMetadataService = endpointContext.service.getEndpointMetadataService(); const endpointData = await endpointMetadataService // Using `internalUser` ES client below due to the fact that Fleet data has been moved to // system indices (`.fleet*`). Because this is a readonly action, this should be ok to do // here until proper RBOC controls are implemented - .getEnrichedHostMetadata(esClient.asInternalUser, fleetServices, id); + .getEnrichedHostMetadata(id); const fleetAgentId = endpointData.metadata.elastic.agent.id; From 75ba84f8b609377976f16e7ede2045a2d6cd43a1 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Mon, 23 Sep 2024 15:02:23 -0400 Subject: [PATCH 13/24] Improve mocks --- .../server/endpoint/mocks/mocks.ts | 17 +++++++--- .../endpoint_fleet_services_factory.mocks.ts | 9 ++--- .../endpoint_metadata_service.test.ts | 28 +++++++-------- .../saved_objects_client_factory.mocks.ts | 34 +++++++++++++++++++ 4 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.mocks.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts index 6d4ef03c174bd..f60192a32796b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts @@ -49,6 +49,8 @@ import { unsecuredActionsClientMock } from '@kbn/actions-plugin/server/unsecured import type { PluginStartContract as ActionPluginStartContract } from '@kbn/actions-plugin/server'; import type { Mutable } from 'utility-types'; import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; +import { EndpointMetadataService } from '../services/metadata'; +import { createEndpointFleetServicesFactoryMock } from '../services/fleet/endpoint_fleet_services_factory.mocks'; import type { ProductFeaturesService } from '../../lib/product_features_service'; import { responseActionsClientMock } from '../services/actions/clients/mocks'; import { getEndpointAuthzInitialStateMock } from '../../../common/endpoint/service/authz/mocks'; @@ -68,7 +70,6 @@ import { import { requestContextFactoryMock } from '../../request_context_factory.mock'; import type { SecuritySolutionRequestHandlerContextMock } from '../../lib/detection_engine/routes/__mocks__/request_context'; import { createMockClients } from '../../lib/detection_engine/routes/__mocks__/request_context'; -import { createEndpointMetadataServiceTestContextMock } from '../services/metadata/mocks'; import type { EndpointAuthz } from '../../../common/endpoint/types/authz'; import { createLicenseServiceMock } from '../../../common/license/mocks'; import { createFeatureUsageServiceMock } from '../services/feature_usage/mocks'; @@ -97,7 +98,15 @@ export const createMockEndpointAppContext = ( export const createMockEndpointAppContextService = ( mockManifestManager?: ManifestManager ): jest.Mocked => { - const mockEndpointMetadataContext = createEndpointMetadataServiceTestContextMock(); // FIXME:PT remove this + const { esClient, fleetStartServices } = createMockEndpointAppContextServiceStartContract(); + const fleetServices = createEndpointFleetServicesFactoryMock({ + fleetDependencies: fleetStartServices, + }).service.asInternalUser(); + const endpointMetadataService = new EndpointMetadataService( + esClient, + savedObjectsClientMock.create(), + fleetServices + ); const casesClientMock = createCasesClientMock(); const fleetFromHostFilesClientMock = createFleetFromHostFilesClientMock(); const fleetToHostFilesClientMock = createFleetToHostFilesClientMock(); @@ -115,8 +124,8 @@ export const createMockEndpointAppContextService = ( }, createLogger: jest.fn((...parts) => loggerFactory.get(...parts)), getManifestManager: jest.fn().mockReturnValue(mockManifestManager ?? jest.fn()), - getEndpointMetadataService: jest.fn(() => mockEndpointMetadataContext.endpointMetadataService), - getInternalFleetServices: jest.fn(() => mockEndpointMetadataContext.fleetServices), + getEndpointMetadataService: jest.fn(() => endpointMetadataService), + getInternalFleetServices: jest.fn(() => fleetServices), getEndpointAuthz: jest.fn(async (_) => getEndpointAuthzInitialStateMock()), getCasesClient: jest.fn().mockReturnValue(casesClientMock), getFleetFromHostFilesClient: jest.fn(async () => fleetFromHostFilesClientMock), diff --git a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts index 470521804b95c..1e37993c95501 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/fleet/endpoint_fleet_services_factory.mocks.ts @@ -8,10 +8,10 @@ import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import type { FleetStartContract } from '@kbn/fleet-plugin/server'; import { createFleetStartContractMock } from '@kbn/fleet-plugin/server/mocks'; -import { coreMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; -import { SavedObjectsClientFactory } from '../saved_objects'; +import type { SavedObjectsClientFactory } from '../saved_objects'; import type { EndpointFleetServicesFactoryInterface } from './endpoint_fleet_services_factory'; import { EndpointFleetServicesFactory } from './endpoint_fleet_services_factory'; +import { createSavedObjectsClientFactoryMock } from '../saved_objects/saved_objects_client_factory.mocks'; interface EndpointFleetServicesFactoryInterfaceMocked extends EndpointFleetServicesFactoryInterface { @@ -33,10 +33,7 @@ export const createEndpointFleetServicesFactoryMock = ( } => { const { fleetDependencies = createFleetStartContractMock(), - savedObjects = new SavedObjectsClientFactory( - savedObjectsServiceMock.createStartContract(), - coreMock.createSetup().http - ), + savedObjects = createSavedObjectsClientFactoryMock().service, } = dependencies; return { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts index d69ea7e9924c2..5fa1c981073f2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts @@ -143,22 +143,20 @@ describe('EndpointMetadataService', () => { const mockDoc = unitedMetadataSearchResponseMock(endpointMetadataDoc, mockAgent); esClient.search.mockResponse(mockDoc); agentPolicyServiceMock.getByIds.mockResolvedValue(agentPolicies); - testMockedContext.packagePolicyService.list.mockImplementation( - async (_, { page, perPage }) => { - const response = { - items: packagePolicies, - page: page ?? 1, - total: packagePolicies.length, - perPage: packagePolicies.length, - }; - - if ((page ?? 1) > 1) { - response.items = []; - } - - return response; + testMockedContext.packagePolicyService.list.mockImplementation(async (_, { page }) => { + const response = { + items: packagePolicies, + page: page ?? 1, + total: packagePolicies.length, + perPage: packagePolicies.length, + }; + + if ((page ?? 1) > 1) { + response.items = []; } - ); + + return response; + }); const queryOptions = { page: 1, pageSize: 10, kuery: '', hostStatuses: [] }; const metadataListResponse = await metadataService.getHostMetadataList(queryOptions); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.mocks.ts new file mode 100644 index 0000000000000..45adca06b2443 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.mocks.ts @@ -0,0 +1,34 @@ +/* + * 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 type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; +import type { HttpServiceSetup } from '@kbn/core/server'; +import { savedObjectsServiceMock } from '@kbn/core-saved-objects-server-mocks'; +import { coreMock } from '@kbn/core/server/mocks'; +import { SavedObjectsClientFactory } from './saved_objects_client_factory'; + +interface CreateSavedObjectsClientFactoryMockOptions { + savedObjectsServiceStart: jest.Mocked; + httpServiceSetup: HttpServiceSetup; +} + +export const createSavedObjectsClientFactoryMock = ( + dependencies: Partial = {} +): { + service: SavedObjectsClientFactory; + dependencies: CreateSavedObjectsClientFactoryMockOptions; +} => { + const { + savedObjectsServiceStart = savedObjectsServiceMock.createStartContract(), + httpServiceSetup = coreMock.createSetup().http, + } = dependencies; + + return { + service: new SavedObjectsClientFactory(savedObjectsServiceStart, httpServiceSetup), + dependencies: { savedObjectsServiceStart, httpServiceSetup }, + }; +}; From 096ef90ff08b01cb9c6a7a39fb0e64ed28c893bf Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 23 Sep 2024 19:23:19 +0000 Subject: [PATCH 14/24] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/security_solution/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index e33ecb852b5b1..2b84a3a336397 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -226,5 +226,6 @@ "@kbn/entityManager-plugin", "@kbn/entities-schema", "@kbn/inference-plugin", + "@kbn/core-saved-objects-server-mocks", ] } From f7c796bf69b8eeb132734c6cf866b8e43b9ab073 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Tue, 24 Sep 2024 15:52:58 -0400 Subject: [PATCH 15/24] Fix fleet mock --- x-pack/plugins/fleet/server/mocks/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 282aa5cd7f8cf..17f85cd252c2b 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -283,6 +283,7 @@ export function createUninstallTokenServiceMock(): DeeplyMockedKeys => { const fleetAuthzMock = createFleetAuthzMock(); const fleetArtifactsClient = createArtifactsClientMock(); + const fleetActionsClient = createFleetActionsClientMock(); const startContract: DeeplyMockedKeys = { fleetSetupCompleted: jest.fn(async () => {}), @@ -296,7 +297,7 @@ export const createFleetStartContractMock = (): DeeplyMockedKeys createFleetActionsClientMock()), + createFleetActionsClient: jest.fn((_) => fleetActionsClient), getPackageSpecTagId: jest.fn(getPackageSpecTagId), }; From 96f9149e1fc2a30cec0014c16a2888d949cf2244 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Tue, 24 Sep 2024 16:38:57 -0400 Subject: [PATCH 16/24] Fix several tests and mocks --- .../turn_off_agent_policy_features.test.ts | 18 ++++++++++++------ .../turn_off_agent_policy_features.ts | 3 ++- .../turn_off_policy_protections.test.ts | 15 +++++++++------ .../migrations/turn_off_policy_protections.ts | 3 ++- .../routes/actions/response_actions.test.ts | 4 +++- .../endpoint/routes/metadata/metadata.test.ts | 11 +++++------ .../endpoint/services/actions/clients/mocks.ts | 5 ++++- .../saved_objects_client_factory.mocks.ts | 9 ++++++++- .../saved_objects_client_factory.ts | 3 ++- 9 files changed, 47 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.test.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.test.ts index 6279870f92752..b031a7882bf3a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { createMockEndpointAppContextServiceStartContract } from '../mocks'; import type { Logger } from '@kbn/logging'; import type { EndpointInternalFleetServicesInterface } from '../services/fleet'; @@ -13,6 +12,9 @@ import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys'; import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service'; import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks'; import { turnOffAgentPolicyFeatures } from './turn_off_agent_policy_features'; +import { createEndpointFleetServicesFactoryMock } from '../services/fleet/endpoint_fleet_services_factory.mocks'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { allowedExperimentalValues } from '../../../common'; describe('Turn Off Agent Policy Features Migration', () => { let fleetServices: EndpointInternalFleetServicesInterface; @@ -23,12 +25,16 @@ describe('Turn Off Agent Policy Features Migration', () => { turnOffAgentPolicyFeatures(fleetServices, productFeatureService, logger); beforeEach(() => { - const endpointContextStartContract = createMockEndpointAppContextServiceStartContract(); + const mockedFleetServices = createEndpointFleetServicesFactoryMock(); - ({ logger } = endpointContextStartContract); - - productFeatureService = endpointContextStartContract.productFeaturesService; - fleetServices = endpointContextStartContract.endpointFleetServicesFactory.asInternalUser(); + fleetServices = mockedFleetServices.service.asInternalUser(); + logger = loggingSystemMock.createLogger(); + productFeatureService = createProductFeaturesServiceMock( + undefined, + allowedExperimentalValues, + undefined, + logger + ); }); describe('and `agentTamperProtection` is enabled', () => { diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts index cfb520719172d..d55cfcfbe4c1d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts @@ -29,7 +29,8 @@ export const turnOffAgentPolicyFeatures = async ( `App feature [${ProductFeatureSecurityKey.endpointAgentTamperProtection}] is disabled. Checking fleet agent policies for compliance` ); - const { agentPolicy: agentPolicyService, internalSoClient } = fleetServices; + const { agentPolicy: agentPolicyService, savedObjects } = fleetServices; + const internalSoClient = savedObjects.createInternalScopedSoClient(undefined, false); const { updatedPolicies, failedPolicies } = await agentPolicyService.turnOffAgentTamperProtections(internalSoClient); diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts index a87d16b3b7f5b..8f84ca9f1bb17 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts @@ -19,6 +19,8 @@ import type { PromiseResolvedValue } from '../../../common/endpoint/types/utilit import { ensureOnlyEventCollectionIsAllowed } from '../../../common/endpoint/models/policy_config_helpers'; import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service'; import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { createEndpointFleetServicesFactoryMock } from '../services/fleet/endpoint_fleet_services_factory.mocks'; describe('Turn Off Policy Protections Migration', () => { let esClient: ElasticsearchClient; @@ -58,10 +60,11 @@ describe('Turn Off Policy Protections Migration', () => { beforeEach(() => { const endpointContextStartContract = createMockEndpointAppContextServiceStartContract(); - ({ esClient, logger } = endpointContextStartContract); - + logger = loggingSystemMock.createLogger(); + ({ esClient } = endpointContextStartContract); productFeatureService = endpointContextStartContract.productFeaturesService; - fleetServices = endpointContextStartContract.endpointFleetServicesFactory.asInternalUser(); + + fleetServices = createEndpointFleetServicesFactoryMock().service.asInternalUser(); }); describe('and both `endpointPolicyProtections` and `endpointProtectionUpdates` is enabled', () => { @@ -139,7 +142,7 @@ describe('Turn Off Policy Protections Migration', () => { expect(fleetServices.packagePolicy.list as jest.Mock).toHaveBeenCalledTimes(2); expect(fleetServices.packagePolicy.bulkUpdate as jest.Mock).toHaveBeenCalledWith( - fleetServices.internalSoClient, + fleetServices.savedObjects.createInternalScopedSoClient(undefined, false), esClient, [ expect.objectContaining({ id: bulkUpdateResponse.updatedPolicies![0].id }), @@ -210,7 +213,7 @@ describe('Turn Off Policy Protections Migration', () => { expect(fleetServices.packagePolicy.list as jest.Mock).toHaveBeenCalledTimes(2); expect(fleetServices.packagePolicy.bulkUpdate as jest.Mock).toHaveBeenCalledWith( - fleetServices.internalSoClient, + fleetServices.savedObjects.createInternalScopedSoClient(undefined, false), esClient, [ expect.objectContaining({ id: bulkUpdateResponse.updatedPolicies![0].id }), @@ -317,7 +320,7 @@ describe('Turn Off Policy Protections Migration', () => { expect(fleetServices.packagePolicy.list as jest.Mock).toHaveBeenCalledTimes(2); expect(fleetServices.packagePolicy.bulkUpdate as jest.Mock).toHaveBeenCalledWith( - fleetServices.internalSoClient, + fleetServices.savedObjects.createInternalUnscopedSoClient(), esClient, [ expect.objectContaining({ id: bulkUpdateResponse.updatedPolicies![0].id }), diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts index fa93e8c0b62f6..9b27ffc9b3947 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts @@ -62,7 +62,8 @@ export const turnOffPolicyProtectionsIfNotSupported = async ( ); } - const { packagePolicy, internalSoClient, endpointPolicyKuery } = fleetServices; + const { packagePolicy, savedObjects, endpointPolicyKuery } = fleetServices; + const internalSoClient = savedObjects.createInternalScopedSoClient(undefined, false); const updates: UpdatePackagePolicy[] = []; const messages: string[] = []; const perPage = 1000; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 669d3770be37d..8468cf84ee271 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -142,7 +142,9 @@ describe('Response actions', () => { const routerMock = httpServiceMock.createRouter(); mockResponse = httpServerMock.createResponseFactory(); const startContract = createMockEndpointAppContextServiceStartContract(); - (startContract.messageSigningService?.sign as jest.Mock).mockImplementation(() => { + ( + startContract.fleetStartServices.messageSigningService?.sign as jest.Mock + ).mockImplementation(() => { return { data: 'thisisthedata', signature: 'thisisasignature', diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index f1061db4e8c59..3da6ef8757fa0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -94,8 +94,7 @@ describe('test endpoint routes', () => { startContract = createMockEndpointAppContextServiceStartContract(); ( - startContract.endpointFleetServicesFactory.asInternalUser() - .packagePolicy as jest.Mocked + startContract.fleetStartServices.packagePolicyService as jest.Mocked ).list.mockImplementation(() => { return Promise.resolve({ items: [], @@ -108,10 +107,10 @@ describe('test endpoint routes', () => { endpointAppContextService = new EndpointAppContextService(); endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...startContract }); - mockAgentClient = startContract.endpointFleetServicesFactory.asInternalUser() - .agent as jest.Mocked; - mockAgentPolicyService = startContract.endpointFleetServicesFactory.asInternalUser() - .agentPolicy as jest.Mocked; + mockAgentClient = startContract.fleetStartServices.agentService + .asInternalUser as jest.Mocked; + mockAgentPolicyService = startContract.fleetStartServices + .agentPolicyService as jest.Mocked; registerEndpointRoutes(routerMock, { ...createMockEndpointAppContext(), diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts index a721bda2f38ab..69901033eaafd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts @@ -106,7 +106,10 @@ const createConstructorOptionsMock = (): Required Date: Wed, 25 Sep 2024 08:48:35 -0400 Subject: [PATCH 17/24] Move fixes to tests and mocks due to refactor --- .../server/endpoint/routes/actions/response_actions.test.ts | 1 + .../server/endpoint/routes/metadata/metadata.test.ts | 5 ++++- .../services/metadata/endpoint_metadata_service.test.ts | 5 ++--- .../server/endpoint/services/metadata/mocks.ts | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 8468cf84ee271..66b804e07eb10 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -165,6 +165,7 @@ describe('Response actions', () => { endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...startContract, + esClient: mockScopedClient.asInternalUser, licenseService, }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 3da6ef8757fa0..63d3c466dd2b6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -106,7 +106,10 @@ describe('test endpoint routes', () => { endpointAppContextService = new EndpointAppContextService(); endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); - endpointAppContextService.start({ ...startContract }); + endpointAppContextService.start({ + ...startContract, + esClient: mockScopedClient.asInternalUser, + }); mockAgentClient = startContract.fleetStartServices.agentService .asInternalUser as jest.Mocked; mockAgentPolicyService = startContract.fleetStartServices diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts index 5fa1c981073f2..2fe173ff55eb5 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts @@ -7,7 +7,7 @@ import { uniq } from 'lodash'; import type { EndpointMetadataServiceTestContextMock } from './mocks'; import { createEndpointMetadataServiceTestContextMock } from './mocks'; -import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { legacyMetadataSearchResponseMock, @@ -37,7 +37,7 @@ describe('EndpointMetadataService', () => { endpointDocGenerator = new EndpointDocGenerator('seed'); testMockedContext = createEndpointMetadataServiceTestContextMock(); metadataService = testMockedContext.endpointMetadataService; - esClient = elasticsearchServiceMock.createScopedClusterClient().asInternalUser; + esClient = testMockedContext.esClient; soClient = savedObjectsClientMock.create(); soClient.find = jest.fn().mockResolvedValue({ saved_objects: [] }); fleetAppContextService.start( @@ -83,7 +83,6 @@ describe('EndpointMetadataService', () => { beforeEach(() => { agentPolicyServiceMock = testMockedContext.agentPolicyService; - esClient = elasticsearchServiceMock.createScopedClusterClient().asInternalUser; }); it('should throw wrapped error if es error', async () => { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts index 470ec5893d896..4013cf1a507e5 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts @@ -6,7 +6,7 @@ */ import type { SavedObjectsServiceStart } from '@kbn/core/server'; -import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { coreMock, type ElasticsearchClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import type { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks'; import type { AgentPolicyServiceInterface, AgentService } from '@kbn/fleet-plugin/server'; import { createEndpointFleetServicesFactoryMock } from '../fleet/endpoint_fleet_services_factory.mocks'; @@ -27,6 +27,7 @@ export interface EndpointMetadataServiceTestContextMock { endpointMetadataService: EndpointMetadataService; fleetServices: EndpointInternalFleetServicesInterface; logger: ReturnType['get']>; + esClient: ElasticsearchClientMock; } export const createEndpointMetadataServiceTestContextMock = @@ -69,5 +70,6 @@ export const createEndpointMetadataServiceTestContextMock = logger, endpointMetadataService, fleetServices, + esClient: esClient as ElasticsearchClientMock, }; }; From afc21ef59a4698d4da4fc7248130c4a7c881681b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:09:11 +0000 Subject: [PATCH 18/24] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/security_solution/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 2b84a3a336397..0817d3f71f625 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -227,5 +227,6 @@ "@kbn/entities-schema", "@kbn/inference-plugin", "@kbn/core-saved-objects-server-mocks", + "@kbn/core-http-router-server-internal", ] } From 2a55e44236f6bed31ac14cbcf2543b773cb9b882 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 25 Sep 2024 11:25:44 -0400 Subject: [PATCH 19/24] Fix types in `cloud_defend`, `cloud_security_posture` and `observability_solution` plugins --- x-pack/plugins/cloud_defend/server/plugin.test.ts | 4 +--- x-pack/plugins/cloud_security_posture/server/plugin.test.ts | 4 +--- .../routes/source_maps/schedule_source_map_migration.ts | 6 +++--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/cloud_defend/server/plugin.test.ts b/x-pack/plugins/cloud_defend/server/plugin.test.ts index 8f2b7fd9998fc..93be8d2062680 100644 --- a/x-pack/plugins/cloud_defend/server/plugin.test.ts +++ b/x-pack/plugins/cloud_defend/server/plugin.test.ts @@ -55,15 +55,13 @@ const createMockFleetStartContract = (): DeeplyMockedKeys => fromRequest: jest.fn(async (_) => createFleetAuthzMock()), }, fleetSetupCompleted: jest.fn().mockResolvedValue(undefined), - // @ts-expect-error 2322 agentService: createMockAgentService(), - // @ts-expect-error 2322 packageService: createMockPackageService(), agentPolicyService: createMockAgentPolicyService(), registerExternalCallback: jest.fn((..._: ExternalCallback) => {}), packagePolicyService: createPackagePolicyServiceMock(), createArtifactsClient: jest.fn().mockReturnValue(createArtifactsClientMock()), - }; + } as unknown as DeeplyMockedKeys; }; describe('Cloud Defend Plugin', () => { diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts index 9707bc9864909..bac14b84139f0 100644 --- a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts @@ -62,15 +62,13 @@ const createMockFleetStartContract = (): DeeplyMockedKeys => fromRequest: jest.fn(async (_) => createFleetAuthzMock()), }, fleetSetupCompleted: jest.fn().mockResolvedValue(undefined), - // @ts-expect-error 2322 agentService: createMockAgentService(), - // @ts-expect-error 2322 packageService: createMockPackageService(), agentPolicyService: createMockAgentPolicyService(), registerExternalCallback: jest.fn((..._: ExternalCallback) => {}), packagePolicyService: createPackagePolicyServiceMock(), createArtifactsClient: jest.fn().mockReturnValue(createArtifactsClientMock()), - }; + } as unknown as DeeplyMockedKeys; }; describe('Cloud Security Posture Plugin', () => { diff --git a/x-pack/plugins/observability_solution/apm/server/routes/source_maps/schedule_source_map_migration.ts b/x-pack/plugins/observability_solution/apm/server/routes/source_maps/schedule_source_map_migration.ts index b581a30665a21..2fbfc65fcce03 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/source_maps/schedule_source_map_migration.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/source_maps/schedule_source_map_migration.ts @@ -7,7 +7,7 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { FleetStartContract } from '@kbn/fleet-plugin/server'; -import { FleetArtifactsClient } from '@kbn/fleet-plugin/server/services'; +import { ArtifactsClientInterface } from '@kbn/fleet-plugin/server/services'; import { TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; import { CoreStart, Logger } from '@kbn/core/server'; import { getApmArtifactClient } from '../fleet/source_maps'; @@ -141,7 +141,7 @@ async function getArtifactsForPage({ kuery, }: { page: number; - apmArtifactClient: FleetArtifactsClient; + apmArtifactClient: ArtifactsClientInterface; kuery: string; }) { return await apmArtifactClient.listArtifacts({ @@ -163,7 +163,7 @@ async function paginateArtifacts({ }: { taskState?: TaskState; page: number; - apmArtifactClient: FleetArtifactsClient; + apmArtifactClient: ArtifactsClientInterface; kuery: string; logger: Logger; internalESClient: ElasticsearchClient; From 4f2f64ff8cd1a8b513908a5dddda8b8f11f89db3 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 25 Sep 2024 12:00:53 -0400 Subject: [PATCH 20/24] Fix additional mocks in tests files --- .../fleet_integration.test.ts | 88 ++++++++----------- .../validators/base_validator.test.ts | 8 +- 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index c4e32f050c1d7..81bfbf1cdd979 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -10,7 +10,6 @@ import type { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types import { elasticsearchServiceMock, httpServerMock, - loggingSystemMock, savedObjectsClientMock, } from '@kbn/core/server/mocks'; import { @@ -76,13 +75,15 @@ import type { PostAgentPolicyCreateCallback, PutPackagePolicyUpdateCallback, } from '@kbn/fleet-plugin/server/types'; +import type { EndpointMetadataService } from '../endpoint/services/metadata'; +import { createEndpointMetadataServiceTestContextMock } from '../endpoint/services/metadata/mocks'; jest.mock('uuid', () => ({ v4: (): string => 'NEW_UUID', })); describe('ingest_integration tests ', () => { - let endpointAppContextMock: EndpointAppContextServiceStartContract; + let endpointAppContextStartContract: EndpointAppContextServiceStartContract; let req: KibanaRequest; let ctx: ReturnType; const exceptionListClient: ExceptionListClient = getExceptionListClientMock(); @@ -100,18 +101,24 @@ describe('ingest_integration tests ', () => { const generator = new EndpointDocGenerator(); const cloudService = cloudMock.createSetup(); let productFeaturesService: ProductFeaturesService; + let endpointMetadataService: EndpointMetadataService; + let logger: Logger; beforeEach(() => { - endpointAppContextMock = createMockEndpointAppContextServiceStartContract(); + endpointAppContextStartContract = createMockEndpointAppContextServiceStartContract(); ctx = requestContextMock.createTools().context; req = httpServerMock.createKibanaRequest(); licenseEmitter = new Subject(); licenseService = new LicenseService(); licenseService.start(licenseEmitter); - productFeaturesService = endpointAppContextMock.productFeaturesService; + productFeaturesService = endpointAppContextStartContract.productFeaturesService; + + const metadataMocks = createEndpointMetadataServiceTestContextMock(); + logger = metadataMocks.logger; + endpointMetadataService = metadataMocks.endpointMetadataService; jest - .spyOn(endpointAppContextMock.endpointMetadataService, 'getFleetEndpointPackagePolicy') + .spyOn(endpointMetadataService, 'getFleetEndpointPackagePolicy') .mockResolvedValue(createMockPolicyData()); }); @@ -156,12 +163,11 @@ describe('ingest_integration tests ', () => { }); const invokeCallback = async (manifestManager: ManifestManager): Promise => { - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyCreateCallback( logger, manifestManager, requestContextFactoryMock.create(), - endpointAppContextMock.alerting, + endpointAppContextStartContract.alerting, licenseService, exceptionListClient, cloudService, @@ -346,7 +352,6 @@ describe('ingest_integration tests ', () => { describe('package policy post create callback', () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyPostCreateCallback(logger, exceptionListClient); const policyConfig = generator.generatePolicyPackagePolicy() as PackagePolicy; @@ -420,8 +425,6 @@ describe('ingest_integration tests ', () => { describe('agent policy update callback', () => { it('ProductFeature disabled - returns an error if higher tier features are turned on in the policy', async () => { - const logger = loggingSystemMock.create().get('ingest_integration.test'); - productFeaturesService = createProductFeaturesServiceMock( ALL_PRODUCT_FEATURE_KEYS.filter( (key) => key !== ProductFeatureSecurityKey.endpointAgentTamperProtection @@ -437,8 +440,6 @@ describe('ingest_integration tests ', () => { ); }); it('ProductFeature disabled - returns agent policy if higher tier features are turned off in the policy', async () => { - const logger = loggingSystemMock.create().get('ingest_integration.test'); - productFeaturesService = createProductFeaturesServiceMock( ALL_PRODUCT_FEATURE_KEYS.filter( (key) => key !== ProductFeatureSecurityKey.endpointAgentTamperProtection @@ -453,8 +454,6 @@ describe('ingest_integration tests ', () => { expect(updatedPolicyConfig).toEqual(policyConfig); }); it('ProductFeature enabled - returns agent policy if higher tier features are turned on in the policy', async () => { - const logger = loggingSystemMock.create().get('ingest_integration.test'); - const callback = getAgentPolicyUpdateCallback(logger, productFeaturesService); const policyConfig = generator.generateAgentPolicy(); @@ -465,8 +464,6 @@ describe('ingest_integration tests ', () => { expect(updatedPolicyConfig).toEqual(policyConfig); }); it('ProductFeature enabled - returns agent policy if higher tier features are turned off in the policy', async () => { - const logger = loggingSystemMock.create().get('ingest_integration.test'); - const callback = getAgentPolicyUpdateCallback(logger, productFeaturesService); const policyConfig = generator.generateAgentPolicy(); @@ -477,12 +474,10 @@ describe('ingest_integration tests ', () => { }); describe('agent policy create callback', () => { - let logger: Logger; let callback: PostAgentPolicyCreateCallback; let policyConfig: GetAgentPoliciesResponseItem; beforeEach(() => { - logger = loggingSystemMock.create().get('ingest_integration.test'); callback = getAgentPolicyCreateCallback(logger, productFeaturesService); policyConfig = generator.generateAgentPolicy(); }); @@ -537,12 +532,11 @@ describe('ingest_integration tests ', () => { }); it('returns an error if paid features are turned on in the policy', async () => { const mockPolicy = policyFactory(); // defaults with paid features on - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -558,12 +552,11 @@ describe('ingest_integration tests ', () => { it('updates successfully if no paid features are turned on in the policy', async () => { const mockPolicy = policyFactoryWithoutPaidFeatures(); mockPolicy.windows.malware.mode = ProtectionModes.detect; - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -594,10 +587,10 @@ describe('ingest_integration tests ', () => { ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_protection_updates') ); const callback = getPackagePolicyUpdateCallback( - endpointAppContextMock.logger, + logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -649,12 +642,11 @@ describe('ingest_integration tests ', () => { 'should return bad request for invalid endpoint package policy global manifest values', async ({ date, message }) => { const mockPolicy = policyFactory(); // defaults with paid features on - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -715,12 +707,11 @@ describe('ingest_integration tests ', () => { 'should return bad request for invalid endpoint package policy global manifest values', async ({ date, message }) => { const mockPolicy = policyFactory(); // defaults with paid features on - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -759,12 +750,11 @@ describe('ingest_integration tests ', () => { it('updates successfully when paid features are turned on', async () => { const mockPolicy = policyFactory(); mockPolicy.windows.popup.malware.message = 'paid feature'; - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -786,10 +776,10 @@ describe('ingest_integration tests ', () => { ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections') ); const callback = getPackagePolicyUpdateCallback( - endpointAppContextMock.logger, + logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -864,12 +854,11 @@ describe('ingest_integration tests ', () => { mockPolicy.meta.license_uuid = 'updated-uid'; mockPolicy.meta.serverless = false; mockPolicy.meta.billable = false; - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -903,12 +892,11 @@ describe('ingest_integration tests ', () => { mockPolicy.meta.license_uuid = 'updated-uid'; mockPolicy.meta.serverless = false; mockPolicy.meta.billable = false; - const logger = loggingSystemMock.create().get('ingest_integration.test'); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -936,7 +924,6 @@ describe('ingest_integration tests ', () => { describe('when `antivirus_registration.mode` is changed', () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const logger = loggingSystemMock.create().get('ingest_integration.test'); let callback: PutPackagePolicyUpdateCallback; let inputPolicyConfig: PolicyData; let inputWindowsConfig: PolicyConfig['windows']; @@ -950,8 +937,8 @@ describe('ingest_integration tests ', () => { callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService @@ -1044,14 +1031,13 @@ describe('ingest_integration tests ', () => { const soClient = savedObjectsClientMock.create(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const logger = loggingSystemMock.create().get('ingest_integration.test'); licenseEmitter.next(Enterprise); const callback = getPackagePolicyUpdateCallback( logger, licenseService, - endpointAppContextMock.featureUsageService, - endpointAppContextMock.endpointMetadataService, + endpointAppContextStartContract.featureUsageService, + endpointMetadataService, cloudService, esClient, productFeaturesService diff --git a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.test.ts b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.test.ts index 8c3dfcd4bc27e..0f5da118ac2ae 100644 --- a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.test.ts +++ b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.test.ts @@ -37,8 +37,8 @@ describe('When using Artifacts Exceptions BaseValidator', () => { const servicesStart = createMockEndpointAppContextServiceStartContract(); - packagePolicyService = servicesStart.endpointFleetServicesFactory.asInternalUser() - .packagePolicy as jest.Mocked; + packagePolicyService = servicesStart.fleetStartServices + .packagePolicyService as jest.Mocked; endpointAppContextServices = new EndpointAppContextService(); endpointAppContextServices.setup(createMockEndpointAppContextServiceSetupContract()); @@ -48,7 +48,9 @@ describe('When using Artifacts Exceptions BaseValidator', () => { if (withNoAuth) { const fleetAuthz = createFleetAuthzMock(); fleetAuthz.fleet.all = false; - (servicesStart.fleetAuthzService?.fromRequest as jest.Mock).mockResolvedValue(fleetAuthz); + (servicesStart.fleetStartServices.authz.fromRequest as jest.Mock).mockResolvedValue( + fleetAuthz + ); (servicesStart.security.authc.getCurrentUser as jest.Mock).mockReturnValue( securityMock.createMockAuthenticatedUser() ); From 3e418a8f5751c9164c1556bb7fe5ec8e55a46717 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 25 Sep 2024 15:13:07 -0400 Subject: [PATCH 21/24] adjust Cloud Defend and Cloud posture plugins use of fleet start contract mock --- .../cloud_defend/server/plugin.test.ts | 33 ++----------------- .../server/plugin.test.ts | 29 ++-------------- 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/cloud_defend/server/plugin.test.ts b/x-pack/plugins/cloud_defend/server/plugin.test.ts index 93be8d2062680..3df0ae04f812a 100644 --- a/x-pack/plugins/cloud_defend/server/plugin.test.ts +++ b/x-pack/plugins/cloud_defend/server/plugin.test.ts @@ -11,29 +11,16 @@ import { httpServerMock, savedObjectsClientMock, } from '@kbn/core/server/mocks'; -import { - createPackagePolicyServiceMock, - createArtifactsClientMock, - createMockPackageService, - createMockAgentService, - createMockAgentPolicyService, -} from '@kbn/fleet-plugin/server/mocks'; import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { CloudDefendPlugin } from './plugin'; import { CloudDefendPluginStartDeps } from './types'; -import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks'; import { PackagePolicy, UpdatePackagePolicy } from '@kbn/fleet-plugin/common'; -import { - ExternalCallback, - FleetStartContract, - PostPackagePolicyPostCreateCallback, -} from '@kbn/fleet-plugin/server'; +import { PostPackagePolicyPostCreateCallback } from '@kbn/fleet-plugin/server'; import { INTEGRATION_PACKAGE_NAME } from '../common/constants'; import Chance from 'chance'; import type { AwaitedProperties } from '@kbn/utility-types'; -import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import { ElasticsearchClient, RequestHandlerContext, @@ -42,6 +29,7 @@ import { import { securityMock } from '@kbn/security-plugin/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import * as onPackagePolicyPostCreateCallback from './lib/fleet_util'; +import { createFleetStartContractMock } from '@kbn/fleet-plugin/server/mocks'; const chance = new Chance(); @@ -49,24 +37,9 @@ const mockRouteContext = { core: coreMock.createRequestHandlerContext(), } as unknown as AwaitedProperties; -const createMockFleetStartContract = (): DeeplyMockedKeys => { - return { - authz: { - fromRequest: jest.fn(async (_) => createFleetAuthzMock()), - }, - fleetSetupCompleted: jest.fn().mockResolvedValue(undefined), - agentService: createMockAgentService(), - packageService: createMockPackageService(), - agentPolicyService: createMockAgentPolicyService(), - registerExternalCallback: jest.fn((..._: ExternalCallback) => {}), - packagePolicyService: createPackagePolicyServiceMock(), - createArtifactsClient: jest.fn().mockReturnValue(createArtifactsClientMock()), - } as unknown as DeeplyMockedKeys; -}; - describe('Cloud Defend Plugin', () => { describe('start()', () => { - const fleetMock = createMockFleetStartContract(); + const fleetMock = createFleetStartContractMock(); const mockPlugins: CloudDefendPluginStartDeps = { fleet: fleetMock, data: dataPluginMock.createStartContract(), diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts index bac14b84139f0..9171fd8d0e8ec 100644 --- a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts @@ -11,20 +11,13 @@ import { httpServerMock, savedObjectsClientMock, } from '@kbn/core/server/mocks'; -import { - createPackagePolicyServiceMock, - createArtifactsClientMock, - createMockPackageService, - createMockAgentService, - createMockAgentPolicyService, -} from '@kbn/fleet-plugin/server/mocks'; +import { createFleetStartContractMock } from '@kbn/fleet-plugin/server/mocks'; import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { createPackagePolicyMock, deletePackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { CspPlugin } from './plugin'; import { CspServerPluginStartDeps } from './types'; -import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks'; import { Installation, ListResult, @@ -32,15 +25,12 @@ import { UpdatePackagePolicy, } from '@kbn/fleet-plugin/common'; import { - FleetStartContract, PostPackagePolicyPostDeleteCallback, PostPackagePolicyPostCreateCallback, - ExternalCallback, } from '@kbn/fleet-plugin/server'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../common/constants'; import Chance from 'chance'; import type { AwaitedProperties } from '@kbn/utility-types'; -import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import { createIndexPatternsStartMock } from '@kbn/data-views-plugin/server/mocks'; import { ElasticsearchClient, @@ -56,24 +46,9 @@ const mockRouteContext = { core: coreMock.createRequestHandlerContext(), } as unknown as AwaitedProperties; -const createMockFleetStartContract = (): DeeplyMockedKeys => { - return { - authz: { - fromRequest: jest.fn(async (_) => createFleetAuthzMock()), - }, - fleetSetupCompleted: jest.fn().mockResolvedValue(undefined), - agentService: createMockAgentService(), - packageService: createMockPackageService(), - agentPolicyService: createMockAgentPolicyService(), - registerExternalCallback: jest.fn((..._: ExternalCallback) => {}), - packagePolicyService: createPackagePolicyServiceMock(), - createArtifactsClient: jest.fn().mockReturnValue(createArtifactsClientMock()), - } as unknown as DeeplyMockedKeys; -}; - describe('Cloud Security Posture Plugin', () => { describe('start()', () => { - const fleetMock = createMockFleetStartContract(); + const fleetMock = createFleetStartContractMock(); const mockPlugins: CspServerPluginStartDeps = { fleet: fleetMock, data: dataPluginMock.createStartContract(), From 7231903238c4756a25cb0c21e8136ff1264baf5d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:28:52 +0000 Subject: [PATCH 22/24] [CI] Auto-commit changed files from 'node scripts/yarn_deduplicate' --- x-pack/plugins/cloud_defend/tsconfig.json | 1 - x-pack/plugins/cloud_security_posture/tsconfig.json | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/cloud_defend/tsconfig.json b/x-pack/plugins/cloud_defend/tsconfig.json index 2e71cfde9128d..9b87f53f7c7b6 100755 --- a/x-pack/plugins/cloud_defend/tsconfig.json +++ b/x-pack/plugins/cloud_defend/tsconfig.json @@ -32,7 +32,6 @@ "@kbn/es-types", "@kbn/data-views-plugin", "@kbn/utility-types", - "@kbn/utility-types-jest", "@kbn/kubernetes-security-plugin", "@kbn/core-http-router-server-mocks", "@kbn/core-elasticsearch-server", diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 2b075062b38c9..4e43b6df2485e 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -38,7 +38,6 @@ "@kbn/monaco", "@kbn/utility-types", "@kbn/core-logging-server-mocks", - "@kbn/utility-types-jest", "@kbn/securitysolution-es-utils", "@kbn/core-elasticsearch-client-server-mocks", "@kbn/core-elasticsearch-server", From d1a52b153e69d65ed82c2fde30c24f4927f6ff8a Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 26 Sep 2024 08:29:21 -0400 Subject: [PATCH 23/24] Remove unused variable --- .../agent/clients/endpoint/endpoint_agent_status_client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts index 65486f8d1b38f..ed8e4f45a1367 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/agent/clients/endpoint/endpoint_agent_status_client.ts @@ -18,7 +18,6 @@ export class EndpointAgentStatusClient extends AgentStatusClient { async getAgentStatuses(agentIds: string[]): Promise { const metadataService = this.options.endpointService.getEndpointMetadataService(); const esClient = this.options.esClient; - const soClient = this.options.soClient; try { const agentIdsKql = agentIds.map((agentId) => `agent.id: ${agentId}`).join(' or '); From b44fb4eea9e153b9716869ef0ac019b7b81508a7 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Thu, 26 Sep 2024 08:41:45 -0400 Subject: [PATCH 24/24] Change signature of saved object factory `createInternalScopedSoClient()` method --- .../server/endpoint/endpoint_app_context_services.ts | 4 ++-- .../endpoint/migrations/turn_off_agent_policy_features.ts | 2 +- .../migrations/turn_off_policy_protections.test.ts | 4 ++-- .../endpoint/migrations/turn_off_policy_protections.ts | 2 +- .../server/endpoint/services/metadata/mocks.ts | 2 +- .../saved_objects/saved_objects_client_factory.ts | 8 ++++---- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 1c6cdf87c0e6b..0d5595879899b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -155,7 +155,7 @@ export class EndpointAppContextService { productFeaturesService, } = this.startDependencies; const endpointMetadataService = this.getEndpointMetadataService(); - const soClient = this.savedObjects.createInternalScopedSoClient(undefined, false); + const soClient = this.savedObjects.createInternalScopedSoClient({ readonly: false }); const logger = this.createLogger('endpointFleetExtension'); registerFleetCallback( @@ -253,7 +253,7 @@ export class EndpointAppContextService { return new EndpointMetadataService( this.startDependencies.esClient, - this.savedObjects.createInternalScopedSoClient(spaceId, false), + this.savedObjects.createInternalScopedSoClient({ readonly: false }), this.getInternalFleetServices(), this.createLogger('endpointMetadata') ); diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts index d55cfcfbe4c1d..c054323618991 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_agent_policy_features.ts @@ -30,7 +30,7 @@ export const turnOffAgentPolicyFeatures = async ( ); const { agentPolicy: agentPolicyService, savedObjects } = fleetServices; - const internalSoClient = savedObjects.createInternalScopedSoClient(undefined, false); + const internalSoClient = savedObjects.createInternalScopedSoClient({ readonly: false }); const { updatedPolicies, failedPolicies } = await agentPolicyService.turnOffAgentTamperProtections(internalSoClient); diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts index 8f84ca9f1bb17..66a20569bdee8 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.test.ts @@ -142,7 +142,7 @@ describe('Turn Off Policy Protections Migration', () => { expect(fleetServices.packagePolicy.list as jest.Mock).toHaveBeenCalledTimes(2); expect(fleetServices.packagePolicy.bulkUpdate as jest.Mock).toHaveBeenCalledWith( - fleetServices.savedObjects.createInternalScopedSoClient(undefined, false), + fleetServices.savedObjects.createInternalScopedSoClient({ readonly: false }), esClient, [ expect.objectContaining({ id: bulkUpdateResponse.updatedPolicies![0].id }), @@ -213,7 +213,7 @@ describe('Turn Off Policy Protections Migration', () => { expect(fleetServices.packagePolicy.list as jest.Mock).toHaveBeenCalledTimes(2); expect(fleetServices.packagePolicy.bulkUpdate as jest.Mock).toHaveBeenCalledWith( - fleetServices.savedObjects.createInternalScopedSoClient(undefined, false), + fleetServices.savedObjects.createInternalScopedSoClient({ readonly: false }), esClient, [ expect.objectContaining({ id: bulkUpdateResponse.updatedPolicies![0].id }), diff --git a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts index 9b27ffc9b3947..43e30b25b4168 100644 --- a/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts +++ b/x-pack/plugins/security_solution/server/endpoint/migrations/turn_off_policy_protections.ts @@ -63,7 +63,7 @@ export const turnOffPolicyProtectionsIfNotSupported = async ( } const { packagePolicy, savedObjects, endpointPolicyKuery } = fleetServices; - const internalSoClient = savedObjects.createInternalScopedSoClient(undefined, false); + const internalSoClient = savedObjects.createInternalScopedSoClient({ readonly: false }); const updates: UpdatePackagePolicy[] = []; const messages: string[] = []; const perPage = 1000; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts index 4013cf1a507e5..f0c5fb8d74bcd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/mocks.ts @@ -45,7 +45,7 @@ export const createEndpointMetadataServiceTestContextMock = }).service.asInternalUser(); const endpointMetadataService = new EndpointMetadataService( esClient, - savedObjectsServiceFactory.createInternalScopedSoClient(undefined, false), + savedObjectsServiceFactory.createInternalScopedSoClient({ readonly: false }), fleetServices, logger ); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts index d1882a2fd249f..c925c666d6957 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/saved_objects/saved_objects_client_factory.ts @@ -76,10 +76,10 @@ export class SavedObjectsClientFactory { /** * Creates a SavedObjects client that is scoped to a space (default: `Default`) */ - createInternalScopedSoClient( - spaceId: string = DEFAULT_SPACE_ID, - readonly: boolean = true - ): SavedObjectsClientContract { + createInternalScopedSoClient({ + spaceId = DEFAULT_SPACE_ID, + readonly = true, + }: Partial<{ spaceId: string; readonly: boolean }> = {}): SavedObjectsClientContract { const soClient = this.savedObjectsServiceStart.getScopedClient( this.createFakeHttpRequest(spaceId), { excludedExtensions: [SECURITY_EXTENSION_ID] }