diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index d73ed716e6b19..b0730d1b762d6 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -44,6 +44,7 @@ readonly links: { readonly aggs: { readonly date_histogram: string; readonly date_range: string; + readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; @@ -104,5 +105,11 @@ readonly links: { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; + readonly apis: Record; + readonly observability: Record; + readonly alerting: Record; + readonly maps: Record; + readonly monitoring: Record; + readonly security: Record; }; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 7aa170eef9b50..2d06876f42b6a 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Record<string, string>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Record<string, string>;
} | | diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 2d91eb07c5236..8c16c76c62569 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -195,11 +195,11 @@ a| `xpack.reporting.capture.browser` Defaults to `false`. a| `xpack.reporting.capture.browser` -.chromium.proxy.server` +`.chromium.proxy.server` | The uri for the proxy server. Providing the username and password for the proxy server via the uri is not supported. a| `xpack.reporting.capture.browser` -.chromium.proxy.bypass` +`.chromium.proxy.bypass` | An array of hosts that should not go through the proxy server and should use a direct connection instead. Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601". diff --git a/src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap b/packages/kbn-std/src/__snapshots__/ensure_no_unsafe_properties.test.ts.snap similarity index 100% rename from src/core/server/http/prototype_pollution/__snapshots__/validate_object.test.ts.snap rename to packages/kbn-std/src/__snapshots__/ensure_no_unsafe_properties.test.ts.snap diff --git a/src/core/server/http/prototype_pollution/validate_object.test.ts b/packages/kbn-std/src/ensure_no_unsafe_properties.test.ts similarity index 89% rename from src/core/server/http/prototype_pollution/validate_object.test.ts rename to packages/kbn-std/src/ensure_no_unsafe_properties.test.ts index 23d6c4ae3b49f..c12626b8d777e 100644 --- a/src/core/server/http/prototype_pollution/validate_object.test.ts +++ b/packages/kbn-std/src/ensure_no_unsafe_properties.test.ts @@ -17,14 +17,14 @@ * under the License. */ -import { validateObject } from './validate_object'; +import { ensureNoUnsafeProperties } from './ensure_no_unsafe_properties'; test(`fails on circular references`, () => { const foo: Record = {}; foo.myself = foo; expect(() => - validateObject({ + ensureNoUnsafeProperties({ payload: foo, }) ).toThrowErrorMatchingInlineSnapshot(`"circular reference detected"`); @@ -57,7 +57,7 @@ test(`fails on circular references`, () => { [property]: value, }; test(`can submit ${JSON.stringify(obj)}`, () => { - expect(() => validateObject(obj)).not.toThrowError(); + expect(() => ensureNoUnsafeProperties(obj)).not.toThrowError(); }); }); }); @@ -74,6 +74,6 @@ test(`fails on circular references`, () => { JSON.parse(`{ "foo": { "bar": { "constructor": { "prototype" : null } } } }`), ].forEach((value) => { test(`can't submit ${JSON.stringify(value)}`, () => { - expect(() => validateObject(value)).toThrowErrorMatchingSnapshot(); + expect(() => ensureNoUnsafeProperties(value)).toThrowErrorMatchingSnapshot(); }); }); diff --git a/src/core/server/http/prototype_pollution/validate_object.ts b/packages/kbn-std/src/ensure_no_unsafe_properties.ts similarity index 97% rename from src/core/server/http/prototype_pollution/validate_object.ts rename to packages/kbn-std/src/ensure_no_unsafe_properties.ts index cab6ce295ce92..47cbea5ecf3ee 100644 --- a/src/core/server/http/prototype_pollution/validate_object.ts +++ b/packages/kbn-std/src/ensure_no_unsafe_properties.ts @@ -31,7 +31,7 @@ const hasOwnProperty = (obj: any, property: string) => const isObject = (obj: any) => typeof obj === 'object' && obj !== null; // we're using a stack instead of recursion so we aren't limited by the call stack -export function validateObject(obj: any) { +export function ensureNoUnsafeProperties(obj: any) { if (!isObject(obj)) { return; } diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts index c111428017539..a5b5088f9105f 100644 --- a/packages/kbn-std/src/index.ts +++ b/packages/kbn-std/src/index.ts @@ -27,4 +27,5 @@ export { withTimeout } from './promise'; export { isRelativeUrl, modifyUrl, getUrlOrigin, URLMeaningfulParts } from './url'; export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; +export { ensureNoUnsafeProperties } from './ensure_no_unsafe_properties'; export * from './rxjs_7'; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 2893f4c9a9878..d2039677b53cc 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -72,6 +72,7 @@ export class DocLinksService { aggs: { date_histogram: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-datehistogram-aggregation.html`, date_range: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-daterange-aggregation.html`, + date_format_pattern: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-daterange-aggregation.html#date-format-pattern`, filter: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-filter-aggregation.html`, filters: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-filters-aggregation.html`, geohash_grid: `${ELASTICSEARCH_DOCS}search-aggregations-bucket-geohashgrid-aggregation.html`, @@ -107,6 +108,7 @@ export class DocLinksService { painless: `${ELASTICSEARCH_DOCS}modules-scripting-painless.html`, painlessApi: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/painless/${DOC_LINK_VERSION}/painless-api-reference.html`, painlessSyntax: `${ELASTICSEARCH_DOCS}modules-scripting-painless-syntax.html`, + painlessLanguage: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/painless/${DOC_LINK_VERSION}/painless-lang-spec.html`, luceneExpressions: `${ELASTICSEARCH_DOCS}modules-scripting-expression.html`, }, indexPatterns: { @@ -150,7 +152,7 @@ export class DocLinksService { classificationAucRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-class-aucroc`, }, transforms: { - guide: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/transforms.html`, + guide: `${ELASTICSEARCH_DOCS}transforms.html`, }, visualize: { guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/visualize.html`, @@ -158,6 +160,36 @@ export class DocLinksService { lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`, maps: `${ELASTIC_WEBSITE_URL}maps`, }, + observability: { + guide: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/index.html`, + }, + alerting: { + guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/managing-alerts-and-actions.html`, + actionTypes: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/action-types.html`, + }, + maps: { + guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-maps.html`, + }, + monitoring: { + alertsKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html`, + monitorElasticsearch: `${ELASTICSEARCH_DOCS}configuring-metricbeat.html`, + monitorKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/monitoring-metricbeat.html`, + }, + security: { + elasticsearchSettings: `${ELASTICSEARCH_DOCS}security-settings.html`, + kibanaTLS: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/configuring-tls.html`, + kibanaPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-privileges.html`, + indicesPrivileges: `${ELASTICSEARCH_DOCS}security-privileges.html#privileges-list-indices`, + mappingRoles: `${ELASTICSEARCH_DOCS}mapping-roles.html`, + }, + apis: { + createIndex: `${ELASTICSEARCH_DOCS}indices-create-index.html`, + createSnapshotLifecylePolicy: `${ELASTICSEARCH_DOCS}slm-api-put-policy.html`, + createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`, + createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`, + createPipeline: `${ELASTICSEARCH_DOCS}put-pipeline-api.html`, + openIndex: `${ELASTICSEARCH_DOCS}indices-open-close.html`, + }, }, }); } @@ -204,6 +236,7 @@ export interface DocLinksStart { readonly aggs: { readonly date_histogram: string; readonly date_range: string; + readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; @@ -264,5 +297,11 @@ export interface DocLinksStart { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; + readonly apis: Record; + readonly observability: Record; + readonly alerting: Record; + readonly maps: Record; + readonly monitoring: Record; + readonly security: Record; }; } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 0303eb62b6419..4d00ebb213564 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -523,6 +523,7 @@ export interface DocLinksStart { readonly aggs: { readonly date_histogram: string; readonly date_range: string; + readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; @@ -583,6 +584,12 @@ export interface DocLinksStart { readonly ml: Record; readonly transforms: Record; readonly visualize: Record; + readonly apis: Record; + readonly observability: Record; + readonly alerting: Record; + readonly maps: Record; + readonly monitoring: Record; + readonly security: Record; }; } diff --git a/src/core/server/http/http_tools.ts b/src/core/server/http/http_tools.ts index 8bec26f31fa26..f09f3dc2730a1 100644 --- a/src/core/server/http/http_tools.ts +++ b/src/core/server/http/http_tools.ts @@ -29,8 +29,8 @@ import Hoek from '@hapi/hoek'; import type { ServerOptions as TLSOptions } from 'https'; import type { ValidationError } from 'joi'; import uuid from 'uuid'; +import { ensureNoUnsafeProperties } from '@kbn/std'; import { HttpConfig } from './http_config'; -import { validateObject } from './prototype_pollution'; const corsAllowedHeaders = ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf']; /** @@ -69,7 +69,7 @@ export function getServerOptions(config: HttpConfig, { configureTLS = true } = { // This is a default payload validation which applies to all LP routes which do not specify their own // `validate.payload` handler, in order to reduce the likelyhood of prototype pollution vulnerabilities. // (All NP routes are already required to specify their own validation in order to access the payload) - payload: (value) => Promise.resolve(validateObject(value)), + payload: (value) => Promise.resolve(ensureNoUnsafeProperties(value)), }, }, state: { diff --git a/src/core/server/http/prototype_pollution/index.ts b/src/core/server/http/prototype_pollution/index.ts deleted file mode 100644 index e1a33ffba155e..0000000000000 --- a/src/core/server/http/prototype_pollution/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { validateObject } from './validate_object'; diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_type_timeseries/server/routes/vis.ts index bba086720da0a..3ed9aaaaea226 100644 --- a/src/plugins/vis_type_timeseries/server/routes/vis.ts +++ b/src/plugins/vis_type_timeseries/server/routes/vis.ts @@ -19,6 +19,7 @@ import { IRouter, KibanaRequest } from 'kibana/server'; import { schema } from '@kbn/config-schema'; +import { ensureNoUnsafeProperties } from '@kbn/std'; import { getVisData, GetVisDataOptions } from '../lib/get_vis_data'; import { visPayloadSchema } from '../../common/vis_schema'; import { ROUTES } from '../../common/constants'; @@ -40,6 +41,14 @@ export const visDataRoutes = ( }, }, async (requestContext, request, response) => { + try { + ensureNoUnsafeProperties(request.body); + } catch (error) { + return response.badRequest({ + body: error.message, + }); + } + try { visPayloadSchema.validate(request.body); } catch (error) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx index cac133acd4d2d..19f6be3db51b0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/layout.tsx @@ -51,8 +51,8 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ -

- {from === 'edit' ? ( +

+ {from === 'edit' || from === 'package-edit' ? ( -

+

); - }, [from, packageInfo]); + }, [dataTestSubj, from, packageInfo]); const pageDescription = useMemo(() => { return from === 'edit' || from === 'package-edit' ? ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index de768e92efb3d..97a63de4f7ba2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1876,7 +1876,7 @@ describe('IndexPattern Data Source suggestions', () => { expect(suggestions.length).toBe(6); }); - it('returns an only metric version of a given table', () => { + it('returns an only metric version of a given table, but does not include current state as reduced', () => { const initialState = testInitialState(); const state: IndexPatternPrivateState = { indexPatternRefs: [], @@ -1953,6 +1953,21 @@ describe('IndexPattern Data Source suggestions', () => { }; const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).not.toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reduced', + columns: [ + expect.objectContaining({ + operation: expect.objectContaining({ label: 'field2' }), + }), + expect.objectContaining({ + operation: expect.objectContaining({ label: 'Average of field1' }), + }), + ], + }), + }) + ); expect(suggestions).toContainEqual( expect.objectContaining({ table: expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index 969324c67e909..9d7328b4dca37 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -583,8 +583,9 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer columnOrder: [...bucketedColumns, ...availableMetricColumns], }; - if (availableReferenceColumns.length) { - // Don't remove buckets when dealing with any refs. This can break refs. + if (availableBucketedColumns.length <= 1 || availableReferenceColumns.length) { + // Don't simplify when dealing with single-bucket table. Also don't break + // reference-based columns by removing buckets. return []; } else if (availableMetricColumns.length > 1) { return [{ ...layer, columnOrder: [...bucketedColumns, availableMetricColumns[0]] }]; @@ -597,7 +598,6 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer availableReferenceColumns.length ? [] : availableMetricColumns.map((columnId) => { - // build suggestions with only metrics return { ...layer, columnOrder: [columnId] }; }) ) @@ -606,8 +606,7 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer state, layerId, updatedLayer, - changeType: - layer.columnOrder.length === updatedLayer.columnOrder.length ? 'unchanged' : 'reduced', + changeType: 'reduced', label: updatedLayer.columnOrder.length === 1 ? getMetricSuggestionTitle(updatedLayer, availableMetricColumns.length === 1) diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 56bc315d6e9e4..b3259b19cf2c0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -232,8 +232,8 @@ const createPolicy = async ( ): Promise => { // Create Agent Policy first const newAgentPolicyData: CreateAgentPolicyRequest['body'] = { - name: `Policy for ${policyName}`, - description: '', + name: `Policy for ${policyName} (${Math.random().toString(36).substr(2, 5)})`, + description: `Policy created with endpoint data generator (${policyName})`, namespace: 'default', }; let agentPolicy; @@ -368,12 +368,7 @@ const fleetEnrollAgentForHost = async ( }, }, host: { - architecture: 'x86_64', - hostname: endpointHost.host, - name: endpointHost.host, - id: '1c032ec0-3a94-4d54-9ad2-c5610c0eaba4', - ip: ['fe80::703b:b9e6:887d:7f5/64', '10.0.2.15/24', '::1/128', '127.0.0.1/8'], - mac: ['08:00:27:d8:c5:c0'], + ...endpointHost.host, }, os: { family: 'windows', diff --git a/x-pack/plugins/security_solution/public/common/store/actions.ts b/x-pack/plugins/security_solution/public/common/store/actions.ts index f4134b5c47c2c..dc3125d5db25b 100644 --- a/x-pack/plugins/security_solution/public/common/store/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/actions.ts @@ -5,7 +5,6 @@ */ import { EndpointAction } from '../../management/pages/endpoint_hosts/store/action'; -import { PolicyListAction } from '../../management/pages/policy/store/policy_list'; import { PolicyDetailsAction } from '../../management/pages/policy/store/policy_details'; import { TrustedAppsPageAction } from '../../management/pages/trusted_apps/store/action'; @@ -18,6 +17,5 @@ import { RoutingAction } from './routing'; export type AppAction = | EndpointAction | RoutingAction - | PolicyListAction | PolicyDetailsAction | TrustedAppsPageAction; diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index cd4ce743bb701..66b6fefac607c 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -18,8 +18,6 @@ export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_ROUTING_ROOT_P // --[ STORE ]--------------------------------------------------------------------------- /** The SIEM global store namespace where the management state will be mounted */ export const MANAGEMENT_STORE_GLOBAL_NAMESPACE: ManagementStoreGlobalNamespace = 'management'; -/** Namespace within the Management state where policy list state is maintained */ -export const MANAGEMENT_STORE_POLICY_LIST_NAMESPACE = 'policyList'; /** Namespace within the Management state where policy details state is maintained */ export const MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE = 'policyDetails'; /** Namespace within the Management state where endpoint-host state is maintained */ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts index 1e3a92e6ec135..789e8a1d1d5f4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts @@ -26,7 +26,7 @@ import { } from '../../../../common/store/test_utils'; import { getEndpointListPath } from '../../../common/routing'; -jest.mock('../../policy/store/policy_list/services/ingest', () => ({ +jest.mock('../../policy/store/services/ingest', () => ({ sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), sendGetEndpointSecurityPackage: () => Promise.resolve({}), })); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index d19b3a0ce4177..8c1841aace273 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -23,7 +23,7 @@ import { endpointListReducer } from './reducer'; import { endpointMiddlewareFactory } from './middleware'; import { getEndpointListPath } from '../../../common/routing'; -jest.mock('../../policy/store/policy_list/services/ingest', () => ({ +jest.mock('../../policy/store/services/ingest', () => ({ sendGetAgentConfigList: () => Promise.resolve({ items: [] }), sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), sendGetEndpointSecurityPackage: () => Promise.resolve({}), diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index a78bd5fde3216..3a51846de096f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -25,7 +25,7 @@ import { sendGetEndpointSecurityPackage, sendGetAgentPolicyList, sendGetFleetAgentsWithEndpoint, -} from '../../policy/store/policy_list/services/ingest'; +} from '../../policy/store/services/ingest'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../fleet/common'; import { metadataCurrentIndexPattern } from '../../../../../common/endpoint/constants'; import { IIndexPattern, Query } from '../../../../../../../../src/plugins/data/public'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts index 5b14b7d658965..7c7337933483a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts @@ -19,7 +19,7 @@ import { INGEST_API_EPM_PACKAGES, INGEST_API_PACKAGE_POLICIES, INGEST_API_FLEET_AGENTS, -} from '../../policy/store/policy_list/services/ingest'; +} from '../../policy/store/services/ingest'; import { GetAgentPoliciesResponse, GetAgentPoliciesResponseItem, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts index 07ab38fd52776..312b83201045c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/policy_response_friendly_names.ts @@ -17,7 +17,7 @@ const policyResponses: Array<[string, string]> = [ 'configure_elasticsearch_connection', i18n.translate( 'xpack.securitySolution.endpoint.details.policyResponse.configure_elasticsearch_connection', - { defaultMessage: 'Configure Elastic Search Connection' } + { defaultMessage: 'Configure Elasticsearch Connection' } ), ], [ @@ -162,7 +162,7 @@ const policyResponses: Array<[string, string]> = [ 'read_elasticsearch_config', i18n.translate( 'xpack.securitySolution.endpoint.details.policyResponse.read_elasticsearch_config', - { defaultMessage: 'Read ElasticSearch Config' } + { defaultMessage: 'Read Elasticsearch Config' } ), ], [ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 487f5ddab5504..6446fefff1559 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -25,7 +25,7 @@ import { } from '../../../../../common/endpoint/types'; import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; import { POLICY_STATUS_TO_HEALTH_COLOR, POLICY_STATUS_TO_TEXT } from './host_constants'; -import { mockPolicyResultList } from '../../policy/store/policy_list/test_mock_utils'; +import { mockPolicyResultList } from '../../policy/store/test_mock_utils'; // not sure why this can't be imported from '../../../../common/mock/formatted_relative'; // but sure enough it needs to be inline in this one file @@ -39,8 +39,8 @@ jest.mock('@kbn/i18n/react', () => { }; }); jest.mock('../../../../common/components/link_to'); -jest.mock('../../policy/store/policy_list/services/ingest', () => { - const originalModule = jest.requireActual('../../policy/store/policy_list/services/ingest'); +jest.mock('../../policy/store/services/ingest', () => { + const originalModule = jest.requireActual('../../policy/store/services/ingest'); return { ...originalModule, sendGetEndpointSecurityPackage: () => Promise.resolve({}), diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts index 2f9f0d6723749..cc286b4c478d3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts @@ -18,7 +18,7 @@ import { sendGetPackagePolicy, sendGetFleetAgentStatusForPolicy, sendPutPackagePolicy, -} from '../policy_list/services/ingest'; +} from '../services/ingest'; import { NewPolicyData, PolicyData } from '../../../../../../common/endpoint/types'; import { ImmutableMiddlewareFactory } from '../../../../../common/store'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/action.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/action.ts deleted file mode 100644 index cfd053948922b..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/action.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PolicyData } from '../../../../../../common/endpoint/types'; -import { ServerApiError } from '../../../../../common/types'; -import { GetAgentStatusResponse, GetPackagesResponse } from '../../../../../../../fleet/common'; - -interface ServerReturnedPolicyListData { - type: 'serverReturnedPolicyListData'; - payload: { - policyItems: PolicyData[]; - total: number; - pageSize: number; - pageIndex: number; - }; -} - -interface ServerFailedToReturnPolicyListData { - type: 'serverFailedToReturnPolicyListData'; - payload: ServerApiError; -} - -interface UserClickedPolicyListDeleteButton { - type: 'userClickedPolicyListDeleteButton'; - payload: { policyId: string }; -} - -interface UserOpenedPolicyListDeleteModal { - type: 'userOpenedPolicyListDeleteModal'; - payload: { agentPolicyId: string }; -} - -interface ServerDeletedPolicyFailure { - type: 'serverDeletedPolicyFailure'; - payload: ServerApiError; -} - -interface ServerDeletedPolicy { - type: 'serverDeletedPolicy'; - payload: { id: string; success: boolean }; -} - -interface ServerReturnedPolicyAgentsSummaryForDeleteFailure { - type: 'serverReturnedPolicyAgentsSummaryForDeleteFailure'; - payload: ServerApiError; -} - -interface ServerReturnedPolicyAgentsSummaryForDelete { - type: 'serverReturnedPolicyAgentsSummaryForDelete'; - payload: { agentStatusSummary: GetAgentStatusResponse['results'] }; -} - -interface ServerReturnedEndpointPackageInfo { - type: 'serverReturnedEndpointPackageInfo'; - payload: GetPackagesResponse['response'][0]; -} - -export type PolicyListAction = - | ServerReturnedPolicyListData - | ServerFailedToReturnPolicyListData - | UserClickedPolicyListDeleteButton - | ServerDeletedPolicyFailure - | ServerDeletedPolicy - | UserOpenedPolicyListDeleteModal - | ServerReturnedPolicyAgentsSummaryForDeleteFailure - | ServerReturnedPolicyAgentsSummaryForDelete - | ServerReturnedEndpointPackageInfo; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts deleted file mode 100644 index 524c44406bd33..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PolicyListState } from '../../types'; -import { Store, applyMiddleware, createStore } from 'redux'; - -import { coreMock } from '../../../../../../../../../src/core/public/mocks'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../fleet/common'; - -import { policyListReducer } from './reducer'; -import { policyListMiddlewareFactory } from './middleware'; - -import { - isOnPolicyListPage, - selectIsLoading, - urlSearchParams, - selectIsDeleting, - endpointPackageVersion, -} from './selectors'; -import { DepsStartMock, depsStartMock } from '../../../../../common/mock/endpoint'; -import { setPolicyListApiMockImplementation } from './test_mock_utils'; -import { INGEST_API_PACKAGE_POLICIES } from './services/ingest'; -import { - createSpyMiddleware, - MiddlewareActionSpyHelper, -} from '../../../../../common/store/test_utils'; -import { getPoliciesPath } from '../../../../common/routing'; - -describe('policy list store concerns', () => { - const policyListPathUrl = getPoliciesPath(); - let fakeCoreStart: ReturnType; - let depsStart: DepsStartMock; - let store: Store; - let waitForAction: MiddlewareActionSpyHelper['waitForAction']; - - beforeEach(() => { - fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); - depsStart = depsStartMock(); - setPolicyListApiMockImplementation(fakeCoreStart.http); - let actionSpyMiddleware; - ({ actionSpyMiddleware, waitForAction } = createSpyMiddleware()); - - store = createStore( - policyListReducer, - undefined, - applyMiddleware(policyListMiddlewareFactory(fakeCoreStart, depsStart), actionSpyMiddleware) - ); - }); - - it('it does nothing on `userChangedUrl` if pathname is NOT `/policy`', async () => { - const state = store.getState(); - expect(isOnPolicyListPage(state)).toBe(false); - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: '/foo', - search: '', - hash: '', - }, - }); - expect(store.getState()).toEqual(state); - }); - - it('it reports `isOnPolicyListPage` correctly when router pathname is `/policy`', async () => { - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - expect(isOnPolicyListPage(store.getState())).toBe(true); - }); - - it('it sets `isLoading` when `userChangedUrl`', async () => { - expect(selectIsLoading(store.getState())).toBe(false); - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - expect(selectIsLoading(store.getState())).toBe(true); - await waitForAction('serverReturnedPolicyListData'); - expect(selectIsLoading(store.getState())).toBe(false); - }); - - it('it sets `isDeleting` when `userClickedPolicyListDeleteButton`', async () => { - expect(selectIsDeleting(store.getState())).toBe(false); - store.dispatch({ - type: 'userClickedPolicyListDeleteButton', - payload: { - policyId: '123', - }, - }); - expect(selectIsDeleting(store.getState())).toBe(true); - await waitForAction('serverDeletedPolicy'); - expect(selectIsDeleting(store.getState())).toBe(false); - }); - - it('it sets refreshes policy data when `serverDeletedPolicy`', async () => { - expect(selectIsLoading(store.getState())).toBe(false); - store.dispatch({ - type: 'serverDeletedPolicy', - payload: { - policyId: '', - success: true, - }, - }); - expect(selectIsLoading(store.getState())).toBe(true); - await waitForAction('serverReturnedPolicyListData'); - expect(selectIsLoading(store.getState())).toBe(false); - }); - - it('it resets state on `userChangedUrl` and pathname is NOT `/policy`', async () => { - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - await waitForAction('serverReturnedPolicyListData'); - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: '/foo', - search: '', - hash: '', - }, - }); - expect(store.getState()).toEqual({ - apiError: undefined, - location: undefined, - policyItems: [], - isLoading: false, - isDeleting: false, - deleteStatus: undefined, - endpointPackageInfo: undefined, - pageIndex: 0, - pageSize: 10, - total: 0, - agentStatusSummary: { - error: 0, - events: 0, - offline: 0, - online: 0, - total: 0, - other: 0, - }, - }); - }); - it('uses default pagination params when not included in url', async () => { - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: '', - hash: '', - }, - }); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 10, - }, - }); - }); - - describe('when url contains search params', () => { - const dispatchUserChangedUrl = (searchParams: string = '') => - store.dispatch({ - type: 'userChangedUrl', - payload: { - pathname: policyListPathUrl, - search: searchParams, - hash: '', - }, - }); - - it('uses pagination params from url', async () => { - dispatchUserChangedUrl('?page_size=50&page_index=0'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 50, - }, - }); - }); - it('uses defaults for params not in url', async () => { - dispatchUserChangedUrl('?page_index=99'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 99, - page_size: 10, - }); - dispatchUserChangedUrl('?page_size=50'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 0, - page_size: 50, - }); - }); - it('accepts only positive numbers for page_index and page_size', async () => { - dispatchUserChangedUrl('?page_size=-50&page_index=-99'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 10, - }, - }); - }); - it('it ignores non-numeric values for page_index and page_size', async () => { - dispatchUserChangedUrl('?page_size=fifty&page_index=ten'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 1, - perPage: 10, - }, - }); - }); - it('accepts only known values for `page_size`', async () => { - dispatchUserChangedUrl('?page_size=300&page_index=10'); - await waitForAction('serverReturnedPolicyListData'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 11, - perPage: 10, - }, - }); - }); - it(`ignores unknown url search params`, async () => { - dispatchUserChangedUrl('?page_size=20&page_index=10&foo=bar'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 10, - page_size: 20, - }); - }); - it(`uses last param value if param is defined multiple times`, async () => { - dispatchUserChangedUrl('?page_size=20&page_size=50&page_index=20&page_index=40'); - expect(urlSearchParams(store.getState())).toEqual({ - page_index: 40, - page_size: 50, - }); - }); - - it('should load package information only if not already in state', async () => { - dispatchUserChangedUrl('?page_size=10&page_index=10'); - await waitForAction('serverReturnedEndpointPackageInfo'); - expect(endpointPackageVersion(store.getState())).toEqual('0.5.0'); - fakeCoreStart.http.get.mockClear(); - dispatchUserChangedUrl('?page_size=10&page_index=11'); - expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_PACKAGE_POLICIES, { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, - page: 12, - perPage: 10, - }, - }); - expect(endpointPackageVersion(store.getState())).toEqual('0.5.0'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.ts deleted file mode 100644 index e09f80883d888..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PolicyListState } from '../../types'; -import { ImmutableReducer } from '../../../../../common/store'; -import { AppAction } from '../../../../../common/store/actions'; -import { Immutable } from '../../../../../../common/endpoint/types'; -export { policyListReducer } from './reducer'; -export { PolicyListAction } from './action'; -export { policyListMiddlewareFactory } from './middleware'; - -export interface EndpointPolicyListStatePluginState { - policyList: Immutable; -} - -export interface EndpointPolicyListStatePluginReducer { - policyList: ImmutableReducer; -} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/middleware.ts deleted file mode 100644 index e57eb0e32e516..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/middleware.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { GetPolicyListResponse, PolicyListState } from '../../types'; -import { - sendGetEndpointSpecificPackagePolicies, - sendDeletePackagePolicy, - sendGetFleetAgentStatusForPolicy, - sendGetEndpointSecurityPackage, -} from './services/ingest'; -import { endpointPackageInfo, isOnPolicyListPage, urlSearchParams } from './selectors'; -import { ImmutableMiddlewareFactory } from '../../../../../common/store'; -import { initialPolicyListState } from './reducer'; -import { - DeletePackagePoliciesResponse, - DeletePackagePoliciesRequest, - GetAgentStatusResponse, -} from '../../../../../../../fleet/common'; - -export const policyListMiddlewareFactory: ImmutableMiddlewareFactory = ( - coreStart -) => { - const http = coreStart.http; - - return ({ getState, dispatch }) => (next) => async (action) => { - next(action); - - const state = getState(); - - if ( - (action.type === 'userChangedUrl' && isOnPolicyListPage(state)) || - action.type === 'serverDeletedPolicy' - ) { - if (!endpointPackageInfo(state)) { - // We only need the package information to retrieve the version number, - // and even if we don't have the version, the UI is still ok because we - // handle that condition. Because of this, we retrieve the package information - // in a non-blocking way here and also ignore any API failures (only log it - // to the console) - sendGetEndpointSecurityPackage(http) - .then((packageInfo) => { - dispatch({ - type: 'serverReturnedEndpointPackageInfo', - payload: packageInfo, - }); - }) - .catch((error) => { - // eslint-disable-next-line no-console - console.error(error); - }); - } - - const { page_index: pageIndex, page_size: pageSize } = urlSearchParams(state); - let response: GetPolicyListResponse; - - try { - response = await sendGetEndpointSpecificPackagePolicies(http, { - query: { - perPage: pageSize, - page: pageIndex + 1, - }, - }); - } catch (err) { - dispatch({ - type: 'serverFailedToReturnPolicyListData', - payload: err.body ?? err, - }); - return; - } - - dispatch({ - type: 'serverReturnedPolicyListData', - payload: { - policyItems: response ? response.items : initialPolicyListState().policyItems, - pageIndex, - pageSize, - total: response ? response.total : initialPolicyListState().total, - }, - }); - } else if (action.type === 'userClickedPolicyListDeleteButton') { - const { policyId } = action.payload; - const packagePolicyIds: DeletePackagePoliciesRequest['body']['packagePolicyIds'] = [policyId]; - let apiResponse: DeletePackagePoliciesResponse; - try { - apiResponse = await sendDeletePackagePolicy(http, { body: { packagePolicyIds } }); - } catch (err) { - dispatch({ - type: 'serverDeletedPolicyFailure', - payload: err.body ?? err, - }); - return; - } - - dispatch({ - type: 'serverDeletedPolicy', - payload: { - id: apiResponse ? apiResponse[0].id : '', - success: true, - }, - }); - } else if (action.type === 'userOpenedPolicyListDeleteModal') { - const { agentPolicyId } = action.payload; - let apiResponse: GetAgentStatusResponse; - try { - apiResponse = await sendGetFleetAgentStatusForPolicy(http, agentPolicyId); - } catch (err) { - dispatch({ - type: 'serverReturnedPolicyAgentsSummaryForDeleteFailure', - payload: err.body ?? err, - }); - return; - } - dispatch({ - type: 'serverReturnedPolicyAgentsSummaryForDelete', - payload: { - agentStatusSummary: apiResponse.results, - }, - }); - } - }; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts deleted file mode 100644 index 53954449ab9c3..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isOnPolicyListPage } from './selectors'; -import { ImmutableReducer } from '../../../../../common/store'; -import { AppAction } from '../../../../../common/store/actions'; -import { Immutable } from '../../../../../../common/endpoint/types'; -import { PolicyListState } from '../../types'; - -/** - * Return the initial state. - * In case `state` was mutated, we return a fresh initial state object. - */ -export const initialPolicyListState: () => Immutable = () => ({ - policyItems: [], - endpointPackageInfo: undefined, - isLoading: false, - isDeleting: false, - deleteStatus: undefined, - apiError: undefined, - pageIndex: 0, - pageSize: 10, - total: 0, - location: undefined, - agentStatusSummary: { - error: 0, - events: 0, - offline: 0, - online: 0, - total: 0, - other: 0, - }, -}); - -export const policyListReducer: ImmutableReducer = ( - state = initialPolicyListState(), - action -) => { - if (action.type === 'serverReturnedPolicyListData') { - return { - ...state, - ...action.payload, - isLoading: false, - isDeleting: false, - }; - } - - if (action.type === 'serverFailedToReturnPolicyListData') { - return { - ...state, - apiError: action.payload, - isLoading: false, - isDeleting: false, - }; - } - - if (action.type === 'serverDeletedPolicyFailure') { - return { - ...state, - ...action.payload, - isLoading: false, - isDeleting: false, - }; - } - - if (action.type === 'serverDeletedPolicy') { - return { - ...state, - deleteStatus: action.payload.success, - isLoading: true, - isDeleting: false, - }; - } - - if (action.type === 'userClickedPolicyListDeleteButton') { - return { - ...state, - isLoading: false, - isDeleting: true, - }; - } - - if (action.type === 'serverReturnedPolicyAgentsSummaryForDelete') { - return { - ...state, - ...action.payload, - }; - } - - if (action.type === 'serverReturnedPolicyAgentsSummaryForDeleteFailure') { - return { - ...state, - ...action.payload, - }; - } - - if (action.type === 'serverReturnedEndpointPackageInfo') { - return { - ...state, - endpointPackageInfo: action.payload, - }; - } - - if (action.type === 'userChangedUrl') { - const newState: Immutable = { - ...state, - location: action.payload, - }; - const isCurrentlyOnListPage = isOnPolicyListPage(newState); - const wasPreviouslyOnListPage = isOnPolicyListPage(state); - - // If on the current page, then return new state with location information - // Also adjust some state if user is just entering the policy list view - if (isCurrentlyOnListPage) { - if (!wasPreviouslyOnListPage) { - return { - ...newState, - apiError: undefined, - isLoading: true, - isDeleting: false, - }; - } - return newState; - } - return initialPolicyListState(); - } - - return state; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/selectors.ts deleted file mode 100644 index ce57d238d7581..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/selectors.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createSelector } from 'reselect'; -import { parse } from 'query-string'; -import { matchPath } from 'react-router-dom'; -import { PolicyListState, PolicyListUrlSearchParams } from '../../types'; -import { Immutable } from '../../../../../../common/endpoint/types'; -import { MANAGEMENT_ROUTING_POLICIES_PATH } from '../../../../common/constants'; - -const PAGE_SIZES = Object.freeze([10, 20, 50]); - -export const selectPolicyItems = (state: Immutable) => state.policyItems; - -export const selectPageIndex = (state: Immutable) => state.pageIndex; - -export const selectPageSize = (state: Immutable) => state.pageSize; - -export const selectTotal = (state: Immutable) => state.total; - -export const selectIsLoading = (state: Immutable) => state.isLoading; - -export const selectApiError = (state: Immutable) => state.apiError; - -export const selectIsDeleting = (state: Immutable) => state.isDeleting; - -export const selectDeleteStatus = (state: Immutable) => state.deleteStatus; - -export const selectAgentStatusSummary = (state: Immutable) => - state.agentStatusSummary; - -export const isOnPolicyListPage = (state: Immutable) => { - return ( - matchPath(state.location?.pathname ?? '', { - path: MANAGEMENT_ROUTING_POLICIES_PATH, - exact: true, - }) !== null - ); -}; - -const routeLocation = (state: Immutable) => state.location; - -/** - * Returns the supported URL search params, populated with defaults if none where present in the URL - */ -export const urlSearchParams: ( - state: Immutable -) => PolicyListUrlSearchParams = createSelector(routeLocation, (location) => { - const searchParams = { - page_index: 0, - page_size: 10, - }; - if (!location) { - return searchParams; - } - - const query = parse(location.search); - - // Search params can appear multiple times in the URL, in which case the value for them, - // once parsed, would be an array. In these case, we take the last value defined - searchParams.page_index = Number( - (Array.isArray(query.page_index) - ? query.page_index[query.page_index.length - 1] - : query.page_index) ?? 0 - ); - searchParams.page_size = Number( - (Array.isArray(query.page_size) - ? query.page_size[query.page_size.length - 1] - : query.page_size) ?? 10 - ); - - // If pageIndex is not a valid positive integer, set it to 0 - if (!Number.isFinite(searchParams.page_index) || searchParams.page_index < 0) { - searchParams.page_index = 0; - } - - // if pageSize is not one of the expected page sizes, reset it to 10 - if (!PAGE_SIZES.includes(searchParams.page_size)) { - searchParams.page_size = 10; - } - - return searchParams; -}); - -/** - * Returns package information for Endpoint - * @param state - */ -export const endpointPackageInfo = (state: Immutable) => state.endpointPackageInfo; - -/** - * Returns the version number for the endpoint package. - */ -export const endpointPackageVersion = createSelector( - endpointPackageInfo, - (info) => info?.version ?? undefined -); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts similarity index 96% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.test.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts index 43a12868368c5..5e1e6706488c0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.test.ts @@ -10,8 +10,8 @@ import { sendGetEndpointSecurityPackage, sendGetEndpointSpecificPackagePolicies, } from './ingest'; -import { httpServiceMock } from '../../../../../../../../../../src/core/public/mocks'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../../fleet/common'; +import { httpServiceMock } from '../../../../../../../../../src/core/public/mocks'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../fleet/common'; import { policyListApiPathHandlers } from '../test_mock_utils'; describe('ingest service', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.ts similarity index 96% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.ts index a241f8d1c4556..ad37f1217de70 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/services/ingest.ts @@ -15,9 +15,9 @@ import { GetPackagesResponse, GetAgentPoliciesRequest, GetAgentPoliciesResponse, -} from '../../../../../../../../fleet/common'; -import { GetPolicyListResponse, GetPolicyResponse, UpdatePolicyResponse } from '../../../types'; -import { NewPolicyData } from '../../../../../../../common/endpoint/types'; +} from '../../../../../../../fleet/common'; +import { GetPolicyListResponse, GetPolicyResponse, UpdatePolicyResponse } from '../../types'; +import { NewPolicyData } from '../../../../../../common/endpoint/types'; const INGEST_API_ROOT = `/api/fleet`; export const INGEST_API_PACKAGE_POLICIES = `${INGEST_API_ROOT}/package_policies`; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/test_mock_utils.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/test_mock_utils.ts similarity index 67% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/test_mock_utils.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/test_mock_utils.ts index 3c8b1f913c868..6e12f325f83f0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/test_mock_utils.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/test_mock_utils.ts @@ -4,36 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpStart } from 'kibana/public'; import { INGEST_API_EPM_PACKAGES, INGEST_API_PACKAGE_POLICIES } from './services/ingest'; -import { EndpointDocGenerator } from '../../../../../../common/endpoint/generate_data'; -import { GetPolicyListResponse } from '../../types'; -import { GetPackagesResponse } from '../../../../../../../fleet/common'; +import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; +import { GetPolicyListResponse } from '../types'; +import { GetPackagesResponse } from '../../../../../../fleet/common'; const generator = new EndpointDocGenerator('policy-list'); -/** - * It sets the mock implementation on the necessary http methods to support the policy list view - * @param mockedHttpService - * @param totalPolicies - */ -export const setPolicyListApiMockImplementation = ( - mockedHttpService: jest.Mocked, - totalPolicies: number = 1 -): void => { - const policyApiHandlers = policyListApiPathHandlers(totalPolicies); - - mockedHttpService.get.mockImplementation(async (...args) => { - const [path] = args; - if (typeof path === 'string') { - if (policyApiHandlers[path]) { - return policyApiHandlers[path](); - } - } - return Promise.reject(new Error(`MOCK: unknown policy list api: ${path}`)); - }); -}; - /** * Returns the response body for a call to get the list of Policies * @param options diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts index ce942205b1620..e61067cbe57fc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/index.ts @@ -4,6 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './policy_list'; export * from './policy_details'; export * from './policy_advanced'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index e9c13b23834b1..1280f1c351c2b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -12,7 +12,7 @@ import '../../../../common/mock/match_media.ts'; import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { getPolicyDetailPath, getEndpointListPath } from '../../../common/routing'; -import { policyListApiPathHandlers } from '../store/policy_list/test_mock_utils'; +import { policyListApiPathHandlers } from '../store/test_mock_utils'; import { licenseService } from '../../../../common/hooks/use_license'; jest.mock('../../../../common/components/link_to'); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts index 97436064eebe2..6438c43bf7b5a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts @@ -5,28 +5,13 @@ */ import { useSelector } from 'react-redux'; -import { PolicyListState, PolicyDetailsState } from '../types'; +import { PolicyDetailsState } from '../types'; import { State } from '../../../../common/store'; import { MANAGEMENT_STORE_GLOBAL_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, } from '../../../common/constants'; -/** - * Narrows global state down to the PolicyListState before calling the provided Policy List Selector - * @param selector - */ -export function usePolicyListSelector(selector: (state: PolicyListState) => TSelected) { - return useSelector((state: State) => { - return selector( - state[MANAGEMENT_STORE_GLOBAL_NAMESPACE][ - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE - ] as PolicyListState - ); - }); -} - /** * Narrows global state down to the PolicyDetailsState before calling the provided Policy Details Selector * @param selector diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx deleted file mode 100644 index 4fe7a496276f3..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { PolicyList } from './index'; -import '../../../../common/mock/match_media.ts'; -import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; -import { setPolicyListApiMockImplementation } from '../store/policy_list/test_mock_utils'; - -jest.mock('../../../../common/components/link_to'); - -// Skipping these test now that the Policy List has been hidden -describe.skip('when on the policies page', () => { - let render: () => ReturnType; - let history: AppContextTestRender['history']; - let coreStart: AppContextTestRender['coreStart']; - let middlewareSpy: AppContextTestRender['middlewareSpy']; - - beforeEach(() => { - const mockedContext = createAppRootMockRenderer(); - ({ history, coreStart, middlewareSpy } = mockedContext); - render = () => mockedContext.render(); - }); - - it('should NOT display timeline', async () => { - const renderResult = render(); - const timelineFlyout = await renderResult.queryByTestId('flyoutOverlay'); - expect(timelineFlyout).toBeNull(); - }); - - it('should show the empty state', async () => { - const renderResult = render(); - const table = await renderResult.findByTestId('emptyPolicyTable'); - expect(table).not.toBeNull(); - }); - - it('should display the instructions', async () => { - const renderResult = render(); - const onboardingSteps = await renderResult.findByTestId('policyOnboardingInstructions'); - expect(onboardingSteps).not.toBeNull(); - }); - - describe('when list data loads', () => { - let firstPolicyID: string; - const renderList = async () => { - const renderResult = render(); - history.push('/policy'); - await Promise.all([ - middlewareSpy - .waitForAction('serverReturnedPolicyListData') - .then((action) => (firstPolicyID = action.payload.policyItems[0].id)), - // middlewareSpy.waitForAction('serverReturnedAgentPolicyListData'), - ]); - return renderResult; - }; - - beforeEach(async () => { - setPolicyListApiMockImplementation(coreStart.http, 3); - }); - - it('should display rows in the table', async () => { - const renderResult = await renderList(); - const rows = await renderResult.findAllByRole('row'); - expect(rows).toHaveLength(4); - }); - - it('should display policy name value as a link', async () => { - const renderResult = await renderList(); - const policyNameLink = (await renderResult.findAllByTestId('policyNameLink'))[0]; - expect(policyNameLink).not.toBeNull(); - expect(policyNameLink.getAttribute('href')).toContain(`policy/${firstPolicyID}`); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx deleted file mode 100644 index 09c1fc1915d02..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useCallback, useEffect, useMemo, CSSProperties, useState } from 'react'; -import { - EuiBasicTable, - EuiText, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiTableFieldDataColumnType, - EuiLink, - EuiPopover, - EuiContextMenuPanelProps, - EuiContextMenuItem, - EuiButtonIcon, - EuiContextMenuPanel, - EuiOverlayMask, - EuiConfirmModal, - EuiCallOut, - EuiButton, - EuiHorizontalRule, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { useDispatch } from 'react-redux'; -import { useLocation, useHistory } from 'react-router-dom'; -import { createStructuredSelector } from 'reselect'; -import styled from 'styled-components'; -import { ApplicationStart } from 'src/core/public'; -import { CreateStructuredSelector } from '../../../../common/store'; -import * as selectors from '../store/policy_list/selectors'; -import { usePolicyListSelector } from './policy_hooks'; -import { PolicyListAction } from '../store/policy_list'; -import { useToasts } from '../../../../common/lib/kibana'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { Immutable, PolicyData } from '../../../../../common/endpoint/types'; -import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; -import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; -import { PolicyEmptyState } from '../../../components/management_empty_state'; -import { FormattedDateAndTime } from '../../../../common/components/endpoint/formatted_date_time'; -import { SecurityPageName } from '../../../../app/types'; -import { useFormatUrl } from '../../../../common/components/link_to'; -import { getPolicyDetailPath, getPoliciesPath } from '../../../common/routing'; -import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; -import { CreatePackagePolicyRouteState } from '../../../../../../fleet/public'; -import { MANAGEMENT_APP_ID } from '../../../common/constants'; -import { AdministrationListPage } from '../../../components/administration_list_page'; - -interface TableChangeCallbackArguments { - page: { index: number; size: number }; -} - -interface PackageData { - name: string; - title: string; - version: string; -} - -const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({ - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', -}); - -const DangerEuiContextMenuItem = styled(EuiContextMenuItem)` - color: ${(props) => props.theme.eui.euiColorDangerText}; -`; - -// eslint-disable-next-line react/display-name -export const TableRowActions = React.memo<{ items: EuiContextMenuPanelProps['items'] }>( - ({ items }) => { - const [isOpen, setIsOpen] = useState(false); - const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); - const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); - - return ( - - } - isOpen={isOpen} - closePopover={handleCloseMenu} - repositionOnScroll - > - - - ); - } -); - -const PolicyLink: React.FC<{ name: string; route: string; href: string }> = ({ - name, - route, - href, -}) => { - const clickHandler = useNavigateByRouterEventHandler(route); - return ( - // eslint-disable-next-line @elastic/eui/href-or-on-click - - {name} - - ); -}; - -const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); -export const PolicyList = React.memo(() => { - const { services } = useKibana<{ application: ApplicationStart }>(); - const toasts = useToasts(); - const history = useHistory(); - const location = useLocation(); - const { formatUrl, search } = useFormatUrl(SecurityPageName.administration); - - const [showDelete, setShowDelete] = useState(false); - const [policyIdToDelete, setPolicyIdToDelete] = useState(''); - - const dispatch = useDispatch<(action: PolicyListAction) => void>(); - const { - selectPolicyItems: policyItems, - selectPageIndex: pageIndex, - selectPageSize: pageSize, - selectTotal: totalItemCount, - selectIsLoading: loading, - selectApiError: apiError, - selectIsDeleting: isDeleting, - selectDeleteStatus: deleteStatus, - selectAgentStatusSummary: agentStatusSummary, - endpointPackageVersion, - } = usePolicyListSelector(selector); - - const handleCreatePolicyClick = useNavigateToAppEventHandler( - 'fleet', - { - // We redirect to Ingest's Integaration page if we can't get the package version, and - // to the Integration Endpoint Package Add Integration if we have package information. - // Also, - // We pass along soem state information so that the Ingest page can change the behaviour - // of the cancel and submit buttons and redirect the user back to endpoint policy - path: `#/integrations${ - endpointPackageVersion ? `/endpoint-${endpointPackageVersion}/add-integration` : '' - }`, - state: { - onCancelNavigateTo: [MANAGEMENT_APP_ID, { path: getPoliciesPath() }], - onCancelUrl: formatUrl(getPoliciesPath()), - onSaveNavigateTo: [MANAGEMENT_APP_ID, { path: getPoliciesPath() }], - }, - } - ); - - useEffect(() => { - if (apiError) { - toasts.addDanger({ - title: apiError.error, - text: apiError.message, - }); - } - }, [apiError, dispatch, toasts]); - - // Handle showing update statuses - useEffect(() => { - if (deleteStatus !== undefined) { - if (deleteStatus === true) { - setPolicyIdToDelete(''); - setShowDelete(false); - toasts.addSuccess({ - title: i18n.translate('xpack.securitySolution.endpoint.policyList.deleteSuccessToast', { - defaultMessage: 'Success!', - }), - text: i18n.translate( - 'xpack.securitySolution.endpoint.policyList.deleteSuccessToastDetails', - { - defaultMessage: 'Policy has been deleted.', - } - ), - }); - } else { - toasts.addDanger({ - title: i18n.translate('xpack.securitySolution.endpoint.policyList.deleteFailedToast', { - defaultMessage: 'Failed!', - }), - text: i18n.translate('xpack.securitySolution.endpoint.policyList.deleteFailedToastBody', { - defaultMessage: 'Failed to delete policy', - }), - }); - } - } - }, [toasts, deleteStatus]); - - const paginationSetup = useMemo(() => { - return { - pageIndex, - pageSize, - totalItemCount, - pageSizeOptions: [10, 20, 50], - hidePerPageOptions: false, - }; - }, [pageIndex, pageSize, totalItemCount]); - - const handleTableChange = useCallback( - ({ page: { index, size } }: TableChangeCallbackArguments) => { - history.push(`${location.pathname}?page_index=${index}&page_size=${size}`); - }, - [history, location.pathname] - ); - - const handleDeleteOnClick = useCallback( - ({ policyId, agentPolicyId }: { policyId: string; agentPolicyId: string }) => { - dispatch({ - type: 'userOpenedPolicyListDeleteModal', - payload: { - agentPolicyId, - }, - }); - setPolicyIdToDelete(policyId); - setShowDelete(true); - }, - [dispatch] - ); - - const handleDeleteConfirmation = useCallback( - ({ policyId }: { policyId: string }) => { - dispatch({ - type: 'userClickedPolicyListDeleteButton', - payload: { - policyId, - }, - }); - }, - [dispatch] - ); - - const handleDeleteCancel = useCallback(() => { - setShowDelete(false); - }, []); - - const columns: Array>> = useMemo( - () => [ - { - field: 'name', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.nameField', { - defaultMessage: 'Policy Name', - }), - // eslint-disable-next-line react/display-name - render: (name: string, item: Immutable) => { - const routePath = getPolicyDetailPath(item.id, search); - const routeUrl = formatUrl(routePath); - return ( - - - - - - - - - - - ); - }, - }, - { - field: 'created_by', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.createdBy', { - defaultMessage: 'Created By', - }), - truncateText: true, - }, - { - field: 'created_at', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.createdAt', { - defaultMessage: 'Created Date', - }), - render(createdAt: string) { - return ; - }, - }, - { - field: 'updated_by', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.updatedBy', { - defaultMessage: 'Last Updated By', - }), - truncateText: true, - }, - { - field: 'updated_at', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.updatedAt', { - defaultMessage: 'Last Updated', - }), - render(updatedAt: string) { - return ; - }, - }, - { - field: 'package', - name: i18n.translate('xpack.securitySolution.endpoint.policyList.versionFieldLabel', { - defaultMessage: 'Version', - }), - render(pkg: Immutable) { - return i18n.translate('xpack.securitySolution.endpoint.policyList.versionField', { - defaultMessage: 'v{version}', - values: { - version: pkg.version, - }, - }); - }, - }, - { - field: '', - name: 'Actions', - actions: [ - { - // eslint-disable-next-line react/display-name - render: (item: Immutable) => { - return ( - - - - - , - { - handleDeleteOnClick({ agentPolicyId: item.policy_id, policyId: item.id }); - }} - > - - , - ]} - /> - ); - }, - }, - ], - }, - ], - [services.application, handleDeleteOnClick, formatUrl, search] - ); - - const bodyContent = useMemo(() => { - return policyItems && policyItems.length > 0 ? ( - - ) : ( - - ); - }, [policyItems, loading, columns, handleCreatePolicyClick, handleTableChange, paginationSetup]); - - return ( - <> - {showDelete && ( - { - handleDeleteConfirmation({ policyId: policyIdToDelete }); - }} - /> - )} - - } - subtitle={ - - } - actions={ - - - - } - > - {policyItems && policyItems.length > 0 && ( - <> - - - - - - )} - {bodyContent} - - - ); -}); - -PolicyList.displayName = 'PolicyList'; - -const ConfirmDelete = React.memo<{ - hostCount: number; - isDeleting: boolean; - onConfirm: () => void; - onCancel: () => void; -}>(({ hostCount, isDeleting, onCancel, onConfirm }) => { - return ( - - - ) : ( - - ) - } - confirmButtonDisabled={isDeleting} - cancelButtonText={i18n.translate( - 'xpack.securitySolution.endpoint.policyList.deleteConfirm.cancelButtonTitle', - { - defaultMessage: 'Cancel', - } - )} - > - {hostCount > 0 && ( - <> - - - - - - )} -

- -

-
-
- ); -}); - -ConfirmDelete.displayName = 'ConfirmDelete'; diff --git a/x-pack/plugins/security_solution/public/management/store/middleware.ts b/x-pack/plugins/security_solution/public/management/store/middleware.ts index 77d02262e93b7..c984525edcc13 100644 --- a/x-pack/plugins/security_solution/public/management/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/store/middleware.ts @@ -13,10 +13,8 @@ import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_GLOBAL_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, } from '../common/constants'; -import { policyListMiddlewareFactory } from '../pages/policy/store/policy_list'; import { policyDetailsMiddlewareFactory } from '../pages/policy/store/policy_details'; import { endpointMiddlewareFactory } from '../pages/endpoint_hosts/store/middleware'; import { trustedAppsPageMiddlewareFactory } from '../pages/trusted_apps/store/middleware'; @@ -31,10 +29,6 @@ export const managementMiddlewareFactory: SecuritySubPluginMiddlewareFactory = ( depsStart ) => { return [ - substateMiddlewareFactory( - createSubStateSelector(MANAGEMENT_STORE_POLICY_LIST_NAMESPACE), - policyListMiddlewareFactory(coreStart, depsStart) - ), substateMiddlewareFactory( createSubStateSelector(MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE), policyDetailsMiddlewareFactory(coreStart, depsStart) diff --git a/x-pack/plugins/security_solution/public/management/store/reducer.ts b/x-pack/plugins/security_solution/public/management/store/reducer.ts index 93186fdb67349..61cf242f21960 100644 --- a/x-pack/plugins/security_solution/public/management/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/store/reducer.ts @@ -9,14 +9,9 @@ import { policyDetailsReducer, initialPolicyDetailsState, } from '../pages/policy/store/policy_details/reducer'; -import { - policyListReducer, - initialPolicyListState, -} from '../pages/policy/store/policy_list/reducer'; import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, } from '../common/constants'; import { ImmutableCombineReducers } from '../../common/store'; @@ -35,7 +30,6 @@ const immutableCombineReducers: ImmutableCombineReducers = combineReducers; * Returns the initial state of the store for the SIEM Management section */ export const mockManagementState: Immutable = { - [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: initialPolicyListState(), [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: initialPolicyDetailsState(), [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: initialEndpointListState, [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: initialTrustedAppsPageState(), @@ -45,7 +39,6 @@ export const mockManagementState: Immutable = { * Redux store reducer for the SIEM Management section */ export const managementReducer = immutableCombineReducers({ - [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: policyListReducer, [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer, [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: endpointListReducer, [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: trustedAppsPageReducer, diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index 8b53f4c1d8525..3feb8d56766b7 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -6,9 +6,9 @@ import { CombinedState } from 'redux'; import { SecurityPageName } from '../app/types'; -import { PolicyListState, PolicyDetailsState } from './pages/policy/types'; +import { PolicyDetailsState } from './pages/policy/types'; import { EndpointState } from './pages/endpoint_hosts/types'; -import { TrustedAppsListPageState } from './pages/trusted_apps/state/trusted_apps_list_page_state'; +import { TrustedAppsListPageState } from './pages/trusted_apps/state'; /** * The type for the management store global namespace. Used mostly internally to reference @@ -17,7 +17,6 @@ import { TrustedAppsListPageState } from './pages/trusted_apps/state/trusted_app export type ManagementStoreGlobalNamespace = 'management'; export type ManagementState = CombinedState<{ - policyList: PolicyListState; policyDetails: PolicyDetailsState; endpoints: EndpointState; trustedApps: TrustedAppsListPageState; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index 1b5e6932dff08..7ae8cf4c6507c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiSuperSelect, EuiToolTip } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import React, { useCallback } from 'react'; import styled, { createGlobalStyle } from 'styled-components'; @@ -15,6 +15,7 @@ import { DispatchUpdateReduxTime } from '../../../../common/components/super_dat import { DataProvider } from '../data_providers/data_provider'; import { QueryBarTimeline } from '../query_bar'; +import { EuiSuperSelect } from './super_select'; import { options } from './helpers'; import * as i18n from './translations'; @@ -28,8 +29,8 @@ const SearchOrFilterGlobalStyle = createGlobalStyle` width: 350px !important; } - .${searchOrFilterPopoverClassName}__popoverPanel { - width: ${searchOrFilterPopoverWidth}; + .${searchOrFilterPopoverClassName}.euiPopover__panel { + width: ${searchOrFilterPopoverWidth} !important; .euiSuperSelect__listbox { width: ${searchOrFilterPopoverWidth} !important; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/super_select.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/super_select.tsx new file mode 100644 index 0000000000000..36a6ea7385b8a --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/super_select.tsx @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + Duplicated EuiSuperSelect, because due to the recent changes there is no way to pass panelClassName + prop to EuiInputPopover, which doesn't allow us to properly style the EuiInputPopover panel + (we want the panel to be wider than the input) +*/ + +import { + EuiSuperSelectProps, + EuiScreenReaderOnly, + EuiSuperSelectControl, + EuiInputPopover, + EuiContextMenuItem, + keys, + EuiI18n, +} from '@elastic/eui'; +import React, { Component } from 'react'; +import classNames from 'classnames'; + +enum ShiftDirection { + BACK = 'back', + FORWARD = 'forward', +} + +export class EuiSuperSelect extends Component> { + static defaultProps = { + compressed: false, + fullWidth: false, + hasDividers: false, + isInvalid: false, + isLoading: false, + }; + + private itemNodes: Array = []; + private _isMounted: boolean = false; + + state = { + isPopoverOpen: this.props.isOpen || false, + }; + + componentDidMount() { + this._isMounted = true; + if (this.props.isOpen) { + this.openPopover(); + } + } + + componentWillUnmount() { + this._isMounted = false; + } + + setItemNode = (node: HTMLButtonElement | null, index: number) => { + this.itemNodes[index] = node; + }; + + openPopover = () => { + this.setState({ + isPopoverOpen: true, + }); + + const focusSelected = () => { + const indexOfSelected = this.props.options.reduce((acc, option, index) => { + if (acc != null) return acc; + if (option == null) return null; + return option.value === this.props.valueOfSelected ? index : null; + }, null); + + requestAnimationFrame(() => { + if (!this._isMounted) { + return; + } + + if (this.props.valueOfSelected != null) { + if (indexOfSelected != null) { + this.focusItemAt(indexOfSelected); + } else { + focusSelected(); + } + } + }); + }; + + requestAnimationFrame(focusSelected); + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + itemClicked = (value: T) => { + this.setState({ + isPopoverOpen: false, + }); + if (this.props.onChange) { + this.props.onChange(value); + } + }; + + onSelectKeyDown = (event: React.KeyboardEvent) => { + if (event.key === keys.ARROW_UP || event.key === keys.ARROW_DOWN) { + event.preventDefault(); + event.stopPropagation(); + this.openPopover(); + } + }; + + onItemKeyDown = (event: React.KeyboardEvent) => { + switch (event.key) { + case keys.ESCAPE: + // close the popover and prevent ancestors from handling + event.preventDefault(); + event.stopPropagation(); + this.closePopover(); + break; + + case keys.TAB: + // no-op + event.preventDefault(); + event.stopPropagation(); + break; + + case keys.ARROW_UP: + event.preventDefault(); + event.stopPropagation(); + this.shiftFocus(ShiftDirection.BACK); + break; + + case keys.ARROW_DOWN: + event.preventDefault(); + event.stopPropagation(); + this.shiftFocus(ShiftDirection.FORWARD); + break; + } + }; + + focusItemAt(index: number) { + const targetElement = this.itemNodes[index]; + if (targetElement != null) { + targetElement.focus(); + } + } + + shiftFocus(direction: ShiftDirection) { + const currentIndex = this.itemNodes.indexOf(document.activeElement as HTMLButtonElement); + let targetElementIndex: number; + + if (currentIndex === -1) { + // somehow the select options has lost focus + targetElementIndex = 0; + } else { + if (direction === ShiftDirection.BACK) { + targetElementIndex = currentIndex === 0 ? this.itemNodes.length - 1 : currentIndex - 1; + } else { + targetElementIndex = currentIndex === this.itemNodes.length - 1 ? 0 : currentIndex + 1; + } + } + + this.focusItemAt(targetElementIndex); + } + + render() { + const { + className, + options, + valueOfSelected, + onChange, + isOpen, + isInvalid, + hasDividers, + itemClassName, + itemLayoutAlign, + fullWidth, + popoverClassName, + compressed, + ...rest + } = this.props; + + const popoverClasses = classNames('euiSuperSelect', popoverClassName); + + const buttonClasses = classNames( + { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'euiSuperSelect--isOpen__button': this.state.isPopoverOpen, + }, + className + ); + + const itemClasses = classNames( + 'euiSuperSelect__item', + { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'euiSuperSelect__item--hasDividers': hasDividers, + }, + itemClassName + ); + + const button = ( + + ); + + const items = options.map((option, index) => { + const { value, dropdownDisplay, inputDisplay, ...optionRest } = option; + + return ( + this.itemClicked(value)} + onKeyDown={this.onItemKeyDown} + layoutAlign={itemLayoutAlign} + buttonRef={(node) => this.setItemNode(node, index)} + role="option" + id={value} + aria-selected={valueOfSelected === value} + {...optionRest} + > + {dropdownDisplay || inputDisplay} + + ); + }); + + return ( + + +

+ +

+
+
+ {items} +
+
+ ); + } +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fda6b81c4af03..1dbd6858815c4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17110,36 +17110,12 @@ "xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.security": "セキュリティ", "xpack.securitySolution.endpoint.policyDetailType": "タイプ", "xpack.securitySolution.endpoint.policyList.actionButtonText": "Endpoint Securityを追加", - "xpack.securitySolution.endpoint.policyList.actionMenu": "開く", - "xpack.securitySolution.endpoint.policyList.agentPolicyAction": "エージェントポリシーを表示", - "xpack.securitySolution.endpoint.policyList.createdAt": "作成日時", - "xpack.securitySolution.endpoint.policyList.createdBy": "作成者", - "xpack.securitySolution.endpoint.policyList.createNewButton": "新しいポリシーを作成", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.cancelButtonTitle": "キャンセル", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.confirmDeleteButton": "ポリシーを削除", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.deletingButton": "削除中…", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.message": "この操作は元に戻すことができません。続行していいですか?", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.title": "ポリシーを削除し、変更をデプロイ", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningMessage": "このポリシーを削除すると、これらのホストからEndpoint Securityが削除されます", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningTitle": "このアクションにより、{hostCount, plural, one {個のホスト} other {個のホスト}}からEndpoint Securityが削除されます", - "xpack.securitySolution.endpoint.policyList.deleteFailedToast": "失敗しました。", - "xpack.securitySolution.endpoint.policyList.deleteFailedToastBody": "ポリシーを削除できませんでした", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToast": "成功!", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToastDetails": "ポリシーが削除されました。", "xpack.securitySolution.endpoint.policyList.emptyCreateNewButton": "エージェントの登録", - "xpack.securitySolution.endpoint.policyList.nameField": "ポリシー名", "xpack.securitySolution.endpoint.policyList.onboardingDocsLink": "セキュリティアプリドキュメントを表示", "xpack.securitySolution.endpoint.policyList.onboardingSectionOne": "Endpoint Securityでは、脅威防御、検出、深いセキュリティデータの可視化を実現し、ホストを保護します。", "xpack.securitySolution.endpoint.policyList.onboardingSectionThree": "開始するには、Endpoint Security統合をエージェントに追加します。詳細については、 ", "xpack.securitySolution.endpoint.policyList.onboardingSectionTwo": "このページでは、Endpoint Securityを実行している環境でホストを表示して管理できます。", "xpack.securitySolution.endpoint.policyList.onboardingTitle": "Endpoint Securityの基本", - "xpack.securitySolution.endpoint.policyList.policyDeleteAction": "ポリシーを削除", - "xpack.securitySolution.endpoint.policyList.revision": "rev. {revNumber}", - "xpack.securitySolution.endpoint.policyList.updatedAt": "最終更新", - "xpack.securitySolution.endpoint.policyList.updatedBy": "最終更新者", - "xpack.securitySolution.endpoint.policyList.versionField": "v{version}", - "xpack.securitySolution.endpoint.policyList.versionFieldLabel": "バージョン", - "xpack.securitySolution.endpoint.policyList.viewTitleTotalCount": "{totalItemCount, plural, one {# ポリシー} other {# ポリシー}}", "xpack.securitySolution.endpoint.policyResponse.backLinkTitle": "エンドポイント詳細", "xpack.securitySolution.endpoint.policyResponse.title": "ポリシー応答", "xpack.securitySolution.endpoint.resolver.eitherLineageLimitExceeded": "以下のビジュアライゼーションとイベントリストの一部のプロセスイベントを表示できませんでした。データの上限に達しました。", @@ -17777,8 +17753,6 @@ "xpack.securitySolution.paginatedTable.tooManyResultsToastText": "クエリ範囲を縮めて結果をさらにフィルタリングしてください", "xpack.securitySolution.paginatedTable.tooManyResultsToastTitle": " - 結果が多すぎます", "xpack.securitySolution.policiesTab": "ポリシー", - "xpack.securitySolution.policyList.pageSubTitle": "保護の表示と構成", - "xpack.securitySolution.policyList.pageTitle": "ポリシー", "xpack.securitySolution.policyStatusText.failure": "失敗", "xpack.securitySolution.policyStatusText.success": "成功", "xpack.securitySolution.policyStatusText.warning": "警告", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 609e09d0197af..216f8a36948f5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17128,36 +17128,12 @@ "xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.security": "安全", "xpack.securitySolution.endpoint.policyDetailType": "类型", "xpack.securitySolution.endpoint.policyList.actionButtonText": "添加 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.actionMenu": "打开", - "xpack.securitySolution.endpoint.policyList.agentPolicyAction": "查看代理策略", - "xpack.securitySolution.endpoint.policyList.createdAt": "创建日期", - "xpack.securitySolution.endpoint.policyList.createdBy": "创建者", - "xpack.securitySolution.endpoint.policyList.createNewButton": "创建新策略", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.cancelButtonTitle": "取消", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.confirmDeleteButton": "删除策略", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.deletingButton": "正在删除……", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.message": "此操作无法撤消。是否确定要继续?", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.title": "删除策略并部署更改", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningMessage": "删除此策略将从这些主机上移除 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.deleteConfirm.warningTitle": "此操作将从 {hostCount, plural, one {# 个主机} other {# 个主机}}上删除 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.deleteFailedToast": "失败!", - "xpack.securitySolution.endpoint.policyList.deleteFailedToastBody": "无法删除策略", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToast": "成功!", - "xpack.securitySolution.endpoint.policyList.deleteSuccessToastDetails": "策略已删除。", "xpack.securitySolution.endpoint.policyList.emptyCreateNewButton": "注册代理", - "xpack.securitySolution.endpoint.policyList.nameField": "策略名称", "xpack.securitySolution.endpoint.policyList.onboardingDocsLink": "查看 Security 应用文档", "xpack.securitySolution.endpoint.policyList.onboardingSectionOne": "Endpoint Security 使用威胁防御、检测和深度安全数据可见性来保护您的主机。", "xpack.securitySolution.endpoint.policyList.onboardingSectionThree": "首先,将 Endpoint Security 集成添加到您的代理。有关更多信息, ", "xpack.securitySolution.endpoint.policyList.onboardingSectionTwo": "从此页面,您将可以查看和管理环境中运行 Endpoint Security 的主机。", "xpack.securitySolution.endpoint.policyList.onboardingTitle": "开始使用 Endpoint Security", - "xpack.securitySolution.endpoint.policyList.policyDeleteAction": "删除策略", - "xpack.securitySolution.endpoint.policyList.revision": "修订 {revNumber}", - "xpack.securitySolution.endpoint.policyList.updatedAt": "最后更新时间", - "xpack.securitySolution.endpoint.policyList.updatedBy": "最后更新者", - "xpack.securitySolution.endpoint.policyList.versionField": "v{version}", - "xpack.securitySolution.endpoint.policyList.versionFieldLabel": "版本", - "xpack.securitySolution.endpoint.policyList.viewTitleTotalCount": "{totalItemCount, plural, one {# 个策略} other {# 个策略}}", "xpack.securitySolution.endpoint.policyResponse.backLinkTitle": "终端详情", "xpack.securitySolution.endpoint.policyResponse.title": "策略响应", "xpack.securitySolution.endpoint.resolver.eitherLineageLimitExceeded": "下面可视化和事件列表中的一些进程事件无法显示,因为已达到数据限制。", @@ -17795,8 +17771,6 @@ "xpack.securitySolution.paginatedTable.tooManyResultsToastText": "缩减您的查询范围,以更好地筛选结果", "xpack.securitySolution.paginatedTable.tooManyResultsToastTitle": " - 结果过多", "xpack.securitySolution.policiesTab": "策略", - "xpack.securitySolution.policyList.pageSubTitle": "查看并配置防护", - "xpack.securitySolution.policyList.pageTitle": "策略", "xpack.securitySolution.policyStatusText.failure": "失败", "xpack.securitySolution.policyStatusText.success": "成功", "xpack.securitySolution.policyStatusText.warning": "警告", diff --git a/x-pack/test/functional_cors/config.ts b/x-pack/test/functional_cors/config.ts index b792aa2d183b6..737cccc5ae33b 100644 --- a/x-pack/test/functional_cors/config.ts +++ b/x-pack/test/functional_cors/config.ts @@ -27,7 +27,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { }; const { protocol, hostname } = kbnTestConfig.getUrlParts(); - const pluginPort = await getPort({ port: 9000 }); + const pluginPort = await getPort(); const originUrl = Url.format({ protocol, hostname, diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts deleted file mode 100644 index 741040b12fd7b..0000000000000 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; -import { PolicyTestResourceInfo } from '../../services/endpoint_policy'; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const pageObjects = getPageObjects([ - 'common', - 'endpoint', - 'policy', - 'endpointPageUtils', - 'ingestManagerCreatePackagePolicy', - ]); - const testSubjects = getService('testSubjects'); - const policyTestResources = getService('policyTestResources'); - const RELATIVE_DATE_FORMAT = /\d (?:seconds|minutes) ago/i; - - describe('When on the Endpoint Policy List', function () { - this.tags(['ciGroup7']); - before(async () => { - await pageObjects.policy.navigateToPolicyList(); - }); - - it('loads the Policy List Page', async () => { - await testSubjects.existOrFail('policyListPage'); - }); - it('displays page title', async () => { - const policyTitle = await testSubjects.getVisibleText('header-page-title'); - expect(policyTitle).to.equal('Policies'); - }); - it('shows header create policy button', async () => { - const createButtonTitle = await testSubjects.getVisibleText('headerCreateNewPolicyButton'); - expect(createButtonTitle).to.equal('Create new policy'); - }); - it('shows empty state', async () => { - await testSubjects.existOrFail('emptyPolicyTable'); - }); - - describe('and policies exists', () => { - let policyInfo: PolicyTestResourceInfo; - - before(async () => { - // load/create a policy and then navigate back to the policy view so that the list is refreshed - policyInfo = await policyTestResources.createPolicy(); - await pageObjects.policy.navigateToPolicyList(); - await pageObjects.endpoint.waitForTableToHaveData('policyTable'); - }); - after(async () => { - if (policyInfo) { - await policyInfo.cleanup(); - } - }); - - it('has correct table headers', async () => { - const allHeaderCells = await pageObjects.endpointPageUtils.tableHeaderVisibleText( - 'policyTable' - ); - expect(allHeaderCells).to.eql([ - 'Policy Name', - 'Created By', - 'Created Date', - 'Last Updated By', - 'Last Updated', - 'Version', - 'Actions', - ]); - }); - - it('should show policy on the list', async () => { - const [, policyRow] = await pageObjects.endpointPageUtils.tableData('policyTable'); - // Validate row data with the exception of the Date columns - since those are initially - // shown as relative. - expect([policyRow[0], policyRow[1], policyRow[3], policyRow[5], policyRow[6]]).to.eql([ - 'Protect East Coastrev. 1', - 'elastic', - 'elastic', - `v${policyInfo.packagePolicy.package?.version}`, - '', - ]); - [policyRow[2], policyRow[4]].forEach((relativeDate) => { - expect(relativeDate).to.match(RELATIVE_DATE_FORMAT); - }); - }); - - it('should show agent policy action as a link', async () => { - await (await pageObjects.policy.findFirstActionsButton()).click(); - const agentPolicyLink = await testSubjects.find('agentPolicyLink'); - expect(await agentPolicyLink.getAttribute('href')).to.match( - new RegExp(`\/ingestManager#\/policies\/${policyInfo.agentPolicy.id}$`) - ); - // Close action menu - await (await pageObjects.policy.findFirstActionsButton()).click(); - }); - - it('should delete a policy', async () => { - await pageObjects.policy.launchAndFindDeleteModal(); - await testSubjects.existOrFail('policyListDeleteModal'); - await pageObjects.common.clickConfirmOnModal(); - const emptyPolicyTable = await testSubjects.find('emptyPolicyTable'); - expect(emptyPolicyTable).not.to.be(null); - }); - }); - - describe('and user clicks on page header create button', () => { - beforeEach(async () => { - await pageObjects.policy.navigateToPolicyList(); - await (await pageObjects.policy.findHeaderCreateNewButton()).click(); - }); - - it('should redirect to ingest management integrations add package policy', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnCreatePageOrFail(); - }); - - it('should redirect user back to Policy List if Cancel button is clicked', async () => { - await (await pageObjects.ingestManagerCreatePackagePolicy.findCancelButton()).click(); - await pageObjects.policy.ensureIsOnPolicyPage(); - }); - - it('should redirect user back to Policy List if Back link is clicked', async () => { - await (await pageObjects.ingestManagerCreatePackagePolicy.findBackLink()).click(); - await pageObjects.policy.ensureIsOnPolicyPage(); - }); - - it('should display custom endpoint configuration message', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); - const endpointConfig = await pageObjects.policy.findPackagePolicyEndpointCustomConfiguration(); - expect(endpointConfig).not.to.be(undefined); - }); - - it('should have empty value for package policy name', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); - expect(await pageObjects.ingestManagerCreatePackagePolicy.getPackagePolicyName()).to.be(''); - }); - - it('should redirect user back to Policy List after a successful save', async () => { - const newPolicyName = `endpoint policy ${Date.now()}`; - await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy(); - await pageObjects.ingestManagerCreatePackagePolicy.setPackagePolicyName(newPolicyName); - await (await pageObjects.ingestManagerCreatePackagePolicy.findSaveButton()).click(); - await pageObjects.ingestManagerCreatePackagePolicy.waitForSaveSuccessNotification(); - await pageObjects.policy.ensureIsOnPolicyPage(); - await policyTestResources.deletePolicyByName(newPolicyName); - }); - }); - - describe('and user clicks on page header create button', () => { - it('should direct users to the ingest management integrations add package policy', async () => { - await pageObjects.policy.navigateToPolicyList(); - await (await pageObjects.policy.findOnboardingStartButton()).click(); - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnCreatePageOrFail(); - }); - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts index 8bfbdc32452ee..18ffc6229e74f 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts @@ -12,42 +12,6 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr const browser = getService('browser'); return { - /** - * Navigates to the Endpoint Policy List - */ - async navigateToPolicyList() { - await pageObjects.common.navigateToUrlWithBrowserHistory( - 'securitySolutionManagement', - '/policy' - ); - await pageObjects.header.waitUntilLoadingHasFinished(); - }, - - /** - * Finds and returns the Policy Details Page Save button - */ - async findFirstActionsButton() { - await this.ensureIsOnPolicyPage(); - return await testSubjects.find('policyActionsButton'); - }, - - /** - * Finds and returns the Policy Details Page Save button - */ - async launchAndFindDeleteModal() { - const actionsButton = await this.findFirstActionsButton(); - await actionsButton.click(); - await testSubjects.click('policyDeleteButton'); - return await testSubjects.find('policyListDeleteModal'); - }, - - /** - * ensures that the Policy Page is the currently display view - */ - async ensureIsOnPolicyPage() { - await testSubjects.existOrFail('policyListPage'); - }, - /** * Navigates to the Endpoint Policy Details page * @@ -130,13 +94,5 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr async findPackagePolicyEndpointCustomConfiguration(onEditPage: boolean = false) { return await testSubjects.find(`endpointPackagePolicy_${onEditPage ? 'edit' : 'create'}`); }, - - /** - * Finds and returns the onboarding button displayed in empty List pages - */ - async findOnboardingStartButton() { - await testSubjects.waitForEnabled('onboardingStartButton'); - return await testSubjects.find('onboardingStartButton'); - }, }; }