mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Fix adding prepackaged rules via fleet plugin (#114467)
Co-authored-by: Garrett Spong <garrett.spong@elastic.co> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
67d6355b2e
commit
5d1b5104fc
47 changed files with 794 additions and 561 deletions
|
@ -772,17 +772,17 @@
|
|||
"interfaces": [
|
||||
{
|
||||
"parentPluginId": "securitySolution",
|
||||
"id": "def-server.AppRequestContext",
|
||||
"id": "def-server.SecuritySolutionApiRequestHandlerContext",
|
||||
"type": "Interface",
|
||||
"tags": [],
|
||||
"label": "AppRequestContext",
|
||||
"label": "SecuritySolutionApiRequestHandlerContext",
|
||||
"description": [],
|
||||
"path": "x-pack/plugins/security_solution/server/types.ts",
|
||||
"deprecated": false,
|
||||
"children": [
|
||||
{
|
||||
"parentPluginId": "securitySolution",
|
||||
"id": "def-server.AppRequestContext.getAppClient",
|
||||
"id": "def-server.SecuritySolutionApiRequestHandlerContext.getAppClient",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "getAppClient",
|
||||
|
@ -804,7 +804,7 @@
|
|||
},
|
||||
{
|
||||
"parentPluginId": "securitySolution",
|
||||
"id": "def-server.AppRequestContext.getSpaceId",
|
||||
"id": "def-server.SecuritySolutionApiRequestHandlerContext.getSpaceId",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "getSpaceId",
|
||||
|
@ -819,7 +819,7 @@
|
|||
},
|
||||
{
|
||||
"parentPluginId": "securitySolution",
|
||||
"id": "def-server.AppRequestContext.getExecutionLogClient",
|
||||
"id": "def-server.SecuritySolutionApiRequestHandlerContext.getExecutionLogClient",
|
||||
"type": "Function",
|
||||
"tags": [],
|
||||
"label": "getExecutionLogClient",
|
||||
|
@ -31438,4 +31438,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ import {
|
|||
SPACE_IDS,
|
||||
} from '../../common/technical_rule_data_field_names';
|
||||
import { ParsedTechnicalFields } from '../../common/parse_technical_fields';
|
||||
import { Dataset, RuleDataPluginService } from '../rule_data_plugin_service';
|
||||
import { Dataset, IRuleDataService } from '../rule_data_plugin_service';
|
||||
|
||||
const getEsQueryConfig: typeof getEsQueryConfigTyped = getEsQueryConfigNonTyped;
|
||||
const getSafeSortIds: typeof getSafeSortIdsTyped = getSafeSortIdsNonTyped;
|
||||
|
@ -71,7 +71,7 @@ export interface ConstructorOptions {
|
|||
authorization: PublicMethodsOf<AlertingAuthorization>;
|
||||
auditLogger?: AuditLogger;
|
||||
esClient: ElasticsearchClient;
|
||||
ruleDataService: RuleDataPluginService;
|
||||
ruleDataService: IRuleDataService;
|
||||
}
|
||||
|
||||
export interface UpdateOptions<Params extends AlertTypeParams> {
|
||||
|
@ -116,7 +116,7 @@ export class AlertsClient {
|
|||
private readonly authorization: PublicMethodsOf<AlertingAuthorization>;
|
||||
private readonly esClient: ElasticsearchClient;
|
||||
private readonly spaceId: string | undefined;
|
||||
private readonly ruleDataService: RuleDataPluginService;
|
||||
private readonly ruleDataService: IRuleDataService;
|
||||
|
||||
constructor(options: ConstructorOptions) {
|
||||
this.logger = options.logger;
|
||||
|
|
|
@ -13,8 +13,7 @@ import { loggingSystemMock } from 'src/core/server/mocks';
|
|||
import { securityMock } from '../../../security/server/mocks';
|
||||
import { AuditLogger } from '../../../security/server';
|
||||
import { alertingAuthorizationMock } from '../../../alerting/server/authorization/alerting_authorization.mock';
|
||||
import { ruleDataPluginServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import { RuleDataPluginService } from '../rule_data_plugin_service';
|
||||
import { ruleDataServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
jest.mock('./alerts_client');
|
||||
|
||||
|
@ -26,7 +25,7 @@ const alertsClientFactoryParams: AlertsClientFactoryProps = {
|
|||
getAlertingAuthorization: (_: KibanaRequest) => alertingAuthMock,
|
||||
securityPluginSetup,
|
||||
esClient: {} as ElasticsearchClient,
|
||||
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
const fakeRequest = {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { PublicMethodsOf } from '@kbn/utility-types';
|
|||
import { ElasticsearchClient, KibanaRequest, Logger } from 'src/core/server';
|
||||
import { AlertingAuthorization } from '../../../alerting/server';
|
||||
import { SecurityPluginSetup } from '../../../security/server';
|
||||
import { RuleDataPluginService } from '../rule_data_plugin_service';
|
||||
import { IRuleDataService } from '../rule_data_plugin_service';
|
||||
import { AlertsClient } from './alerts_client';
|
||||
|
||||
export interface AlertsClientFactoryProps {
|
||||
|
@ -17,7 +17,7 @@ export interface AlertsClientFactoryProps {
|
|||
esClient: ElasticsearchClient;
|
||||
getAlertingAuthorization: (request: KibanaRequest) => PublicMethodsOf<AlertingAuthorization>;
|
||||
securityPluginSetup: SecurityPluginSetup | undefined;
|
||||
ruleDataService: RuleDataPluginService | null;
|
||||
ruleDataService: IRuleDataService | null;
|
||||
}
|
||||
|
||||
export class AlertsClientFactory {
|
||||
|
@ -28,7 +28,7 @@ export class AlertsClientFactory {
|
|||
request: KibanaRequest
|
||||
) => PublicMethodsOf<AlertingAuthorization>;
|
||||
private securityPluginSetup!: SecurityPluginSetup | undefined;
|
||||
private ruleDataService!: RuleDataPluginService | null;
|
||||
private ruleDataService!: IRuleDataService | null;
|
||||
|
||||
public initialize(options: AlertsClientFactoryProps) {
|
||||
/**
|
||||
|
|
|
@ -19,8 +19,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
|
|||
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
|
||||
import { AuditLogger } from '../../../../security/server';
|
||||
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
|
||||
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import { RuleDataPluginService } from '../../rule_data_plugin_service';
|
||||
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
|
@ -33,7 +32,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
|||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
const DEFAULT_SPACE = 'test_default_space_id';
|
||||
|
|
|
@ -18,8 +18,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
|
|||
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
|
||||
import { AuditLogger } from '../../../../security/server';
|
||||
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
|
||||
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import { RuleDataPluginService } from '../../rule_data_plugin_service';
|
||||
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
|
@ -32,7 +31,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
|||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
const DEFAULT_SPACE = 'test_default_space_id';
|
||||
|
|
|
@ -19,8 +19,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
|
|||
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
|
||||
import { AuditLogger } from '../../../../security/server';
|
||||
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
|
||||
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import { RuleDataPluginService } from '../../rule_data_plugin_service';
|
||||
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
|
@ -33,7 +32,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
|||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
const DEFAULT_SPACE = 'test_default_space_id';
|
||||
|
|
|
@ -18,8 +18,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
|
|||
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
|
||||
import { AuditLogger } from '../../../../security/server';
|
||||
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
|
||||
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import { RuleDataPluginService } from '../../rule_data_plugin_service';
|
||||
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
|
@ -32,7 +31,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
|||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
const DEFAULT_SPACE = 'test_default_space_id';
|
||||
|
|
|
@ -12,7 +12,7 @@ import { PluginInitializerContext } from 'src/core/server';
|
|||
import { RuleRegistryPlugin } from './plugin';
|
||||
|
||||
export type { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract } from './plugin';
|
||||
export { RuleDataPluginService } from './rule_data_plugin_service';
|
||||
export { IRuleDataService, RuleDataPluginService } from './rule_data_plugin_service';
|
||||
export { RuleDataClient } from './rule_data_client';
|
||||
export { IRuleDataClient } from './rule_data_client/types';
|
||||
export type {
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
|
||||
import { alertsClientMock } from './alert_data_client/alerts_client.mock';
|
||||
import { createRuleDataClientMock } from './rule_data_client/rule_data_client.mock';
|
||||
import { ruleDataPluginServiceMock } from './rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import {
|
||||
ruleDataServiceMock,
|
||||
RuleDataServiceMock,
|
||||
} from './rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
import { createLifecycleAlertServicesMock } from './utils/lifecycle_alert_services_mock';
|
||||
|
||||
export const ruleRegistryMocks = {
|
||||
createLifecycleAlertServices: createLifecycleAlertServicesMock,
|
||||
createRuleDataPluginService: ruleDataPluginServiceMock.create,
|
||||
createRuleDataService: ruleDataServiceMock.create,
|
||||
createRuleDataClient: createRuleDataClientMock,
|
||||
createAlertsClientMock: alertsClientMock,
|
||||
};
|
||||
|
||||
export { RuleDataServiceMock };
|
||||
|
|
|
@ -20,7 +20,7 @@ import { PluginStartContract as AlertingStart } from '../../alerting/server';
|
|||
import { SecurityPluginSetup } from '../../security/server';
|
||||
|
||||
import { RuleRegistryPluginConfig } from './config';
|
||||
import { RuleDataPluginService } from './rule_data_plugin_service';
|
||||
import { IRuleDataService, RuleDataService } from './rule_data_plugin_service';
|
||||
import { AlertsClientFactory } from './alert_data_client/alerts_client_factory';
|
||||
import { AlertsClient } from './alert_data_client/alerts_client';
|
||||
import { RacApiRequestHandlerContext, RacRequestHandlerContext } from './types';
|
||||
|
@ -35,7 +35,7 @@ export interface RuleRegistryPluginStartDependencies {
|
|||
}
|
||||
|
||||
export interface RuleRegistryPluginSetupContract {
|
||||
ruleDataService: RuleDataPluginService;
|
||||
ruleDataService: IRuleDataService;
|
||||
}
|
||||
|
||||
export interface RuleRegistryPluginStartContract {
|
||||
|
@ -57,7 +57,7 @@ export class RuleRegistryPlugin
|
|||
private readonly logger: Logger;
|
||||
private readonly kibanaVersion: string;
|
||||
private readonly alertsClientFactory: AlertsClientFactory;
|
||||
private ruleDataService: RuleDataPluginService | null;
|
||||
private ruleDataService: IRuleDataService | null;
|
||||
private security: SecurityPluginSetup | undefined;
|
||||
|
||||
constructor(initContext: PluginInitializerContext) {
|
||||
|
@ -100,7 +100,7 @@ export class RuleRegistryPlugin
|
|||
}
|
||||
};
|
||||
|
||||
this.ruleDataService = new RuleDataPluginService({
|
||||
this.ruleDataService = new RuleDataService({
|
||||
logger,
|
||||
kibanaVersion,
|
||||
isWriteEnabled: isWriteEnabled(this.config, this.legacyConfig),
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { RuleDataPluginService } from './rule_data_plugin_service';
|
||||
import { IRuleDataService } from './rule_data_plugin_service';
|
||||
|
||||
type Schema = PublicMethodsOf<RuleDataPluginService>;
|
||||
|
||||
const createRuleDataPluginService = () => {
|
||||
const mocked: jest.Mocked<Schema> = {
|
||||
export const ruleDataServiceMock = {
|
||||
create: (): jest.Mocked<IRuleDataService> => ({
|
||||
getResourcePrefix: jest.fn(),
|
||||
getResourceName: jest.fn(),
|
||||
isWriteEnabled: jest.fn(),
|
||||
|
@ -19,10 +16,9 @@ const createRuleDataPluginService = () => {
|
|||
initializeIndex: jest.fn(),
|
||||
findIndexByName: jest.fn(),
|
||||
findIndicesByFeature: jest.fn(),
|
||||
};
|
||||
return mocked;
|
||||
}),
|
||||
};
|
||||
|
||||
export const ruleDataPluginServiceMock = {
|
||||
create: createRuleDataPluginService,
|
||||
};
|
||||
export const RuleDataServiceMock = jest
|
||||
.fn<jest.Mocked<IRuleDataService>, []>()
|
||||
.mockImplementation(ruleDataServiceMock.create);
|
||||
|
|
|
@ -17,6 +17,59 @@ import { Dataset, IndexOptions } from './index_options';
|
|||
import { ResourceInstaller } from './resource_installer';
|
||||
import { joinWithDash } from './utils';
|
||||
|
||||
/**
|
||||
* A service for creating and using Elasticsearch indices for alerts-as-data.
|
||||
*/
|
||||
export interface IRuleDataService {
|
||||
/**
|
||||
* Returns a prefix used in the naming scheme of index aliases, templates
|
||||
* and other Elasticsearch resources that this service creates
|
||||
* for alerts-as-data indices.
|
||||
*/
|
||||
getResourcePrefix(): string;
|
||||
|
||||
/**
|
||||
* Prepends a relative resource name with the resource prefix.
|
||||
* @returns Full name of the resource.
|
||||
* @example 'security.alerts' => '.alerts-security.alerts'
|
||||
*/
|
||||
getResourceName(relativeName: string): string;
|
||||
|
||||
/**
|
||||
* If write is enabled, everything works as usual.
|
||||
* If it's disabled, writing to all alerts-as-data indices will be disabled,
|
||||
* and also Elasticsearch resources associated with the indices will not be
|
||||
* installed.
|
||||
*/
|
||||
isWriteEnabled(): boolean;
|
||||
|
||||
/**
|
||||
* Installs common Elasticsearch resources used by all alerts-as-data indices.
|
||||
*/
|
||||
initializeService(): void;
|
||||
|
||||
/**
|
||||
* Initializes alerts-as-data index and starts index bootstrapping right away.
|
||||
* @param indexOptions Index parameters: names and resources.
|
||||
* @returns Client for reading and writing data to this index.
|
||||
*/
|
||||
initializeIndex(indexOptions: IndexOptions): IRuleDataClient;
|
||||
|
||||
/**
|
||||
* Looks up the index information associated with the given registration context and dataset.
|
||||
*/
|
||||
findIndexByName(registrationContext: string, dataset: Dataset): IndexInfo | null;
|
||||
|
||||
/**
|
||||
* Looks up the index information associated with the given Kibana "feature".
|
||||
* Note: features are used in RBAC.
|
||||
*/
|
||||
findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[];
|
||||
}
|
||||
|
||||
// TODO: This is a leftover. Remove its usage from the "observability" plugin and delete it.
|
||||
export type RuleDataPluginService = IRuleDataService;
|
||||
|
||||
interface ConstructorOptions {
|
||||
getClusterClient: () => Promise<ElasticsearchClient>;
|
||||
logger: Logger;
|
||||
|
@ -24,10 +77,7 @@ interface ConstructorOptions {
|
|||
isWriteEnabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A service for creating and using Elasticsearch indices for alerts-as-data.
|
||||
*/
|
||||
export class RuleDataPluginService {
|
||||
export class RuleDataService implements IRuleDataService {
|
||||
private readonly indicesByBaseName: Map<string, IndexInfo>;
|
||||
private readonly indicesByFeatureId: Map<string, IndexInfo[]>;
|
||||
private readonly resourceInstaller: ResourceInstaller;
|
||||
|
@ -49,37 +99,18 @@ export class RuleDataPluginService {
|
|||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prefix used in the naming scheme of index aliases, templates
|
||||
* and other Elasticsearch resources that this service creates
|
||||
* for alerts-as-data indices.
|
||||
*/
|
||||
public getResourcePrefix(): string {
|
||||
return INDEX_PREFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a relative resource name with the resource prefix.
|
||||
* @returns Full name of the resource.
|
||||
* @example 'security.alerts' => '.alerts-security.alerts'
|
||||
*/
|
||||
public getResourceName(relativeName: string): string {
|
||||
return joinWithDash(this.getResourcePrefix(), relativeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* If write is enabled, everything works as usual.
|
||||
* If it's disabled, writing to all alerts-as-data indices will be disabled,
|
||||
* and also Elasticsearch resources associated with the indices will not be
|
||||
* installed.
|
||||
*/
|
||||
public isWriteEnabled(): boolean {
|
||||
return this.options.isWriteEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs common Elasticsearch resources used by all alerts-as-data indices.
|
||||
*/
|
||||
public initializeService(): void {
|
||||
// Run the installation of common resources and handle exceptions.
|
||||
this.installCommonResources = this.resourceInstaller
|
||||
|
@ -93,11 +124,6 @@ export class RuleDataPluginService {
|
|||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes alerts-as-data index and starts index bootstrapping right away.
|
||||
* @param indexOptions Index parameters: names and resources.
|
||||
* @returns Client for reading and writing data to this index.
|
||||
*/
|
||||
public initializeIndex(indexOptions: IndexOptions): IRuleDataClient {
|
||||
if (!this.isInitialized) {
|
||||
throw new Error(
|
||||
|
@ -156,18 +182,11 @@ export class RuleDataPluginService {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the index information associated with the given registration context and dataset.
|
||||
*/
|
||||
public findIndexByName(registrationContext: string, dataset: Dataset): IndexInfo | null {
|
||||
const baseName = this.getResourceName(`${registrationContext}.${dataset}`);
|
||||
return this.indicesByBaseName.get(baseName) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the index information associated with the given Kibana "feature".
|
||||
* Note: features are used in RBAC.
|
||||
*/
|
||||
public findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[] {
|
||||
const foundIndices = this.indicesByFeatureId.get(featureId) ?? [];
|
||||
return dataset ? foundIndices.filter((i) => i.indexOptions.dataset === dataset) : foundIndices;
|
||||
|
|
62
x-pack/plugins/security_solution/server/config.mock.ts
Normal file
62
x-pack/plugins/security_solution/server/config.mock.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../common/constants';
|
||||
import {
|
||||
ExperimentalFeatures,
|
||||
parseExperimentalConfigValue,
|
||||
} from '../common/experimental_features';
|
||||
import { ConfigType } from './config';
|
||||
import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types';
|
||||
|
||||
export const createMockConfig = (): ConfigType => {
|
||||
const enableExperimental: string[] = [];
|
||||
|
||||
return {
|
||||
[SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX,
|
||||
maxRuleImportExportSize: 10000,
|
||||
maxRuleImportPayloadBytes: 10485760,
|
||||
maxTimelineImportExportSize: 10000,
|
||||
maxTimelineImportPayloadBytes: 10485760,
|
||||
enableExperimental,
|
||||
endpointResultListDefaultFirstPageIndex: 0,
|
||||
endpointResultListDefaultPageSize: 10,
|
||||
packagerTaskInterval: '60s',
|
||||
alertMergeStrategy: 'missingFields',
|
||||
alertIgnoreFields: [],
|
||||
prebuiltRulesFromFileSystem: true,
|
||||
prebuiltRulesFromSavedObjects: false,
|
||||
ruleExecutionLog: {
|
||||
underlyingClient: UnderlyingLogClient.savedObjects,
|
||||
},
|
||||
|
||||
kibanaIndex: '.kibana',
|
||||
experimentalFeatures: parseExperimentalConfigValue(enableExperimental),
|
||||
};
|
||||
};
|
||||
|
||||
const withExperimentalFeature = (
|
||||
config: ConfigType,
|
||||
feature: keyof ExperimentalFeatures
|
||||
): ConfigType => {
|
||||
const enableExperimental = config.enableExperimental.concat(feature);
|
||||
return {
|
||||
...config,
|
||||
enableExperimental,
|
||||
experimentalFeatures: parseExperimentalConfigValue(enableExperimental),
|
||||
};
|
||||
};
|
||||
|
||||
const withRuleRegistryEnabled = (config: ConfigType, isEnabled: boolean): ConfigType => {
|
||||
return isEnabled ? withExperimentalFeature(config, 'ruleRegistryEnabled') : config;
|
||||
};
|
||||
|
||||
export const configMock = {
|
||||
createDefault: createMockConfig,
|
||||
withExperimentalFeature,
|
||||
withRuleRegistryEnabled,
|
||||
};
|
|
@ -9,8 +9,10 @@ import { schema, TypeOf } from '@kbn/config-schema';
|
|||
import { PluginInitializerContext } from '../../../../src/core/server';
|
||||
import { SIGNALS_INDEX_KEY, DEFAULT_SIGNALS_INDEX } from '../common/constants';
|
||||
import {
|
||||
ExperimentalFeatures,
|
||||
getExperimentalAllowedValues,
|
||||
isValidExperimentalValue,
|
||||
parseExperimentalConfigValue,
|
||||
} from '../common/experimental_features';
|
||||
import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types';
|
||||
|
||||
|
@ -134,7 +136,23 @@ export const configSchema = schema.object({
|
|||
prebuiltRulesFromSavedObjects: schema.boolean({ defaultValue: true }),
|
||||
});
|
||||
|
||||
export const createConfig = (context: PluginInitializerContext) =>
|
||||
context.config.get<TypeOf<typeof configSchema>>();
|
||||
export type ConfigSchema = TypeOf<typeof configSchema>;
|
||||
|
||||
export type ConfigType = TypeOf<typeof configSchema>;
|
||||
export type ConfigType = ConfigSchema & {
|
||||
kibanaIndex: string;
|
||||
experimentalFeatures: ExperimentalFeatures;
|
||||
};
|
||||
|
||||
export const createConfig = (context: PluginInitializerContext): ConfigType => {
|
||||
const globalConfig = context.config.legacy.get();
|
||||
const pluginConfig = context.config.get<TypeOf<typeof configSchema>>();
|
||||
|
||||
const kibanaIndex = globalConfig.kibana.index;
|
||||
const experimentalFeatures = parseExperimentalConfigValue(pluginConfig.enableExperimental);
|
||||
|
||||
return {
|
||||
...pluginConfig,
|
||||
kibanaIndex,
|
||||
experimentalFeatures,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,13 +27,18 @@ import {
|
|||
import { ManifestManager } from './services/artifacts';
|
||||
import { AppClientFactory } from '../client';
|
||||
import { ConfigType } from '../config';
|
||||
import { IRequestContextFactory } from '../request_context_factory';
|
||||
import { LicenseService } from '../../common/license';
|
||||
import {
|
||||
ExperimentalFeatures,
|
||||
parseExperimentalConfigValue,
|
||||
} from '../../common/experimental_features';
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
import { EndpointMetadataService } from './services/metadata';
|
||||
import { EndpointAppContentServicesNotStartedError } from './errors';
|
||||
import {
|
||||
EndpointAppContentServicesNotSetUpError,
|
||||
EndpointAppContentServicesNotStartedError,
|
||||
} from './errors';
|
||||
|
||||
export interface EndpointAppContextServiceSetupContract {
|
||||
securitySolutionRequestContextFactory: IRequestContextFactory;
|
||||
}
|
||||
|
||||
export type EndpointAppContextServiceStartContract = Partial<
|
||||
Pick<
|
||||
|
@ -59,40 +64,29 @@ export type EndpointAppContextServiceStartContract = Partial<
|
|||
* of the plugin lifecycle. And stop during the stop phase, if needed.
|
||||
*/
|
||||
export class EndpointAppContextService {
|
||||
private agentService: AgentService | undefined;
|
||||
private manifestManager: ManifestManager | undefined;
|
||||
private packagePolicyService: PackagePolicyServiceInterface | undefined;
|
||||
private agentPolicyService: AgentPolicyServiceInterface | undefined;
|
||||
private config: ConfigType | undefined;
|
||||
private license: LicenseService | undefined;
|
||||
private setupDependencies: EndpointAppContextServiceSetupContract | null = null;
|
||||
private startDependencies: EndpointAppContextServiceStartContract | null = null;
|
||||
public security: SecurityPluginStart | undefined;
|
||||
private cases: CasesPluginStartContract | undefined;
|
||||
private endpointMetadataService: EndpointMetadataService | undefined;
|
||||
private experimentalFeatures: ExperimentalFeatures | undefined;
|
||||
|
||||
public setup(dependencies: EndpointAppContextServiceSetupContract) {
|
||||
this.setupDependencies = dependencies;
|
||||
}
|
||||
|
||||
public start(dependencies: EndpointAppContextServiceStartContract) {
|
||||
this.agentService = dependencies.agentService;
|
||||
this.packagePolicyService = dependencies.packagePolicyService;
|
||||
this.agentPolicyService = dependencies.agentPolicyService;
|
||||
this.manifestManager = dependencies.manifestManager;
|
||||
this.config = dependencies.config;
|
||||
this.license = dependencies.licenseService;
|
||||
this.security = dependencies.security;
|
||||
this.cases = dependencies.cases;
|
||||
this.endpointMetadataService = dependencies.endpointMetadataService;
|
||||
this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental);
|
||||
if (!this.setupDependencies) {
|
||||
throw new EndpointAppContentServicesNotSetUpError();
|
||||
}
|
||||
|
||||
if (this.manifestManager && dependencies.registerIngestCallback) {
|
||||
this.startDependencies = dependencies;
|
||||
this.security = dependencies.security;
|
||||
|
||||
if (dependencies.registerIngestCallback && dependencies.manifestManager) {
|
||||
dependencies.registerIngestCallback(
|
||||
'packagePolicyCreate',
|
||||
getPackagePolicyCreateCallback(
|
||||
dependencies.logger,
|
||||
this.manifestManager,
|
||||
dependencies.appClientFactory,
|
||||
dependencies.config.maxTimelineImportExportSize,
|
||||
dependencies.config.prebuiltRulesFromFileSystem,
|
||||
dependencies.config.prebuiltRulesFromSavedObjects,
|
||||
dependencies.security,
|
||||
dependencies.manifestManager,
|
||||
this.setupDependencies.securitySolutionRequestContextFactory,
|
||||
dependencies.alerting,
|
||||
dependencies.licenseService,
|
||||
dependencies.exceptionListsClient
|
||||
|
@ -106,7 +100,10 @@ export class EndpointAppContextService {
|
|||
|
||||
dependencies.registerIngestCallback(
|
||||
'postPackagePolicyDelete',
|
||||
getPackagePolicyDeleteCallback(dependencies.exceptionListsClient, this.experimentalFeatures)
|
||||
getPackagePolicyDeleteCallback(
|
||||
dependencies.exceptionListsClient,
|
||||
dependencies.config.experimentalFeatures
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -114,43 +111,43 @@ export class EndpointAppContextService {
|
|||
public stop() {}
|
||||
|
||||
public getExperimentalFeatures(): Readonly<ExperimentalFeatures> | undefined {
|
||||
return this.experimentalFeatures;
|
||||
return this.startDependencies?.config.experimentalFeatures;
|
||||
}
|
||||
|
||||
public getEndpointMetadataService(): EndpointMetadataService {
|
||||
if (!this.endpointMetadataService) {
|
||||
if (this.startDependencies == null) {
|
||||
throw new EndpointAppContentServicesNotStartedError();
|
||||
}
|
||||
return this.endpointMetadataService;
|
||||
return this.startDependencies.endpointMetadataService;
|
||||
}
|
||||
|
||||
public getAgentService(): AgentService | undefined {
|
||||
return this.agentService;
|
||||
return this.startDependencies?.agentService;
|
||||
}
|
||||
|
||||
public getPackagePolicyService(): PackagePolicyServiceInterface | undefined {
|
||||
return this.packagePolicyService;
|
||||
return this.startDependencies?.packagePolicyService;
|
||||
}
|
||||
|
||||
public getAgentPolicyService(): AgentPolicyServiceInterface | undefined {
|
||||
return this.agentPolicyService;
|
||||
return this.startDependencies?.agentPolicyService;
|
||||
}
|
||||
|
||||
public getManifestManager(): ManifestManager | undefined {
|
||||
return this.manifestManager;
|
||||
return this.startDependencies?.manifestManager;
|
||||
}
|
||||
|
||||
public getLicenseService(): LicenseService {
|
||||
if (!this.license) {
|
||||
if (this.startDependencies == null) {
|
||||
throw new EndpointAppContentServicesNotStartedError();
|
||||
}
|
||||
return this.license;
|
||||
return this.startDependencies.licenseService;
|
||||
}
|
||||
|
||||
public async getCasesClient(req: KibanaRequest): Promise<CasesClient> {
|
||||
if (!this.cases) {
|
||||
if (this.startDependencies?.cases == null) {
|
||||
throw new EndpointAppContentServicesNotStartedError();
|
||||
}
|
||||
return this.cases.getCasesClientWithRequest(req);
|
||||
return this.startDependencies.cases.getCasesClientWithRequest(req);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@ export class EndpointError extends Error {
|
|||
|
||||
export class NotFoundError extends EndpointError {}
|
||||
|
||||
export class EndpointAppContentServicesNotSetUpError extends EndpointError {
|
||||
constructor() {
|
||||
super('EndpointAppContextService has not been set up (EndpointAppContextService.setup())');
|
||||
}
|
||||
}
|
||||
|
||||
export class EndpointAppContentServicesNotStartedError extends EndpointError {
|
||||
constructor() {
|
||||
super('EndpointAppContextService has not been started (EndpointAppContextService.start())');
|
||||
|
|
|
@ -22,6 +22,7 @@ import { AppClientFactory } from '../client';
|
|||
import { createMockConfig } from '../lib/detection_engine/routes/__mocks__';
|
||||
import {
|
||||
EndpointAppContextService,
|
||||
EndpointAppContextServiceSetupContract,
|
||||
EndpointAppContextServiceStartContract,
|
||||
} from './endpoint_app_context_services';
|
||||
import { ManifestManager } from './services/artifacts/manifest_manager/manifest_manager';
|
||||
|
@ -37,6 +38,7 @@ import { parseExperimentalConfigValue } from '../../common/experimental_features
|
|||
// a restricted path.
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { createCasesClientMock } from '../../../cases/server/client/mocks';
|
||||
import { requestContextFactoryMock } from '../request_context_factory.mock';
|
||||
import { EndpointMetadataService } from './services/metadata';
|
||||
|
||||
/**
|
||||
|
@ -69,13 +71,25 @@ export const createMockEndpointAppContextService = (
|
|||
} as unknown as jest.Mocked<EndpointAppContextService>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a mocked input contract for the `EndpointAppContextService#setup()` method
|
||||
*/
|
||||
export const createMockEndpointAppContextServiceSetupContract =
|
||||
(): jest.Mocked<EndpointAppContextServiceSetupContract> => {
|
||||
return {
|
||||
securitySolutionRequestContextFactory: requestContextFactoryMock.create(),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a mocked input contract for the `EndpointAppContextService#start()` method
|
||||
*/
|
||||
export const createMockEndpointAppContextServiceStartContract =
|
||||
(): jest.Mocked<EndpointAppContextServiceStartContract> => {
|
||||
const factory = new AppClientFactory();
|
||||
const config = createMockConfig();
|
||||
const factory = new AppClientFactory();
|
||||
factory.setup({ getSpaceId: () => 'mockSpace', config });
|
||||
|
||||
const casesClientMock = createCasesClientMock();
|
||||
const savedObjectsStart = savedObjectsServiceMock.createStartContract();
|
||||
const agentService = createMockAgentService();
|
||||
|
@ -86,8 +100,6 @@ export const createMockEndpointAppContextServiceStartContract =
|
|||
agentPolicyService
|
||||
);
|
||||
|
||||
factory.setup({ getSpaceId: () => 'mockSpace', config });
|
||||
|
||||
return {
|
||||
agentService,
|
||||
agentPolicyService,
|
||||
|
|
|
@ -25,6 +25,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe
|
|||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import {
|
||||
createMockEndpointAppContextServiceSetupContract,
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
createRouteHandlerContext,
|
||||
} from '../../mocks';
|
||||
|
@ -130,6 +131,7 @@ describe('Action Log API', () => {
|
|||
const esClientMock = elasticsearchServiceMock.createScopedClusterClient();
|
||||
const routerMock = httpServiceMock.createRouter();
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start(createMockEndpointAppContextServiceStartContract());
|
||||
|
||||
registerActionAuditLogRoutes(routerMock, {
|
||||
|
|
|
@ -18,6 +18,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe
|
|||
import { SecuritySolutionRequestHandlerContext } from '../../../types';
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import {
|
||||
createMockEndpointAppContextServiceSetupContract,
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
createMockPackageService,
|
||||
createRouteHandlerContext,
|
||||
|
@ -157,9 +158,12 @@ describe('Host Isolation', () => {
|
|||
keep_policies_up_to_date: false,
|
||||
})
|
||||
);
|
||||
|
||||
licenseEmitter = new Subject();
|
||||
licenseService = new LicenseService();
|
||||
licenseService.start(licenseEmitter);
|
||||
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({
|
||||
...startContract,
|
||||
licenseService,
|
||||
|
|
|
@ -21,6 +21,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe
|
|||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import {
|
||||
createMockEndpointAppContextServiceSetupContract,
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
createRouteHandlerContext,
|
||||
} from '../../mocks';
|
||||
|
@ -67,6 +68,7 @@ describe('Endpoint Action Status', () => {
|
|||
const esClientMock = elasticsearchServiceMock.createScopedClusterClient();
|
||||
const routerMock = httpServiceMock.createRouter();
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start(createMockEndpointAppContextServiceStartContract());
|
||||
|
||||
registerActionStatusRoutes(routerMock, {
|
||||
|
|
|
@ -22,6 +22,7 @@ import { HostInfo, HostResultList, HostStatus } from '../../../../common/endpoin
|
|||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { registerEndpointRoutes } from './index';
|
||||
import {
|
||||
createMockEndpointAppContextServiceSetupContract,
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
createMockPackageService,
|
||||
createRouteHandlerContext,
|
||||
|
@ -134,6 +135,7 @@ describe('test endpoint route', () => {
|
|||
keep_policies_up_to_date: false,
|
||||
})
|
||||
);
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
|
||||
|
@ -394,6 +396,7 @@ describe('test endpoint route', () => {
|
|||
keep_policies_up_to_date: false,
|
||||
})
|
||||
);
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import {
|
||||
createMockEndpointAppContextServiceSetupContract,
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
createRouteHandlerContext,
|
||||
} from '../../mocks';
|
||||
|
@ -45,6 +46,7 @@ describe('test policy response handler', () => {
|
|||
mockSavedObjectClient = savedObjectsClientMock.create();
|
||||
mockResponse = httpServerMock.createResponseFactory();
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start(createMockEndpointAppContextServiceStartContract());
|
||||
});
|
||||
|
||||
|
@ -161,6 +163,7 @@ describe('test policy response handler', () => {
|
|||
page: 1,
|
||||
perPage: 1,
|
||||
};
|
||||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({
|
||||
...createMockEndpointAppContextServiceStartContract(),
|
||||
...{ agentService: mockAgentService },
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { httpServerMock, loggingSystemMock } from 'src/core/server/mocks';
|
||||
import { createNewPackagePolicyMock, deletePackagePolicyMock } from '../../../fleet/common/mocks';
|
||||
import {
|
||||
|
@ -18,7 +20,8 @@ import {
|
|||
getPackagePolicyUpdateCallback,
|
||||
} from './fleet_integration';
|
||||
import { KibanaRequest } from 'kibana/server';
|
||||
import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__';
|
||||
import { requestContextMock } from '../lib/detection_engine/routes/__mocks__';
|
||||
import { requestContextFactoryMock } from '../request_context_factory.mock';
|
||||
import { EndpointAppContextServiceStartContract } from '../endpoint/endpoint_app_context_services';
|
||||
import { createMockEndpointAppContextServiceStartContract } from '../endpoint/mocks';
|
||||
import { licenseMock } from '../../../licensing/common/licensing.mock';
|
||||
|
@ -42,16 +45,12 @@ import {
|
|||
ExperimentalFeatures,
|
||||
} from '../../common/experimental_features';
|
||||
import { DeletePackagePoliciesResponse } from '../../../fleet/common';
|
||||
import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
describe('ingest_integration tests ', () => {
|
||||
let endpointAppContextMock: EndpointAppContextServiceStartContract;
|
||||
let req: KibanaRequest;
|
||||
let ctx: SecuritySolutionRequestHandlerContext;
|
||||
const exceptionListClient: ExceptionListClient = getExceptionListClientMock();
|
||||
const maxTimelineImportExportSize = createMockConfig().maxTimelineImportExportSize;
|
||||
const prebuiltRulesFromFileSystem = createMockConfig().prebuiltRulesFromFileSystem;
|
||||
const prebuiltRulesFromSavedObjects = createMockConfig().prebuiltRulesFromSavedObjects;
|
||||
let licenseEmitter: Subject<ILicense>;
|
||||
let licenseService: LicenseService;
|
||||
const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } });
|
||||
|
@ -88,11 +87,7 @@ describe('ingest_integration tests ', () => {
|
|||
const callback = getPackagePolicyCreateCallback(
|
||||
logger,
|
||||
manifestManager,
|
||||
endpointAppContextMock.appClientFactory,
|
||||
maxTimelineImportExportSize,
|
||||
prebuiltRulesFromFileSystem,
|
||||
prebuiltRulesFromSavedObjects,
|
||||
endpointAppContextMock.security,
|
||||
requestContextFactoryMock.create(),
|
||||
endpointAppContextMock.alerting,
|
||||
licenseService,
|
||||
exceptionListClient
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { KibanaRequest, Logger, RequestHandlerContext } from 'kibana/server';
|
||||
import { ExceptionListClient } from '../../../lists/server';
|
||||
import { PluginStartContract as AlertsStartContract } from '../../../alerting/server';
|
||||
import { SecurityPluginStart } from '../../../security/server';
|
||||
import {
|
||||
PostPackagePolicyCreateCallback,
|
||||
PostPackagePolicyDeleteCallback,
|
||||
|
@ -18,15 +17,15 @@ import {
|
|||
import { NewPackagePolicy, UpdatePackagePolicy } from '../../../fleet/common';
|
||||
|
||||
import { NewPolicyData, PolicyConfig } from '../../common/endpoint/types';
|
||||
import { ManifestManager } from '../endpoint/services';
|
||||
import { AppClientFactory } from '../client';
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
import { LicenseService } from '../../common/license';
|
||||
import { ManifestManager } from '../endpoint/services';
|
||||
import { IRequestContextFactory } from '../request_context_factory';
|
||||
import { installPrepackagedRules } from './handlers/install_prepackaged_rules';
|
||||
import { createPolicyArtifactManifest } from './handlers/create_policy_artifact_manifest';
|
||||
import { createDefaultPolicy } from './handlers/create_default_policy';
|
||||
import { validatePolicyAgainstLicense } from './handlers/validate_policy_against_license';
|
||||
import { removePolicyFromTrustedApps } from './handlers/remove_policy_from_trusted_apps';
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
|
||||
const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
|
||||
packagePolicy: T
|
||||
|
@ -40,11 +39,7 @@ const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
|
|||
export const getPackagePolicyCreateCallback = (
|
||||
logger: Logger,
|
||||
manifestManager: ManifestManager,
|
||||
appClientFactory: AppClientFactory,
|
||||
maxTimelineImportExportSize: number,
|
||||
prebuiltRulesFromFileSystem: boolean,
|
||||
prebuiltRulesFromSavedObjects: boolean,
|
||||
securityStart: SecurityPluginStart,
|
||||
securitySolutionRequestContextFactory: IRequestContextFactory,
|
||||
alerts: AlertsStartContract,
|
||||
licenseService: LicenseService,
|
||||
exceptionsClient: ExceptionListClient | undefined
|
||||
|
@ -59,20 +54,23 @@ export const getPackagePolicyCreateCallback = (
|
|||
return newPackagePolicy;
|
||||
}
|
||||
|
||||
// In this callback we are handling an HTTP request to the fleet plugin. Since we use
|
||||
// code from the security_solution plugin to handle it (installPrepackagedRules),
|
||||
// we need to build the context that is native to security_solution and pass it there.
|
||||
const securitySolutionContext = await securitySolutionRequestContextFactory.create(
|
||||
context,
|
||||
request
|
||||
);
|
||||
|
||||
// perform these operations in parallel in order to help in not delaying the API response too much
|
||||
const [, manifestValue] = await Promise.all([
|
||||
// Install Detection Engine prepackaged rules
|
||||
exceptionsClient &&
|
||||
installPrepackagedRules({
|
||||
logger,
|
||||
appClientFactory,
|
||||
context,
|
||||
context: securitySolutionContext,
|
||||
request,
|
||||
securityStart,
|
||||
alerts,
|
||||
maxTimelineImportExportSize,
|
||||
prebuiltRulesFromFileSystem,
|
||||
prebuiltRulesFromSavedObjects,
|
||||
exceptionsClient,
|
||||
}),
|
||||
|
||||
|
|
|
@ -5,25 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaRequest, Logger, RequestHandlerContext } from 'kibana/server';
|
||||
import { KibanaRequest, Logger } from 'kibana/server';
|
||||
import { ExceptionListClient } from '../../../../lists/server';
|
||||
import { PluginStartContract as AlertsStartContract } from '../../../../alerting/server';
|
||||
import { SecurityPluginStart } from '../../../../security/server';
|
||||
import { AppClientFactory } from '../../client';
|
||||
import { createDetectionIndex } from '../../lib/detection_engine/routes/index/create_index_route';
|
||||
import { createPrepackagedRules } from '../../lib/detection_engine/routes/rules/add_prepackaged_rules_route';
|
||||
import { buildFrameworkRequest } from '../../lib/timeline/utils/common';
|
||||
import { SecuritySolutionApiRequestHandlerContext } from '../../types';
|
||||
|
||||
export interface InstallPrepackagedRulesProps {
|
||||
logger: Logger;
|
||||
appClientFactory: AppClientFactory;
|
||||
context: RequestHandlerContext;
|
||||
context: SecuritySolutionApiRequestHandlerContext;
|
||||
request: KibanaRequest;
|
||||
securityStart: SecurityPluginStart;
|
||||
alerts: AlertsStartContract;
|
||||
maxTimelineImportExportSize: number;
|
||||
prebuiltRulesFromFileSystem: boolean;
|
||||
prebuiltRulesFromSavedObjects: boolean;
|
||||
exceptionsClient: ExceptionListClient;
|
||||
}
|
||||
|
||||
|
@ -33,29 +26,14 @@ export interface InstallPrepackagedRulesProps {
|
|||
*/
|
||||
export const installPrepackagedRules = async ({
|
||||
logger,
|
||||
appClientFactory,
|
||||
context,
|
||||
request,
|
||||
securityStart,
|
||||
alerts,
|
||||
maxTimelineImportExportSize,
|
||||
prebuiltRulesFromFileSystem,
|
||||
prebuiltRulesFromSavedObjects,
|
||||
exceptionsClient,
|
||||
}: InstallPrepackagedRulesProps): Promise<void> => {
|
||||
// prep for detection rules creation
|
||||
const appClient = appClientFactory.create(request);
|
||||
|
||||
// This callback is called by fleet plugin.
|
||||
// It doesn't have access to SecuritySolutionRequestHandlerContext in runtime.
|
||||
// Muting the error to have green CI.
|
||||
// @ts-expect-error
|
||||
const frameworkRequest = await buildFrameworkRequest(context, securityStart, request);
|
||||
|
||||
// Create detection index & rules (if necessary). move past any failure, this is just a convenience
|
||||
try {
|
||||
// @ts-expect-error
|
||||
await createDetectionIndex(context, appClient);
|
||||
await createDetectionIndex(context);
|
||||
} catch (err) {
|
||||
if (err.statusCode !== 409) {
|
||||
// 409 -> detection index already exists, which is fine
|
||||
|
@ -68,14 +46,8 @@ export const installPrepackagedRules = async ({
|
|||
// this checks to make sure index exists first, safe to try in case of failure above
|
||||
// may be able to recover from minor errors
|
||||
await createPrepackagedRules(
|
||||
// @ts-expect-error
|
||||
context,
|
||||
appClient,
|
||||
alerts.getRulesClientWithRequest(request),
|
||||
frameworkRequest,
|
||||
maxTimelineImportExportSize,
|
||||
prebuiltRulesFromFileSystem,
|
||||
prebuiltRulesFromSavedObjects,
|
||||
exceptionsClient
|
||||
);
|
||||
} catch (err) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server';
|
||||
import { Plugin, PluginSetup, PluginStart } from './plugin';
|
||||
import { configSchema, ConfigType } from './config';
|
||||
import { configSchema, ConfigSchema, ConfigType } from './config';
|
||||
import { SIGNALS_INDEX_KEY } from '../common/constants';
|
||||
import { AppClient } from './types';
|
||||
|
||||
|
@ -15,7 +15,7 @@ export const plugin = (context: PluginInitializerContext) => {
|
|||
return new Plugin(context);
|
||||
};
|
||||
|
||||
export const config: PluginConfigDescriptor<ConfigType> = {
|
||||
export const config: PluginConfigDescriptor<ConfigSchema> = {
|
||||
exposeToBrowser: {
|
||||
enableExperimental: true,
|
||||
},
|
||||
|
@ -47,5 +47,5 @@ export const config: PluginConfigDescriptor<ConfigType> = {
|
|||
|
||||
export { ConfigType, Plugin, PluginSetup, PluginStart };
|
||||
export { AppClient };
|
||||
export type { AppRequestContext } from './types';
|
||||
export type { SecuritySolutionApiRequestHandlerContext } from './types';
|
||||
export { EndpointError } from './endpoint/errors';
|
||||
|
|
|
@ -5,34 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../../../../../common/constants';
|
||||
import { requestContextMock } from './request_context';
|
||||
import { serverMock } from './server';
|
||||
import { requestMock } from './request';
|
||||
import { responseMock } from './response_factory';
|
||||
import { ConfigType } from '../../../../config';
|
||||
import { UnderlyingLogClient } from '../../rule_execution_log/types';
|
||||
|
||||
export { requestMock, requestContextMock, responseMock, serverMock };
|
||||
|
||||
export const createMockConfig = (): ConfigType => ({
|
||||
[SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX,
|
||||
maxRuleImportExportSize: 10000,
|
||||
maxRuleImportPayloadBytes: 10485760,
|
||||
maxTimelineImportExportSize: 10000,
|
||||
maxTimelineImportPayloadBytes: 10485760,
|
||||
enableExperimental: [],
|
||||
endpointResultListDefaultFirstPageIndex: 0,
|
||||
endpointResultListDefaultPageSize: 10,
|
||||
packagerTaskInterval: '60s',
|
||||
alertMergeStrategy: 'missingFields',
|
||||
alertIgnoreFields: [],
|
||||
prebuiltRulesFromFileSystem: true,
|
||||
prebuiltRulesFromSavedObjects: false,
|
||||
ruleExecutionLog: {
|
||||
underlyingClient: UnderlyingLogClient.savedObjects,
|
||||
},
|
||||
});
|
||||
export { requestContextMock } from './request_context';
|
||||
export { requestMock } from './request';
|
||||
export { responseMock } from './response_factory';
|
||||
export { serverMock } from './server';
|
||||
export { configMock, createMockConfig } from '../../../../config.mock';
|
||||
|
||||
export const mockGetCurrentUser = {
|
||||
user: {
|
||||
|
|
|
@ -5,66 +5,103 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../../../../types';
|
||||
import {
|
||||
coreMock,
|
||||
elasticsearchServiceMock,
|
||||
savedObjectsClientMock,
|
||||
} from '../../../../../../../../src/core/server/mocks';
|
||||
import type { MockedKeys } from '@kbn/utility-types/jest';
|
||||
import { coreMock } from 'src/core/server/mocks';
|
||||
|
||||
import { ActionsApiRequestHandlerContext } from '../../../../../../actions/server';
|
||||
import { AlertingApiRequestHandlerContext } from '../../../../../../alerting/server';
|
||||
import { rulesClientMock } from '../../../../../../alerting/server/mocks';
|
||||
import { licensingMock } from '../../../../../../licensing/server/mocks';
|
||||
import { listMock } from '../../../../../../lists/server/mocks';
|
||||
import { ruleRegistryMocks } from '../../../../../../rule_registry/server/mocks';
|
||||
|
||||
import { siemMock } from '../../../../mocks';
|
||||
import { createMockConfig } from '../../../../config.mock';
|
||||
import { ruleExecutionLogClientMock } from '../../rule_execution_log/__mocks__/rule_execution_log_client';
|
||||
import { requestMock } from './request';
|
||||
import { internalFrameworkRequest } from '../../../framework';
|
||||
|
||||
const createMockClients = () => ({
|
||||
rulesClient: rulesClientMock.create(),
|
||||
licensing: { license: licensingMock.createLicenseMock() },
|
||||
clusterClient: elasticsearchServiceMock.createScopedClusterClient(),
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
ruleExecutionLogClient: ruleExecutionLogClientMock.create(),
|
||||
appClient: siemMock.createClient(),
|
||||
});
|
||||
import type {
|
||||
SecuritySolutionApiRequestHandlerContext,
|
||||
SecuritySolutionRequestHandlerContext,
|
||||
} from '../../../../types';
|
||||
|
||||
/**
|
||||
* Adds mocking to the interface so we don't have to cast everywhere
|
||||
*/
|
||||
type SecuritySolutionRequestHandlerContextMock = SecuritySolutionRequestHandlerContext & {
|
||||
core: {
|
||||
elasticsearch: {
|
||||
client: {
|
||||
asCurrentUser: {
|
||||
updateByQuery: jest.Mock;
|
||||
search: jest.Mock;
|
||||
security: {
|
||||
hasPrivileges: jest.Mock;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
const createMockClients = () => {
|
||||
const core = coreMock.createRequestHandlerContext();
|
||||
const license = licensingMock.createLicenseMock();
|
||||
|
||||
return {
|
||||
core,
|
||||
clusterClient: core.elasticsearch.client,
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
|
||||
licensing: {
|
||||
...licensingMock.createRequestHandlerContext({ license }),
|
||||
license,
|
||||
},
|
||||
lists: {
|
||||
listClient: listMock.getListClient(),
|
||||
exceptionListClient: listMock.getExceptionListClient(core.savedObjects.client),
|
||||
},
|
||||
rulesClient: rulesClientMock.create(),
|
||||
ruleDataService: ruleRegistryMocks.createRuleDataService(),
|
||||
|
||||
config: createMockConfig(),
|
||||
appClient: siemMock.createClient(),
|
||||
ruleExecutionLogClient: ruleExecutionLogClientMock.create(),
|
||||
};
|
||||
};
|
||||
|
||||
type MockClients = ReturnType<typeof createMockClients>;
|
||||
|
||||
type SecuritySolutionRequestHandlerContextMock =
|
||||
MockedKeys<SecuritySolutionRequestHandlerContext> & {
|
||||
core: MockClients['core'];
|
||||
};
|
||||
|
||||
const createRequestContextMock = (
|
||||
clients: ReturnType<typeof createMockClients> = createMockClients()
|
||||
clients: MockClients = createMockClients()
|
||||
): SecuritySolutionRequestHandlerContextMock => {
|
||||
const coreContext = coreMock.createRequestHandlerContext();
|
||||
return {
|
||||
alerting: { getRulesClient: jest.fn(() => clients.rulesClient) },
|
||||
core: {
|
||||
...coreContext,
|
||||
elasticsearch: {
|
||||
...coreContext.elasticsearch,
|
||||
client: clients.clusterClient,
|
||||
},
|
||||
savedObjects: { client: clients.savedObjectsClient },
|
||||
},
|
||||
core: clients.core,
|
||||
securitySolution: createSecuritySolutionRequestContextMock(clients),
|
||||
actions: {} as unknown as jest.Mocked<ActionsApiRequestHandlerContext>,
|
||||
alerting: {
|
||||
getRulesClient: jest.fn(() => clients.rulesClient),
|
||||
} as unknown as jest.Mocked<AlertingApiRequestHandlerContext>,
|
||||
licensing: clients.licensing,
|
||||
securitySolution: {
|
||||
getAppClient: jest.fn(() => clients.appClient),
|
||||
getExecutionLogClient: jest.fn(() => clients.ruleExecutionLogClient),
|
||||
getSpaceId: jest.fn(() => 'default'),
|
||||
lists: {
|
||||
getListClient: jest.fn(() => clients.lists.listClient),
|
||||
getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient),
|
||||
},
|
||||
} as unknown as SecuritySolutionRequestHandlerContextMock;
|
||||
};
|
||||
};
|
||||
|
||||
const createSecuritySolutionRequestContextMock = (
|
||||
clients: MockClients
|
||||
): jest.Mocked<SecuritySolutionApiRequestHandlerContext> => {
|
||||
const core = clients.core;
|
||||
const kibanaRequest = requestMock.create();
|
||||
|
||||
return {
|
||||
core,
|
||||
getConfig: jest.fn(() => clients.config),
|
||||
getFrameworkRequest: jest.fn(() => {
|
||||
return {
|
||||
...kibanaRequest.body,
|
||||
[internalFrameworkRequest]: kibanaRequest,
|
||||
context: { core },
|
||||
user: {
|
||||
username: 'mockUser',
|
||||
},
|
||||
};
|
||||
}),
|
||||
getAppClient: jest.fn(() => clients.appClient),
|
||||
getSpaceId: jest.fn(() => 'default'),
|
||||
getRuleDataService: jest.fn(() => clients.ruleDataService),
|
||||
getExecutionLogClient: jest.fn(() => clients.ruleExecutionLogClient),
|
||||
getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient),
|
||||
};
|
||||
};
|
||||
|
||||
const createTools = () => {
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'kibana/server';
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'src/core/server';
|
||||
|
||||
import { ActionResult } from '../../../../../../actions/server';
|
||||
import { SignalSearchResponse } from '../../signals/types';
|
||||
import {
|
||||
|
@ -562,6 +564,28 @@ export const getFindBulkResultStatus = (): FindBulkExecutionLogResponse => ({
|
|||
],
|
||||
});
|
||||
|
||||
export const getBasicEmptySearchResponse = (): estypes.SearchResponse<unknown> => ({
|
||||
took: 1,
|
||||
timed_out: false,
|
||||
_shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
|
||||
hits: {
|
||||
hits: [],
|
||||
total: { relation: 'eq', value: 0 },
|
||||
max_score: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export const getBasicNoShardsSearchResponse = (): estypes.SearchResponse<unknown> => ({
|
||||
took: 1,
|
||||
timed_out: false,
|
||||
_shards: { total: 0, successful: 0, skipped: 0, failed: 0 },
|
||||
hits: {
|
||||
hits: [],
|
||||
total: { relation: 'eq', value: 0 },
|
||||
max_score: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export const getEmptySignalsResponse = (): SignalSearchResponse => ({
|
||||
took: 1,
|
||||
timed_out: false,
|
||||
|
@ -588,7 +612,7 @@ export const getEmptyEqlSequencesResponse = (): EqlSearchResponse<unknown> => ({
|
|||
timed_out: false,
|
||||
});
|
||||
|
||||
export const getSuccessfulSignalUpdateResponse = () => ({
|
||||
export const getSuccessfulSignalUpdateResponse = (): estypes.UpdateByQueryResponse => ({
|
||||
took: 18,
|
||||
timed_out: false,
|
||||
total: 1,
|
||||
|
|
|
@ -16,9 +16,8 @@ import {
|
|||
createBootstrapIndex,
|
||||
} from '@kbn/securitysolution-es-utils';
|
||||
import type {
|
||||
AppClient,
|
||||
SecuritySolutionApiRequestHandlerContext,
|
||||
SecuritySolutionPluginRouter,
|
||||
SecuritySolutionRequestHandlerContext,
|
||||
} from '../../../../types';
|
||||
import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants';
|
||||
import { buildSiemResponse } from '../utils';
|
||||
|
@ -32,15 +31,8 @@ import signalsPolicy from './signals_policy.json';
|
|||
import { templateNeedsUpdate } from './check_template_version';
|
||||
import { getIndexVersion } from './get_index_version';
|
||||
import { isOutdated } from '../../migrations/helpers';
|
||||
import { RuleDataPluginService } from '../../../../../../rule_registry/server';
|
||||
import { ConfigType } from '../../../../config';
|
||||
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
|
||||
|
||||
export const createIndexRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
ruleDataService: RuleDataPluginService,
|
||||
config: ConfigType
|
||||
) => {
|
||||
export const createIndexRoute = (router: SecuritySolutionPluginRouter) => {
|
||||
router.post(
|
||||
{
|
||||
path: DETECTION_ENGINE_INDEX_URL,
|
||||
|
@ -51,14 +43,13 @@ export const createIndexRoute = (
|
|||
},
|
||||
async (context, request, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
const { ruleRegistryEnabled } = parseExperimentalConfigValue(config.enableExperimental);
|
||||
|
||||
try {
|
||||
const siemClient = context.securitySolution?.getAppClient();
|
||||
if (!siemClient) {
|
||||
return siemResponse.error({ statusCode: 404 });
|
||||
}
|
||||
await createDetectionIndex(context, siemClient, ruleDataService, ruleRegistryEnabled);
|
||||
await createDetectionIndex(context.securitySolution);
|
||||
return response.ok({ body: { acknowledged: true } });
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
|
@ -71,30 +62,18 @@ export const createIndexRoute = (
|
|||
);
|
||||
};
|
||||
|
||||
class CreateIndexError extends Error {
|
||||
public readonly statusCode: number;
|
||||
constructor(message: string, statusCode: number) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
export const createDetectionIndex = async (
|
||||
context: SecuritySolutionRequestHandlerContext,
|
||||
siemClient: AppClient,
|
||||
ruleDataService: RuleDataPluginService,
|
||||
ruleRegistryEnabled: boolean
|
||||
context: SecuritySolutionApiRequestHandlerContext
|
||||
): Promise<void> => {
|
||||
const config = context.getConfig();
|
||||
const esClient = context.core.elasticsearch.client.asCurrentUser;
|
||||
const spaceId = siemClient.getSpaceId();
|
||||
|
||||
if (!siemClient) {
|
||||
throw new CreateIndexError('', 404);
|
||||
}
|
||||
|
||||
const siemClient = context.getAppClient();
|
||||
const spaceId = context.getSpaceId();
|
||||
const index = siemClient.getSignalsIndex();
|
||||
|
||||
const indexExists = await getIndexExists(esClient, index);
|
||||
const { ruleRegistryEnabled } = config.experimentalFeatures;
|
||||
|
||||
// If using the rule registry implementation, we don't want to create new .siem-signals indices -
|
||||
// only create/update resources if there are existing indices
|
||||
if (ruleRegistryEnabled && !indexExists) {
|
||||
|
@ -106,7 +85,10 @@ export const createDetectionIndex = async (
|
|||
if (!policyExists) {
|
||||
await setPolicy(esClient, index, signalsPolicy);
|
||||
}
|
||||
|
||||
const ruleDataService = context.getRuleDataService();
|
||||
const aadIndexAliasName = ruleDataService.getResourceName(`security.alerts-${spaceId}`);
|
||||
|
||||
if (await templateNeedsUpdate({ alias: index, esClient })) {
|
||||
await esClient.indices.putIndexTemplate({
|
||||
name: index,
|
||||
|
|
|
@ -19,9 +19,9 @@ describe('read_privileges route', () => {
|
|||
server = serverMock.create();
|
||||
({ context } = requestContextMock.createTools());
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({
|
||||
body: getMockPrivilegesResult(),
|
||||
});
|
||||
context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(getMockPrivilegesResult())
|
||||
);
|
||||
|
||||
readPrivilegesRoute(server.router, true);
|
||||
});
|
||||
|
|
|
@ -10,14 +10,13 @@ import {
|
|||
addPrepackagedRulesRequest,
|
||||
getFindResultWithSingleHit,
|
||||
getAlertMock,
|
||||
getBasicEmptySearchResponse,
|
||||
getBasicNoShardsSearchResponse,
|
||||
} from '../__mocks__/request_responses';
|
||||
import { requestContextMock, serverMock, createMockConfig, mockGetCurrentUser } from '../__mocks__';
|
||||
import { configMock, requestContextMock, serverMock } from '../__mocks__';
|
||||
import { AddPrepackagedRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema';
|
||||
import { SecurityPluginSetup } from '../../../../../../security/server';
|
||||
import { addPrepackedRulesRoute, createPrepackagedRules } from './add_prepackaged_rules_route';
|
||||
import { listMock } from '../../../../../../lists/server/mocks';
|
||||
import { siemMock } from '../../../../mocks';
|
||||
import { FrameworkRequest } from '../../../framework';
|
||||
import { ExceptionListClient } from '../../../../../../lists/server';
|
||||
import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
|
@ -76,25 +75,21 @@ describe.each([
|
|||
['Legacy', false],
|
||||
['RAC', true],
|
||||
])('add_prepackaged_rules_route - %s', (_, isRuleRegistryEnabled) => {
|
||||
const siemMockClient = siemMock.createClient();
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { clients, context } = requestContextMock.createTools();
|
||||
let securitySetup: SecurityPluginSetup;
|
||||
let mockExceptionsClient: ExceptionListClient;
|
||||
const testif = isRuleRegistryEnabled ? test.skip : test;
|
||||
const defaultConfig = context.securitySolution.getConfig();
|
||||
|
||||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
({ clients, context } = requestContextMock.createTools());
|
||||
securitySetup = {
|
||||
authc: {
|
||||
getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser),
|
||||
},
|
||||
authz: {},
|
||||
} as unknown as SecurityPluginSetup;
|
||||
|
||||
mockExceptionsClient = listMock.getExceptionListClient();
|
||||
|
||||
context.securitySolution.getConfig.mockImplementation(() =>
|
||||
configMock.withRuleRegistryEnabled(defaultConfig, isRuleRegistryEnabled)
|
||||
);
|
||||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled));
|
||||
clients.rulesClient.update.mockResolvedValue(
|
||||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
|
@ -110,9 +105,9 @@ describe.each([
|
|||
});
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
|
||||
);
|
||||
addPrepackedRulesRoute(server.router, createMockConfig(), securitySetup, isRuleRegistryEnabled);
|
||||
addPrepackedRulesRoute(server.router);
|
||||
});
|
||||
|
||||
describe('status codes', () => {
|
||||
|
@ -138,7 +133,9 @@ describe.each([
|
|||
test('it returns a 400 if the index does not exist when rule registry not enabled', async () => {
|
||||
const request = addPrepackagedRulesRequest();
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(
|
||||
getBasicNoShardsSearchResponse()
|
||||
)
|
||||
);
|
||||
const response = await server.inject(request, context);
|
||||
|
||||
|
@ -302,39 +299,22 @@ describe.each([
|
|||
|
||||
describe('createPrepackagedRules', () => {
|
||||
test('uses exception lists client from context when available', async () => {
|
||||
context.lists = {
|
||||
getExceptionListClient: jest.fn(),
|
||||
getListClient: jest.fn(),
|
||||
};
|
||||
const config = createMockConfig();
|
||||
|
||||
await createPrepackagedRules(
|
||||
context,
|
||||
siemMockClient,
|
||||
context.securitySolution,
|
||||
clients.rulesClient,
|
||||
{} as FrameworkRequest,
|
||||
1200,
|
||||
config.prebuiltRulesFromFileSystem,
|
||||
config.prebuiltRulesFromSavedObjects,
|
||||
mockExceptionsClient
|
||||
);
|
||||
|
||||
expect(mockExceptionsClient.createEndpointList).not.toHaveBeenCalled();
|
||||
expect(context.lists?.getExceptionListClient).toHaveBeenCalled();
|
||||
expect(context.securitySolution.getExceptionListClient).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('uses passed in exceptions list client when lists client not available in context', async () => {
|
||||
const { lists, ...myContext } = context;
|
||||
const config = createMockConfig();
|
||||
context.securitySolution.getExceptionListClient.mockImplementation(() => null);
|
||||
|
||||
await createPrepackagedRules(
|
||||
myContext,
|
||||
siemMockClient,
|
||||
context.securitySolution,
|
||||
clients.rulesClient,
|
||||
{} as FrameworkRequest,
|
||||
1200,
|
||||
config.prebuiltRulesFromFileSystem,
|
||||
config.prebuiltRulesFromSavedObjects,
|
||||
mockExceptionsClient
|
||||
);
|
||||
|
||||
|
|
|
@ -9,9 +9,8 @@ import moment from 'moment';
|
|||
import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils';
|
||||
import { validate } from '@kbn/securitysolution-io-ts-utils';
|
||||
import type {
|
||||
AppClient,
|
||||
SecuritySolutionApiRequestHandlerContext,
|
||||
SecuritySolutionPluginRouter,
|
||||
SecuritySolutionRequestHandlerContext,
|
||||
} from '../../../../types';
|
||||
|
||||
import {
|
||||
|
@ -21,10 +20,6 @@ import {
|
|||
import { importTimelineResultSchema } from '../../../../../common/types/timeline';
|
||||
import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants';
|
||||
|
||||
import { ConfigType } from '../../../../config';
|
||||
import { SetupPlugins } from '../../../../plugin';
|
||||
import { buildFrameworkRequest } from '../../../timeline/utils/common';
|
||||
|
||||
import { getLatestPrepackagedRules } from '../../rules/get_prepackaged_rules';
|
||||
import { installPrepackagedRules } from '../../rules/install_prepacked_rules';
|
||||
import { updatePrepackagedRules } from '../../rules/update_prepacked_rules';
|
||||
|
@ -35,17 +30,11 @@ import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_
|
|||
|
||||
import { buildSiemResponse } from '../utils';
|
||||
import { RulesClient } from '../../../../../../alerting/server';
|
||||
import { FrameworkRequest } from '../../../framework';
|
||||
|
||||
import { ExceptionListClient } from '../../../../../../lists/server';
|
||||
import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines';
|
||||
|
||||
export const addPrepackedRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
config: ConfigType,
|
||||
security: SetupPlugins['security'],
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
export const addPrepackedRulesRoute = (router: SecuritySolutionPluginRouter) => {
|
||||
router.put(
|
||||
{
|
||||
path: DETECTION_ENGINE_PREPACKAGED_URL,
|
||||
|
@ -63,7 +52,6 @@ export const addPrepackedRulesRoute = (
|
|||
},
|
||||
async (context, _, response) => {
|
||||
const siemResponse = buildSiemResponse(response);
|
||||
const frameworkRequest = await buildFrameworkRequest(context, security, _);
|
||||
|
||||
try {
|
||||
const rulesClient = context.alerting?.getRulesClient();
|
||||
|
@ -74,15 +62,9 @@ export const addPrepackedRulesRoute = (
|
|||
}
|
||||
|
||||
const validated = await createPrepackagedRules(
|
||||
context,
|
||||
siemClient,
|
||||
context.securitySolution,
|
||||
rulesClient,
|
||||
frameworkRequest,
|
||||
config.maxTimelineImportExportSize,
|
||||
config.prebuiltRulesFromFileSystem,
|
||||
config.prebuiltRulesFromSavedObjects,
|
||||
undefined,
|
||||
isRuleRegistryEnabled
|
||||
undefined
|
||||
);
|
||||
return response.ok({ body: validated ?? {} });
|
||||
} catch (err) {
|
||||
|
@ -105,22 +87,26 @@ class PrepackagedRulesError extends Error {
|
|||
}
|
||||
|
||||
export const createPrepackagedRules = async (
|
||||
context: SecuritySolutionRequestHandlerContext,
|
||||
siemClient: AppClient,
|
||||
context: SecuritySolutionApiRequestHandlerContext,
|
||||
rulesClient: RulesClient,
|
||||
frameworkRequest: FrameworkRequest,
|
||||
maxTimelineImportExportSize: ConfigType['maxTimelineImportExportSize'],
|
||||
prebuiltRulesFromFileSystem: ConfigType['prebuiltRulesFromFileSystem'],
|
||||
prebuiltRulesFromSavedObjects: ConfigType['prebuiltRulesFromSavedObjects'],
|
||||
exceptionsClient?: ExceptionListClient,
|
||||
isRuleRegistryEnabled?: boolean | undefined
|
||||
exceptionsClient?: ExceptionListClient
|
||||
): Promise<PrePackagedRulesAndTimelinesSchema | null> => {
|
||||
const config = context.getConfig();
|
||||
const frameworkRequest = context.getFrameworkRequest();
|
||||
const esClient = context.core.elasticsearch.client;
|
||||
const savedObjectsClient = context.core.savedObjects.client;
|
||||
const exceptionsListClient =
|
||||
context.lists != null ? context.lists.getExceptionListClient() : exceptionsClient;
|
||||
const siemClient = context.getAppClient();
|
||||
const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient;
|
||||
const ruleAssetsClient = ruleAssetSavedObjectsClientFactory(savedObjectsClient);
|
||||
const ruleStatusClient = context.securitySolution.getExecutionLogClient();
|
||||
const ruleStatusClient = context.getExecutionLogClient();
|
||||
|
||||
const {
|
||||
maxTimelineImportExportSize,
|
||||
prebuiltRulesFromFileSystem,
|
||||
prebuiltRulesFromSavedObjects,
|
||||
experimentalFeatures: { ruleRegistryEnabled },
|
||||
} = config;
|
||||
|
||||
if (!siemClient || !rulesClient) {
|
||||
throw new PrepackagedRulesError('', 404);
|
||||
}
|
||||
|
@ -137,12 +123,12 @@ export const createPrepackagedRules = async (
|
|||
);
|
||||
const prepackagedRules = await getExistingPrepackagedRules({
|
||||
rulesClient,
|
||||
isRuleRegistryEnabled: isRuleRegistryEnabled ?? false,
|
||||
isRuleRegistryEnabled: ruleRegistryEnabled,
|
||||
});
|
||||
const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules);
|
||||
const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules);
|
||||
const signalsIndex = siemClient.getSignalsIndex();
|
||||
if (!isRuleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) {
|
||||
if (!ruleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) {
|
||||
const signalsIndexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex);
|
||||
if (!signalsIndexExists) {
|
||||
throw new PrepackagedRulesError(
|
||||
|
@ -153,12 +139,7 @@ export const createPrepackagedRules = async (
|
|||
}
|
||||
|
||||
await Promise.all(
|
||||
installPrepackagedRules(
|
||||
rulesClient,
|
||||
rulesToInstall,
|
||||
signalsIndex,
|
||||
isRuleRegistryEnabled ?? false
|
||||
)
|
||||
installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex, ruleRegistryEnabled)
|
||||
);
|
||||
const timeline = await installPrepackagedTimelines(
|
||||
maxTimelineImportExportSize,
|
||||
|
@ -172,11 +153,11 @@ export const createPrepackagedRules = async (
|
|||
await updatePrepackagedRules(
|
||||
rulesClient,
|
||||
savedObjectsClient,
|
||||
context.securitySolution.getSpaceId(),
|
||||
context.getSpaceId(),
|
||||
ruleStatusClient,
|
||||
rulesToUpdate,
|
||||
signalsIndex,
|
||||
isRuleRegistryEnabled ?? false
|
||||
ruleRegistryEnabled
|
||||
);
|
||||
|
||||
const prepackagedRulesOutput: PrePackagedRulesAndTimelinesSchema = {
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
getEmptyFindResult,
|
||||
getAlertMock,
|
||||
createBulkMlRuleRequest,
|
||||
getBasicEmptySearchResponse,
|
||||
getBasicNoShardsSearchResponse,
|
||||
} from '../__mocks__/request_responses';
|
||||
import { requestContextMock, serverMock, requestMock } from '../__mocks__';
|
||||
import { createRulesBulkRoute } from './create_rules_bulk_route';
|
||||
|
@ -43,7 +45,7 @@ describe.each([
|
|||
); // successful creation
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
|
||||
);
|
||||
createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
@ -93,7 +95,9 @@ describe.each([
|
|||
|
||||
test('returns an error object if the index does not exist when rule registry not enabled', async () => {
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(
|
||||
getBasicNoShardsSearchResponse()
|
||||
)
|
||||
);
|
||||
const response = await server.inject(getReadBulkRequest(), context);
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
getRuleExecutionStatuses,
|
||||
getFindResultWithSingleHit,
|
||||
createMlRuleRequest,
|
||||
getBasicEmptySearchResponse,
|
||||
getBasicNoShardsSearchResponse,
|
||||
} from '../__mocks__/request_responses';
|
||||
import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks';
|
||||
import { buildMlAuthz } from '../../../machine_learning/authz';
|
||||
|
@ -44,7 +46,7 @@ describe.each([
|
|||
clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); // needed to transform: ;
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
|
||||
);
|
||||
createRulesRoute(server.router, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
@ -103,7 +105,9 @@ describe.each([
|
|||
describe('unhappy paths', () => {
|
||||
test('it returns a 400 if the index does not exist when rule registry not enabled', async () => {
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(
|
||||
getBasicNoShardsSearchResponse()
|
||||
)
|
||||
);
|
||||
const response = await server.inject(getCreateRequest(), context);
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
getEmptyFindResult,
|
||||
getAlertMock,
|
||||
getFindResultWithSingleHit,
|
||||
getBasicEmptySearchResponse,
|
||||
getBasicNoShardsSearchResponse,
|
||||
} from '../__mocks__/request_responses';
|
||||
import { createMockConfig, requestContextMock, serverMock, requestMock } from '../__mocks__';
|
||||
import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks';
|
||||
|
@ -52,7 +54,7 @@ describe.each([
|
|||
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
|
||||
);
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
|
||||
);
|
||||
importRulesRoute(server.router, config, ml, isRuleRegistryEnabled);
|
||||
});
|
||||
|
@ -133,7 +135,9 @@ describe.each([
|
|||
test('returns an error if the index does not exist when rule registry not enabled', async () => {
|
||||
clients.appClient.getSignalsIndex.mockReturnValue('mockSignalsIndex');
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } })
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(
|
||||
getBasicNoShardsSearchResponse()
|
||||
)
|
||||
);
|
||||
const response = await server.inject(request, context);
|
||||
expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400);
|
||||
|
|
|
@ -26,11 +26,13 @@ describe('set signal status', () => {
|
|||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
({ context } = requestContextMock.createTools());
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
|
||||
context.core.elasticsearch.client.asCurrentUser.updateByQuery.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(
|
||||
getSuccessfulSignalUpdateResponse()
|
||||
)
|
||||
);
|
||||
|
||||
setSignalsStatusRoute(server.router);
|
||||
});
|
||||
|
||||
|
|
|
@ -5,14 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaRequest } from '../../../../../../src/core/server';
|
||||
import { KibanaRequest, RequestHandlerContext } from '../../../../../../src/core/server';
|
||||
import { AuthenticatedUser } from '../../../../security/common/model';
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../../types';
|
||||
|
||||
export const internalFrameworkRequest = Symbol('internalFrameworkRequest');
|
||||
|
||||
export interface FrameworkRequest extends Pick<KibanaRequest, 'body'> {
|
||||
[internalFrameworkRequest]: KibanaRequest;
|
||||
context: SecuritySolutionRequestHandlerContext;
|
||||
context: RequestHandlerContext;
|
||||
user: AuthenticatedUser | null;
|
||||
}
|
||||
|
|
|
@ -32,17 +32,12 @@ const notes = [
|
|||
const existingNoteIds = undefined;
|
||||
const isImmutable = true;
|
||||
|
||||
jest.mock('moment', () => {
|
||||
const mockMoment = {
|
||||
toISOString: jest
|
||||
.fn()
|
||||
.mockReturnValueOnce('2020-11-03T11:37:31.655Z')
|
||||
.mockReturnValue('2020-11-04T11:37:31.655Z'),
|
||||
subtract: jest.fn(),
|
||||
};
|
||||
mockMoment.subtract.mockReturnValue(mockMoment);
|
||||
return jest.fn().mockReturnValue(mockMoment);
|
||||
});
|
||||
// System under test uses moment.js under the hood, so we need to mock time.
|
||||
// Mocking moment via jest.mock('moment') breaks imports of moment in other files.
|
||||
// Instead, we simply mock Date.now() via jest API and moment starts using it.
|
||||
// This affects all the tests in this file and doesn't affect tests in other files.
|
||||
// https://jestjs.io/docs/timer-mocks
|
||||
jest.useFakeTimers('modern').setSystemTime(new Date('2020-11-04T11:37:31.655Z'));
|
||||
|
||||
jest.mock('../../../saved_object/timelines', () => ({
|
||||
persistTimeline: jest.fn().mockResolvedValue({
|
||||
|
|
|
@ -12,15 +12,14 @@ import { Readable } from 'stream';
|
|||
import { createListStream } from '@kbn/utils';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import { KibanaRequest } from 'src/core/server';
|
||||
import { KibanaRequest, RequestHandlerContext } from 'src/core/server';
|
||||
import { formatErrors } from '@kbn/securitysolution-io-ts-utils';
|
||||
import { SetupPlugins, StartPlugins } from '../../../plugin';
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../../../types';
|
||||
|
||||
import { FrameworkRequest } from '../../framework';
|
||||
|
||||
export const buildFrameworkRequest = async (
|
||||
context: SecuritySolutionRequestHandlerContext,
|
||||
context: RequestHandlerContext,
|
||||
security: StartPlugins['security'] | SetupPlugins['security'] | undefined,
|
||||
request: KibanaRequest
|
||||
): Promise<FrameworkRequest> => {
|
||||
|
@ -30,7 +29,7 @@ export const buildFrameworkRequest = async (
|
|||
return set<FrameworkRequest>(
|
||||
'user',
|
||||
user,
|
||||
set<KibanaRequest & { context: SecuritySolutionRequestHandlerContext }>(
|
||||
set<KibanaRequest & { context: RequestHandlerContext }>(
|
||||
'context.core.savedObjects.client',
|
||||
savedObjectsClient,
|
||||
request
|
||||
|
|
|
@ -9,45 +9,15 @@ import { Observable } from 'rxjs';
|
|||
import LRU from 'lru-cache';
|
||||
import { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Logger,
|
||||
Plugin as IPlugin,
|
||||
PluginInitializerContext,
|
||||
SavedObjectsClient,
|
||||
} from '../../../../src/core/server';
|
||||
import {
|
||||
PluginSetup as DataPluginSetup,
|
||||
PluginStart as DataPluginStart,
|
||||
} from '../../../../src/plugins/data/server';
|
||||
import {
|
||||
UsageCollectionSetup,
|
||||
UsageCounter,
|
||||
} from '../../../../src/plugins/usage_collection/server';
|
||||
import {
|
||||
PluginSetupContract as AlertingSetup,
|
||||
PluginStartContract as AlertPluginStartContract,
|
||||
} from '../../alerting/server';
|
||||
import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map';
|
||||
import { Logger, SavedObjectsClient } from '../../../../src/core/server';
|
||||
import { UsageCounter } from '../../../../src/plugins/usage_collection/server';
|
||||
|
||||
import { PluginStartContract as CasesPluginStartContract } from '../../cases/server';
|
||||
import { ECS_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets';
|
||||
import { SecurityPluginSetup as SecuritySetup, SecurityPluginStart } from '../../security/server';
|
||||
import {
|
||||
IRuleDataClient,
|
||||
RuleRegistryPluginSetupContract,
|
||||
RuleRegistryPluginStartContract,
|
||||
Dataset,
|
||||
} from '../../rule_registry/server';
|
||||
import { PluginSetupContract as FeaturesSetup } from '../../features/server';
|
||||
import { MlPluginSetup as MlSetup } from '../../ml/server';
|
||||
import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map';
|
||||
import { IRuleDataClient, Dataset } from '../../rule_registry/server';
|
||||
import { ListPluginSetup } from '../../lists/server';
|
||||
import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../encrypted_saved_objects/server';
|
||||
import { SpacesPluginSetup as SpacesSetup } from '../../spaces/server';
|
||||
import { ILicense, LicensingPluginStart } from '../../licensing/server';
|
||||
import { FleetStartContract } from '../../fleet/server';
|
||||
import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server';
|
||||
import { ILicense } from '../../licensing/server';
|
||||
|
||||
import {
|
||||
createEqlAlertType,
|
||||
createIndicatorMatchAlertType,
|
||||
|
@ -70,7 +40,6 @@ import {
|
|||
SIGNALS_ID,
|
||||
LEGACY_NOTIFICATIONS_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
DEFAULT_SPACE_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
EQL_RULE_TYPE_ID,
|
||||
|
@ -89,18 +58,13 @@ import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps';
|
|||
import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution';
|
||||
import { TelemetryEventsSender } from './lib/telemetry/sender';
|
||||
import { TelemetryReceiver } from './lib/telemetry/receiver';
|
||||
import {
|
||||
TelemetryPluginStart,
|
||||
TelemetryPluginSetup,
|
||||
} from '../../../../src/plugins/telemetry/server';
|
||||
import { licenseService } from './lib/license';
|
||||
import { PolicyWatcher } from './endpoint/lib/policy/license_watch';
|
||||
import { parseExperimentalConfigValue } from '../common/experimental_features';
|
||||
import { migrateArtifactsToFleet } from './endpoint/lib/artifacts/migrate_artifacts_to_fleet';
|
||||
import aadFieldConversion from './lib/detection_engine/routes/index/signal_aad_mapping.json';
|
||||
import { alertsFieldMap } from './lib/detection_engine/rule_types/field_maps/alerts';
|
||||
import { rulesFieldMap } from './lib/detection_engine/rule_types/field_maps/rules';
|
||||
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
|
||||
import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider';
|
||||
import { getKibanaPrivilegesFeaturePrivileges, getCasesKibanaFeature } from './features';
|
||||
import { EndpointMetadataService } from './endpoint/services/metadata';
|
||||
import { CreateRuleOptions } from './lib/detection_engine/rule_types/types';
|
||||
|
@ -109,50 +73,28 @@ import { legacyRulesNotificationAlertType } from './lib/detection_engine/notific
|
|||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyIsNotificationAlertExecutor } from './lib/detection_engine/notifications/legacy_types';
|
||||
import { createSecurityRuleTypeWrapper } from './lib/detection_engine/rule_types/create_security_rule_type_wrapper';
|
||||
import { IEventLogClientService, IEventLogService } from '../../event_log/server';
|
||||
import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider';
|
||||
|
||||
export interface SetupPlugins {
|
||||
alerting: AlertingSetup;
|
||||
data: DataPluginSetup;
|
||||
encryptedSavedObjects?: EncryptedSavedObjectsSetup;
|
||||
eventLog: IEventLogService;
|
||||
features: FeaturesSetup;
|
||||
lists?: ListPluginSetup;
|
||||
ml?: MlSetup;
|
||||
ruleRegistry: RuleRegistryPluginSetupContract;
|
||||
security?: SecuritySetup;
|
||||
spaces?: SpacesSetup;
|
||||
taskManager?: TaskManagerSetupContract;
|
||||
telemetry?: TelemetryPluginSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
}
|
||||
import { RequestContextFactory } from './request_context_factory';
|
||||
|
||||
export interface StartPlugins {
|
||||
alerting: AlertPluginStartContract;
|
||||
cases?: CasesPluginStartContract;
|
||||
data: DataPluginStart;
|
||||
eventLog: IEventLogClientService;
|
||||
fleet?: FleetStartContract;
|
||||
licensing: LicensingPluginStart;
|
||||
ruleRegistry: RuleRegistryPluginStartContract;
|
||||
security: SecurityPluginStart;
|
||||
taskManager?: TaskManagerStartContract;
|
||||
telemetry?: TelemetryPluginStart;
|
||||
}
|
||||
import type {
|
||||
ISecuritySolutionPlugin,
|
||||
SecuritySolutionPluginSetupDependencies,
|
||||
SecuritySolutionPluginStartDependencies,
|
||||
SecuritySolutionPluginCoreSetupDependencies,
|
||||
SecuritySolutionPluginCoreStartDependencies,
|
||||
SecuritySolutionPluginSetup,
|
||||
SecuritySolutionPluginStart,
|
||||
PluginInitializerContext,
|
||||
} from './plugin_contract';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface PluginSetup {}
|
||||
export { SetupPlugins, StartPlugins, PluginSetup, PluginStart } from './plugin_contract';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface PluginStart {}
|
||||
|
||||
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
|
||||
private readonly logger: Logger;
|
||||
export class Plugin implements ISecuritySolutionPlugin {
|
||||
private readonly pluginContext: PluginInitializerContext;
|
||||
private readonly config: ConfigType;
|
||||
private context: PluginInitializerContext;
|
||||
private appClientFactory: AppClientFactory;
|
||||
private setupPlugins?: SetupPlugins;
|
||||
private readonly logger: Logger;
|
||||
private readonly appClientFactory: AppClientFactory;
|
||||
|
||||
private readonly endpointAppContextService = new EndpointAppContextService();
|
||||
private readonly telemetryReceiver: TelemetryReceiver;
|
||||
private readonly telemetryEventsSender: TelemetryEventsSender;
|
||||
|
@ -167,10 +109,11 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
private telemetryUsageCounter?: UsageCounter;
|
||||
|
||||
constructor(context: PluginInitializerContext) {
|
||||
this.context = context;
|
||||
this.logger = context.logger.get();
|
||||
this.pluginContext = context;
|
||||
this.config = createConfig(context);
|
||||
this.logger = context.logger.get();
|
||||
this.appClientFactory = new AppClientFactory();
|
||||
|
||||
// Cache up to three artifacts with a max retention of 5 mins each
|
||||
this.artifactsCache = new LRU<string, Buffer>({ max: 3, maxAge: 1000 * 60 * 5 });
|
||||
this.telemetryEventsSender = new TelemetryEventsSender(this.logger);
|
||||
|
@ -179,26 +122,47 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
this.logger.debug('plugin initialized');
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup<StartPlugins, PluginStart>, plugins: SetupPlugins) {
|
||||
public setup(
|
||||
core: SecuritySolutionPluginCoreSetupDependencies,
|
||||
plugins: SecuritySolutionPluginSetupDependencies
|
||||
): SecuritySolutionPluginSetup {
|
||||
this.logger.debug('plugin setup');
|
||||
this.setupPlugins = plugins;
|
||||
|
||||
const config = this.config;
|
||||
const globalConfig = this.context.config.legacy.get();
|
||||
const { pluginContext, config, logger, appClientFactory } = this;
|
||||
const experimentalFeatures = config.experimentalFeatures;
|
||||
|
||||
appClientFactory.setup({
|
||||
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
|
||||
config,
|
||||
});
|
||||
|
||||
const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental);
|
||||
initSavedObjects(core.savedObjects);
|
||||
initUiSettings(core.uiSettings, experimentalFeatures);
|
||||
|
||||
const eventLogService = plugins.eventLog;
|
||||
registerEventLogProvider(eventLogService);
|
||||
|
||||
const requestContextFactory = new RequestContextFactory({ config, core, plugins });
|
||||
const router = core.http.createRouter<SecuritySolutionRequestHandlerContext>();
|
||||
core.http.registerRouteHandlerContext<SecuritySolutionRequestHandlerContext, typeof APP_ID>(
|
||||
APP_ID,
|
||||
(context, request) => requestContextFactory.create(context, request)
|
||||
);
|
||||
|
||||
const endpointContext: EndpointAppContext = {
|
||||
logFactory: this.context.logger,
|
||||
logFactory: pluginContext.logger,
|
||||
service: this.endpointAppContextService,
|
||||
config: (): Promise<ConfigType> => Promise.resolve(config),
|
||||
experimentalFeatures,
|
||||
};
|
||||
|
||||
this.endpointAppContextService.setup({
|
||||
securitySolutionRequestContextFactory: requestContextFactory,
|
||||
});
|
||||
|
||||
initUsageCollectors({
|
||||
core,
|
||||
kibanaIndex: globalConfig.kibana.index,
|
||||
kibanaIndex: config.kibanaIndex,
|
||||
signalsIndex: config.signalsIndex,
|
||||
ml: plugins.ml,
|
||||
usageCollection: plugins.usageCollection,
|
||||
|
@ -206,29 +170,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
|
||||
this.telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID);
|
||||
|
||||
const eventLogService = plugins.eventLog;
|
||||
registerEventLogProvider(eventLogService);
|
||||
|
||||
const router = core.http.createRouter<SecuritySolutionRequestHandlerContext>();
|
||||
core.http.registerRouteHandlerContext<SecuritySolutionRequestHandlerContext, typeof APP_ID>(
|
||||
APP_ID,
|
||||
(context, request, response) => ({
|
||||
getAppClient: () => this.appClientFactory.create(request),
|
||||
getSpaceId: () => plugins.spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID,
|
||||
getExecutionLogClient: () =>
|
||||
new RuleExecutionLogClient({
|
||||
savedObjectsClient: context.core.savedObjects.client,
|
||||
eventLogService,
|
||||
underlyingClient: config.ruleExecutionLog.underlyingClient,
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
this.appClientFactory.setup({
|
||||
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
|
||||
config,
|
||||
});
|
||||
|
||||
// TODO: Once we are past experimental phase this check can be removed along with legacy registration of rules
|
||||
const isRuleRegistryEnabled = experimentalFeatures.ruleRegistryEnabled;
|
||||
|
||||
|
@ -265,9 +206,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
// Register rule types via rule-registry
|
||||
const createRuleOptions: CreateRuleOptions = {
|
||||
experimentalFeatures,
|
||||
logger: this.logger,
|
||||
logger,
|
||||
ml: plugins.ml,
|
||||
version: this.context.env.packageInfo.version,
|
||||
version: pluginContext.env.packageInfo.version,
|
||||
};
|
||||
|
||||
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
|
||||
|
@ -278,19 +219,15 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
eventLogService,
|
||||
});
|
||||
|
||||
this.setupPlugins.alerting.registerType(
|
||||
securityRuleTypeWrapper(createEqlAlertType(createRuleOptions))
|
||||
);
|
||||
this.setupPlugins.alerting.registerType(
|
||||
plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(createRuleOptions)));
|
||||
plugins.alerting.registerType(
|
||||
securityRuleTypeWrapper(createIndicatorMatchAlertType(createRuleOptions))
|
||||
);
|
||||
this.setupPlugins.alerting.registerType(
|
||||
securityRuleTypeWrapper(createMlAlertType(createRuleOptions))
|
||||
);
|
||||
this.setupPlugins.alerting.registerType(
|
||||
plugins.alerting.registerType(securityRuleTypeWrapper(createMlAlertType(createRuleOptions)));
|
||||
plugins.alerting.registerType(
|
||||
securityRuleTypeWrapper(createQueryAlertType(createRuleOptions))
|
||||
);
|
||||
this.setupPlugins.alerting.registerType(
|
||||
plugins.alerting.registerType(
|
||||
securityRuleTypeWrapper(createThresholdAlertType(createRuleOptions))
|
||||
);
|
||||
}
|
||||
|
@ -302,8 +239,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
plugins.encryptedSavedObjects?.canEncrypt === true,
|
||||
plugins.security,
|
||||
plugins.ml,
|
||||
ruleDataService,
|
||||
this.logger,
|
||||
logger,
|
||||
isRuleRegistryEnabled
|
||||
);
|
||||
registerEndpointRoutes(router, endpointContext);
|
||||
|
@ -329,27 +265,25 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
plugins.features.registerKibanaFeature(getCasesKibanaFeature());
|
||||
|
||||
// Continue to register legacy rules against alerting client exposed through rule-registry
|
||||
if (this.setupPlugins.alerting != null) {
|
||||
if (plugins.alerting != null) {
|
||||
const signalRuleType = signalRulesAlertType({
|
||||
logger: this.logger,
|
||||
logger,
|
||||
eventsTelemetry: this.telemetryEventsSender,
|
||||
version: this.context.env.packageInfo.version,
|
||||
version: pluginContext.env.packageInfo.version,
|
||||
ml: plugins.ml,
|
||||
lists: plugins.lists,
|
||||
config: this.config,
|
||||
config,
|
||||
experimentalFeatures,
|
||||
eventLogService,
|
||||
});
|
||||
const ruleNotificationType = legacyRulesNotificationAlertType({
|
||||
logger: this.logger,
|
||||
});
|
||||
const ruleNotificationType = legacyRulesNotificationAlertType({ logger });
|
||||
|
||||
if (isAlertExecutor(signalRuleType)) {
|
||||
this.setupPlugins.alerting.registerType(signalRuleType);
|
||||
plugins.alerting.registerType(signalRuleType);
|
||||
}
|
||||
|
||||
if (legacyIsNotificationAlertExecutor(ruleNotificationType)) {
|
||||
this.setupPlugins.alerting.registerType(ruleNotificationType);
|
||||
plugins.alerting.registerType(ruleNotificationType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,10 +328,14 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
return {};
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: StartPlugins) {
|
||||
public start(
|
||||
core: SecuritySolutionPluginCoreStartDependencies,
|
||||
plugins: SecuritySolutionPluginStartDependencies
|
||||
): SecuritySolutionPluginStart {
|
||||
const { config, logger, appClientFactory } = this;
|
||||
|
||||
const savedObjectsClient = new SavedObjectsClient(core.savedObjects.createInternalRepository());
|
||||
const registerIngestCallback = plugins.fleet?.registerExternalCallback;
|
||||
const logger = this.logger;
|
||||
let manifestManager: ManifestManager | undefined;
|
||||
|
||||
this.licensing$ = plugins.licensing.license$;
|
||||
|
@ -405,7 +343,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
if (this.lists && plugins.taskManager && plugins.fleet) {
|
||||
// Exceptions, Artifacts and Manifests start
|
||||
const taskManager = plugins.taskManager;
|
||||
const experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental);
|
||||
const exceptionListClient = this.lists.getExceptionListClient(savedObjectsClient, 'kibana');
|
||||
const artifactClient = new EndpointArtifactClient(
|
||||
plugins.fleet.createArtifactsClient('endpoint')
|
||||
|
@ -418,7 +355,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
packagePolicyService: plugins.fleet.packagePolicyService,
|
||||
logger,
|
||||
cache: this.artifactsCache,
|
||||
experimentalFeatures,
|
||||
experimentalFeatures: config.experimentalFeatures,
|
||||
});
|
||||
|
||||
// Migrate artifacts to fleet and then start the minifest task after that is done
|
||||
|
@ -463,7 +400,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
plugins.fleet?.agentPolicyService!,
|
||||
logger
|
||||
),
|
||||
appClientFactory: this.appClientFactory,
|
||||
appClientFactory,
|
||||
security: plugins.security,
|
||||
alerting: plugins.alerting,
|
||||
config: this.config,
|
||||
|
@ -475,10 +412,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
exceptionListsClient: exceptionListClient,
|
||||
});
|
||||
|
||||
const globalConfig = this.context.config.legacy.get();
|
||||
this.telemetryReceiver.start(
|
||||
core,
|
||||
globalConfig.kibana.index,
|
||||
config.kibanaIndex,
|
||||
this.endpointAppContextService,
|
||||
exceptionListClient
|
||||
);
|
||||
|
|
102
x-pack/plugins/security_solution/server/plugin_contract.ts
Normal file
102
x-pack/plugins/security_solution/server/plugin_contract.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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 {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
Plugin,
|
||||
PluginInitializerContext,
|
||||
} from '../../../../src/core/server';
|
||||
import {
|
||||
PluginSetup as DataPluginSetup,
|
||||
PluginStart as DataPluginStart,
|
||||
} from '../../../../src/plugins/data/server';
|
||||
import { UsageCollectionSetup as UsageCollectionPluginSetup } from '../../../../src/plugins/usage_collection/server';
|
||||
import {
|
||||
PluginSetupContract as AlertingPluginSetup,
|
||||
PluginStartContract as AlertingPluginStart,
|
||||
} from '../../alerting/server';
|
||||
import { PluginStartContract as CasesPluginStart } from '../../cases/server';
|
||||
import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server';
|
||||
import { IEventLogClientService, IEventLogService } from '../../event_log/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
|
||||
import { FleetStartContract as FleetPluginStart } from '../../fleet/server';
|
||||
import { LicensingPluginStart } from '../../licensing/server';
|
||||
import { ListPluginSetup } from '../../lists/server';
|
||||
import { MlPluginSetup } from '../../ml/server';
|
||||
import {
|
||||
RuleRegistryPluginSetupContract as RuleRegistryPluginSetup,
|
||||
RuleRegistryPluginStartContract as RuleRegistryPluginStart,
|
||||
} from '../../rule_registry/server';
|
||||
import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server';
|
||||
import { SpacesPluginSetup } from '../../spaces/server';
|
||||
import {
|
||||
TaskManagerSetupContract as TaskManagerPluginSetup,
|
||||
TaskManagerStartContract as TaskManagerPluginStart,
|
||||
} from '../../task_manager/server';
|
||||
import {
|
||||
TelemetryPluginStart,
|
||||
TelemetryPluginSetup,
|
||||
} from '../../../../src/plugins/telemetry/server';
|
||||
|
||||
export interface SecuritySolutionPluginSetupDependencies {
|
||||
alerting: AlertingPluginSetup;
|
||||
data: DataPluginSetup;
|
||||
encryptedSavedObjects?: EncryptedSavedObjectsPluginSetup;
|
||||
eventLog: IEventLogService;
|
||||
features: FeaturesPluginSetup;
|
||||
lists?: ListPluginSetup;
|
||||
ml?: MlPluginSetup;
|
||||
ruleRegistry: RuleRegistryPluginSetup;
|
||||
security?: SecurityPluginSetup;
|
||||
spaces?: SpacesPluginSetup;
|
||||
taskManager?: TaskManagerPluginSetup;
|
||||
telemetry?: TelemetryPluginSetup;
|
||||
usageCollection?: UsageCollectionPluginSetup;
|
||||
}
|
||||
|
||||
export interface SecuritySolutionPluginStartDependencies {
|
||||
alerting: AlertingPluginStart;
|
||||
cases?: CasesPluginStart;
|
||||
data: DataPluginStart;
|
||||
eventLog: IEventLogClientService;
|
||||
fleet?: FleetPluginStart;
|
||||
licensing: LicensingPluginStart;
|
||||
ruleRegistry: RuleRegistryPluginStart;
|
||||
security: SecurityPluginStart;
|
||||
taskManager?: TaskManagerPluginStart;
|
||||
telemetry?: TelemetryPluginStart;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface SecuritySolutionPluginSetup {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface SecuritySolutionPluginStart {}
|
||||
|
||||
export type SecuritySolutionPluginCoreSetupDependencies = CoreSetup<
|
||||
SecuritySolutionPluginStartDependencies,
|
||||
SecuritySolutionPluginStart
|
||||
>;
|
||||
|
||||
export type SecuritySolutionPluginCoreStartDependencies = CoreStart;
|
||||
|
||||
export type ISecuritySolutionPlugin = Plugin<
|
||||
SecuritySolutionPluginSetup,
|
||||
SecuritySolutionPluginStart,
|
||||
SecuritySolutionPluginSetupDependencies,
|
||||
SecuritySolutionPluginStartDependencies
|
||||
>;
|
||||
|
||||
export type {
|
||||
PluginInitializerContext,
|
||||
// Legacy type identifiers left for compatibility with the rest of the code:
|
||||
SecuritySolutionPluginSetupDependencies as SetupPlugins,
|
||||
SecuritySolutionPluginStartDependencies as StartPlugins,
|
||||
SecuritySolutionPluginSetup as PluginSetup,
|
||||
SecuritySolutionPluginStart as PluginStart,
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { requestContextMock } from './lib/detection_engine/routes/__mocks__';
|
||||
import { IRequestContextFactory } from './request_context_factory';
|
||||
|
||||
export const requestContextFactoryMock = {
|
||||
create: (): jest.Mocked<IRequestContextFactory> => ({
|
||||
create: jest.fn((context, request) => {
|
||||
const fullContext = requestContextMock.create();
|
||||
const securitySolutionContext = fullContext.securitySolution;
|
||||
return Promise.resolve(securitySolutionContext);
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
export const RequestContextFactoryMock = jest
|
||||
.fn<jest.Mocked<IRequestContextFactory>, []>()
|
||||
.mockImplementation(requestContextFactoryMock.create);
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 { KibanaRequest, RequestHandlerContext } from 'kibana/server';
|
||||
import { ExceptionListClient } from '../../lists/server';
|
||||
|
||||
import { DEFAULT_SPACE_ID } from '../common/constants';
|
||||
import { AppClientFactory } from './client';
|
||||
import { ConfigType } from './config';
|
||||
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
|
||||
import { buildFrameworkRequest } from './lib/timeline/utils/common';
|
||||
import {
|
||||
SecuritySolutionPluginCoreSetupDependencies,
|
||||
SecuritySolutionPluginSetupDependencies,
|
||||
} from './plugin_contract';
|
||||
import { SecuritySolutionApiRequestHandlerContext } from './types';
|
||||
|
||||
export interface IRequestContextFactory {
|
||||
create(
|
||||
context: RequestHandlerContext,
|
||||
request: KibanaRequest
|
||||
): Promise<SecuritySolutionApiRequestHandlerContext>;
|
||||
}
|
||||
|
||||
interface ConstructorOptions {
|
||||
config: ConfigType;
|
||||
core: SecuritySolutionPluginCoreSetupDependencies;
|
||||
plugins: SecuritySolutionPluginSetupDependencies;
|
||||
}
|
||||
|
||||
export class RequestContextFactory implements IRequestContextFactory {
|
||||
private readonly appClientFactory: AppClientFactory;
|
||||
|
||||
constructor(private readonly options: ConstructorOptions) {
|
||||
this.appClientFactory = new AppClientFactory();
|
||||
}
|
||||
|
||||
public async create(
|
||||
context: RequestHandlerContext,
|
||||
request: KibanaRequest
|
||||
): Promise<SecuritySolutionApiRequestHandlerContext> {
|
||||
const { options, appClientFactory } = this;
|
||||
const { config, plugins } = options;
|
||||
const { lists, ruleRegistry, security, spaces } = plugins;
|
||||
|
||||
appClientFactory.setup({
|
||||
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
|
||||
config,
|
||||
});
|
||||
|
||||
const frameworkRequest = await buildFrameworkRequest(context, security, request);
|
||||
|
||||
return {
|
||||
core: context.core,
|
||||
|
||||
getConfig: () => config,
|
||||
|
||||
getFrameworkRequest: () => frameworkRequest,
|
||||
|
||||
getAppClient: () => appClientFactory.create(request),
|
||||
|
||||
getSpaceId: () => spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID,
|
||||
|
||||
getRuleDataService: () => ruleRegistry.ruleDataService,
|
||||
|
||||
getExecutionLogClient: () =>
|
||||
new RuleExecutionLogClient({
|
||||
savedObjectsClient: context.core.savedObjects.client,
|
||||
eventLogService: plugins.eventLog,
|
||||
underlyingClient: config.ruleExecutionLog.underlyingClient,
|
||||
}),
|
||||
|
||||
getExceptionListClient: () => {
|
||||
if (!lists) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const username = security?.authc.getCurrentUser(request)?.username || 'elastic';
|
||||
return new ExceptionListClient({
|
||||
savedObjectsClient: context.core.savedObjects.client,
|
||||
user: username,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { Logger } from 'src/core/server';
|
||||
import { RuleDataPluginService } from '../../../rule_registry/server';
|
||||
|
||||
import { SecuritySolutionPluginRouter } from '../types';
|
||||
|
||||
|
@ -67,7 +66,6 @@ export const initRoutes = (
|
|||
hasEncryptionKey: boolean,
|
||||
security: SetupPlugins['security'],
|
||||
ml: SetupPlugins['ml'],
|
||||
ruleDataService: RuleDataPluginService,
|
||||
logger: Logger,
|
||||
isRuleRegistryEnabled: boolean
|
||||
) => {
|
||||
|
@ -85,7 +83,7 @@ export const initRoutes = (
|
|||
|
||||
// TODO: pass isRuleRegistryEnabled to all relevant routes
|
||||
|
||||
addPrepackedRulesRoute(router, config, security, isRuleRegistryEnabled);
|
||||
addPrepackedRulesRoute(router);
|
||||
getPrepackagedRulesStatusRoute(router, config, security, isRuleRegistryEnabled);
|
||||
createRulesBulkRoute(router, ml, isRuleRegistryEnabled);
|
||||
updateRulesBulkRoute(router, ml, isRuleRegistryEnabled);
|
||||
|
@ -127,7 +125,7 @@ export const initRoutes = (
|
|||
|
||||
// Detection Engine index routes that have the REST endpoints of /api/detection_engine/index
|
||||
// All REST index creation, policy management for spaces
|
||||
createIndexRoute(router, ruleDataService, config);
|
||||
createIndexRoute(router);
|
||||
readIndexRoute(router, config);
|
||||
deleteIndexRoute(router);
|
||||
|
||||
|
|
|
@ -6,28 +6,35 @@
|
|||
*/
|
||||
|
||||
import type { IRouter, RequestHandlerContext } from 'src/core/server';
|
||||
import type { ListsApiRequestHandlerContext } from '../../lists/server';
|
||||
import type { LicensingApiRequestHandlerContext } from '../../licensing/server';
|
||||
import type { ActionsApiRequestHandlerContext } from '../../actions/server';
|
||||
import type { AlertingApiRequestHandlerContext } from '../../alerting/server';
|
||||
import type { LicensingApiRequestHandlerContext } from '../../licensing/server';
|
||||
import type { ListsApiRequestHandlerContext, ExceptionListClient } from '../../lists/server';
|
||||
import type { IRuleDataService } from '../../rule_registry/server';
|
||||
|
||||
import { AppClient } from './client';
|
||||
import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client';
|
||||
import type { ActionsApiRequestHandlerContext } from '../../actions/server';
|
||||
import { ConfigType } from './config';
|
||||
import { IRuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/types';
|
||||
import { FrameworkRequest } from './lib/framework';
|
||||
|
||||
export { AppClient };
|
||||
|
||||
export interface AppRequestContext {
|
||||
export interface SecuritySolutionApiRequestHandlerContext extends RequestHandlerContext {
|
||||
getConfig: () => ConfigType;
|
||||
getFrameworkRequest: () => FrameworkRequest;
|
||||
getAppClient: () => AppClient;
|
||||
getSpaceId: () => string;
|
||||
getExecutionLogClient: () => RuleExecutionLogClient;
|
||||
getRuleDataService: () => IRuleDataService;
|
||||
getExecutionLogClient: () => IRuleExecutionLogClient;
|
||||
getExceptionListClient: () => ExceptionListClient | null;
|
||||
}
|
||||
|
||||
export type SecuritySolutionRequestHandlerContext = RequestHandlerContext & {
|
||||
securitySolution: AppRequestContext;
|
||||
licensing: LicensingApiRequestHandlerContext;
|
||||
alerting: AlertingApiRequestHandlerContext;
|
||||
export interface SecuritySolutionRequestHandlerContext extends RequestHandlerContext {
|
||||
securitySolution: SecuritySolutionApiRequestHandlerContext;
|
||||
actions: ActionsApiRequestHandlerContext;
|
||||
alerting: AlertingApiRequestHandlerContext;
|
||||
licensing: LicensingApiRequestHandlerContext;
|
||||
lists?: ListsApiRequestHandlerContext;
|
||||
};
|
||||
}
|
||||
|
||||
export type SecuritySolutionPluginRouter = IRouter<SecuritySolutionRequestHandlerContext>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue