diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts index a52fc205813f7..da3f387016b9f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts @@ -12,6 +12,61 @@ const OS_FAMILY = ['windows', 'macos', 'linux']; /** Array of 14 day offsets */ const DAY_OFFSETS = Array.from({ length: 14 }, (_, i) => 8.64e7 * (i + 1)); +const USERS = [ + 'elastic', + 'shay', + 'Damian', + 'Sarai', + 'Deirdre', + 'Shawana', + 'Treena', + 'Ellamae', + 'Myriam', + 'Roberto', + 'Cordell', + 'Demetrice', + 'Audrea', + 'Shanel', + 'Gail', + 'Hermila', + 'Mara', + 'Elden', + 'Malisa', + 'Derick', + 'Teddy', + 'Dovie', + 'Betty', + 'Kay', + 'Sharice', + 'Evalyn', + 'Teressa', + 'Teisha', + 'Marianne', + 'Cherelle', + 'Tabitha', + 'Deneen', + 'Leo', + 'Tess', + 'Clair', + 'Marty', + 'Dexter', + 'Candis', + 'Dina', + 'Bennett', + 'Vesta', + 'Trinity', + 'Drusilla', + 'Bree', + 'Bryon', + 'Johnson', + 'Justa', + 'Jada', + 'Armand', + 'Raeann', + 'Yolande', + 'Genevieve', +]; + /** * A generic base class to assist in creating domain specific data generators. It includes * several general purpose random data generators for use within the class and exposes one @@ -36,6 +91,10 @@ export class BaseDataGenerator { throw new Error('method not implemented!'); } + public randomUser(): string { + return this.randomChoice(USERS); + } + /** Returns a future ISO date string */ protected randomFutureDate(from?: Date): string { const now = from ? from.getTime() : Date.now(); diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/trusted_app_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/trusted_app_generator.ts new file mode 100644 index 0000000000000..be0178b83be90 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/trusted_app_generator.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeepPartial } from 'utility-types'; +import { merge } from 'lodash'; +import { BaseDataGenerator } from './base_data_generator'; +import { ConditionEntryField, EffectScope, NewTrustedApp, TrustedApp } from '../types'; + +const TRUSTED_APP_NAMES = [ + 'Symantec Endpoint Security', + 'Bitdefender GravityZone', + 'Malwarebytes', + 'Sophos Intercept X', + 'Webroot Business Endpoint Protection', + 'ESET Endpoint Security', + 'FortiClient', + 'Kaspersky Endpoint Security', + 'Trend Micro Apex One', + 'CylancePROTECT', + 'VIPRE', + 'Norton', + 'McAfee Endpoint Security', + 'AVG AntiVirus', + 'CrowdStrike Falcon', + 'Avast Business Antivirus', + 'Avira Antivirus', + 'Cisco AMP for Endpoints', + 'Eset Endpoint Antivirus', + 'VMware Carbon Black', + 'Palo Alto Networks Traps', + 'Trend Micro', + 'SentinelOne', + 'Panda Security for Desktops', + 'Microsoft Defender ATP', +]; + +const EFFECT_SCOPE_TYPES = ['policy', 'global']; + +export class TrustedAppGenerator extends BaseDataGenerator { + generate(overrides: DeepPartial = {}): TrustedApp { + return merge( + this.generateTrustedAppForCreate(), + { + id: this.randomUUID(), + version: this.randomString(5), + created_at: this.randomPastDate(), + updated_at: new Date().toISOString(), + created_by: this.randomUser(), + updated_by: this.randomUser(), + }, + overrides + ); + } + + generateTrustedAppForCreate({ + effectScope: effectiveScopeOverride, + ...overrides + }: DeepPartial = {}): NewTrustedApp { + const name = this.randomChoice(TRUSTED_APP_NAMES); + const scopeType = this.randomChoice(EFFECT_SCOPE_TYPES); + const effectScope = (effectiveScopeOverride ?? { + type: scopeType, + ...(scopeType === 'policy' ? { policies: this.randomArray(5, () => this.randomUUID()) } : {}), + }) as EffectScope; + + // TODO: remove ts-ignore. TS types are conditional when it comes to the combination of OS and ENTRIES + // @ts-ignore + return merge( + { + description: `Generator says we trust ${name}`, + name, + os: this.randomOSFamily(), + effectScope, + entries: [ + { + field: ConditionEntryField.HASH, + operator: 'included', + type: 'match', + value: '1234234659af249ddf3e40864e9fb241', + }, + { + field: ConditionEntryField.PATH, + operator: 'included', + type: 'match', + value: '/one/two/three', + }, + ], + }, + overrides + ); + } +} diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts index 401a1b70dec4f..985fa2c4aadcf 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts @@ -19,11 +19,17 @@ import { } from '../../../../fleet/common'; import { EndpointDataLoadingError, wrapErrorAndRejectPromise } from './utils'; +export interface SetupFleetForEndpointResponse { + endpointPackage: BulkInstallPackageInfo; +} + /** * Calls the fleet setup APIs and then installs the latest Endpoint package * @param kbnClient */ -export const setupFleetForEndpoint = async (kbnClient: KbnClient) => { +export const setupFleetForEndpoint = async ( + kbnClient: KbnClient +): Promise => { // We try to use the kbnClient **private** logger, bug if unable to access it, then just use console // @ts-ignore const log = kbnClient.log ? kbnClient.log : console; @@ -65,12 +71,16 @@ export const setupFleetForEndpoint = async (kbnClient: KbnClient) => { } // Install/upgrade the endpoint package + let endpointPackage: BulkInstallPackageInfo; + try { - await installOrUpgradeEndpointFleetPackage(kbnClient); + endpointPackage = await installOrUpgradeEndpointFleetPackage(kbnClient); } catch (error) { log.error(error); throw error; } + + return { endpointPackage }; }; /** @@ -78,7 +88,9 @@ export const setupFleetForEndpoint = async (kbnClient: KbnClient) => { * * @param kbnClient */ -export const installOrUpgradeEndpointFleetPackage = async (kbnClient: KbnClient): Promise => { +export const installOrUpgradeEndpointFleetPackage = async ( + kbnClient: KbnClient +): Promise => { const installEndpointPackageResp = (await kbnClient .request({ path: EPM_API_ROUTES.BULK_INSTALL_PATTERN, @@ -108,6 +120,8 @@ export const installOrUpgradeEndpointFleetPackage = async (kbnClient: KbnClient) throw new EndpointDataLoadingError(bulkResp[0].error, bulkResp); } + + return bulkResp[0] as BulkInstallPackageInfo; }; function isFleetBulkInstallError( diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 780e746946138..3a2eaf2763c4f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -17,6 +17,16 @@ export * from './trusted_apps'; * Supported React-Router state for the Policy Details page */ export interface PolicyDetailsRouteState { + /** + * Override the "back link" displayed at the top-left corner of page with custom routing + */ + backLink?: { + /** link label text */ + label: string; + navigateTo: Parameters; + href?: string; + }; + /** * Where the user should be redirected to when the `Save` button is clicked and the update was successful */ diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/formatted_date_time.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/formatted_date_time.tsx index 2fdb7e99d860e..8b8c37f4b8f63 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/formatted_date_time.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/formatted_date_time.tsx @@ -8,6 +8,9 @@ import React from 'react'; import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n/react'; +/** + * @deprecated consider using `FormattedDate` from `x-pack/plugins/security_solution/public/common/components/formatted_date/index.tsx:95` + */ export const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { // If date is greater than or equal to 1h (ago), then show it as a date // else, show it as relative to "now" diff --git a/x-pack/plugins/security_solution/public/common/components/formatted_date/index.tsx b/x-pack/plugins/security_solution/public/common/components/formatted_date/index.tsx index d6bc837eecca2..be11098c0d0ea 100644 --- a/x-pack/plugins/security_solution/public/common/components/formatted_date/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/formatted_date/index.tsx @@ -114,36 +114,51 @@ export const FormattedDate = React.memo<{ FormattedDate.displayName = 'FormattedDate'; +export interface FormattedRelativePreferenceDateProps { + value?: string | number | null; + /** + * Set dateFormat if wanting to override the Kibana system default for when displaying the non-relative date. + * For a list of tokens that can be used to create a date format + * @see https://momentjs.com/docs/#/displaying/format/ + */ + dateFormat?: string; +} /** * Renders the specified date value according to under/over one hour * Under an hour = relative format - * Over an hour = in a format determined by the user's preferences, + * Over an hour = in a format determined by the user's preferences (can be overridden via prop), * with a tooltip that renders: * - the name of the field * - a humanized relative date (e.g. 16 minutes ago) * - a long representation of the date that includes the day of the week (e.g. Thursday, March 21, 2019 6:47pm) * - the raw date value (e.g. 2019-03-22T00:47:46Z) */ - -export const FormattedRelativePreferenceDate = ({ value }: { value?: string | number | null }) => { - if (value == null) { - return getOrEmptyTagFromValue(value); - } - const maybeDate = getMaybeDate(value); - if (!maybeDate.isValid()) { - return getOrEmptyTagFromValue(value); +export const FormattedRelativePreferenceDate = React.memo( + ({ value, dateFormat }) => { + if (value == null) { + return getOrEmptyTagFromValue(value); + } + const maybeDate = getMaybeDate(value); + if (!maybeDate.isValid()) { + return getOrEmptyTagFromValue(value); + } + const date = maybeDate.toDate(); + return ( + + {moment(date).add(1, 'hours').isBefore(new Date()) ? ( + + ) : ( + + )} + + ); } - const date = maybeDate.toDate(); - return ( - - {moment(date).add(1, 'hours').isBefore(new Date()) ? ( - - ) : ( - - )} - - ); -}; +); +FormattedRelativePreferenceDate.displayName = 'FormattedRelativePreferenceDate'; /** * Renders a preceding label according to under/over one hour diff --git a/x-pack/plugins/security_solution/public/management/components/actions_context_menu/actions_context_menu.tsx b/x-pack/plugins/security_solution/public/management/components/actions_context_menu/actions_context_menu.tsx new file mode 100644 index 0000000000000..7d46c7c80677d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/actions_context_menu/actions_context_menu.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useCallback, useMemo, useState } from 'react'; +import { + EuiButtonIcon, + EuiContextMenuPanel, + EuiPopover, + EuiPopoverProps, + EuiContextMenuPanelProps, + EuiIconProps, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + ContextMenuItemNavByRouter, + ContextMenuItemNavByRouterProps, +} from '../context_menu_with_router_support/context_menu_item_nav_by_rotuer'; +import { useTestIdGenerator } from '../hooks/use_test_id_generator'; + +export interface ActionsContextMenuProps { + items: ContextMenuItemNavByRouterProps[]; + /** Default icon is `boxesHorizontal` */ + icon?: EuiIconProps['type']; + 'data-test-subj'?: string; +} + +/** + * Display a context menu behind a icon button (which defaults to the three horizontal dots icon) + */ +export const ActionsContextMenu = memo( + ({ items, 'data-test-subj': dataTestSubj, icon = 'boxesHorizontal' }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + const [isOpen, setIsOpen] = useState(false); + + const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); + const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); + + const panelProps: EuiPopoverProps['panelProps'] = useMemo(() => { + return { 'data-test-subj': getTestId('popoverPanel') }; + }, [getTestId]); + + const menuItems: EuiContextMenuPanelProps['items'] = useMemo(() => { + return items.map((itemProps) => { + return ( + { + handleCloseMenu(); + if (itemProps.onClick) { + return itemProps.onClick(ev); + } + }} + /> + ); + }); + }, [handleCloseMenu, items]); + + return ( + + } + isOpen={isOpen} + closePopover={handleCloseMenu} + > + + + ); + } +); +ActionsContextMenu.displayName = 'ActionsContextMenu'; diff --git a/x-pack/plugins/security_solution/public/management/components/actions_context_menu/index.ts b/x-pack/plugins/security_solution/public/management/components/actions_context_menu/index.ts new file mode 100644 index 0000000000000..d792efed4404b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/actions_context_menu/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './actions_context_menu'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx new file mode 100644 index 0000000000000..14c4e6b947988 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/artifact_entry_card.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo } from 'react'; +import { CommonProps, EuiHorizontalRule, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; +import styled from 'styled-components'; +import { CardHeader, CardHeaderProps } from './components/card_header'; +import { CardSubHeader } from './components/card_sub_header'; +import { getEmptyValue } from '../../../common/components/empty_value'; +import { CriteriaConditions, CriteriaConditionsProps } from './components/criteria_conditions'; +import { EffectScopeProps } from './components/effect_scope'; +import { ContextMenuItemNavByRouterProps } from '../context_menu_with_router_support/context_menu_item_nav_by_rotuer'; +import { AnyArtifact } from './types'; +import { useNormalizedArtifact } from './hooks/use_normalized_artifact'; +import { useTestIdGenerator } from '../hooks/use_test_id_generator'; + +const CardContainerPanel = styled(EuiPanel)` + &.artifactEntryCard + &.artifactEntryCard { + margin-top: ${({ theme }) => theme.eui.spacerSizes.l}; + } +`; + +export interface ArtifactEntryCardProps extends CommonProps { + item: AnyArtifact; + /** + * The list of actions for the card. Will display an icon with the actions in a menu if defined. + */ + actions?: CardHeaderProps['actions']; + + /** + * Information about the policies that are assigned to the `item`'s `effectScope` and that will be + * use to create a navigation link + */ + policies?: { + [policyId: string]: ContextMenuItemNavByRouterProps; + }; +} + +/** + * Display Artifact Items (ex. Trusted App, Event Filter, etc) as a card. + * This component is a TS Generic that allows you to set what the Item type is + */ +export const ArtifactEntryCard = memo( + ({ + item, + policies, + actions, + 'data-test-subj': dataTestSubj, + ...commonProps + }: ArtifactEntryCardProps) => { + const artifact = useNormalizedArtifact(item); + const getTestId = useTestIdGenerator(dataTestSubj); + + // create the policy links for each policy listed in the artifact record by grabbing the + // navigation data from the `policies` prop (if any) + const policyNavLinks = useMemo(() => { + return artifact.effectScope.type === 'policy' + ? artifact?.effectScope.policies.map((id) => { + return policies && policies[id] + ? policies[id] + : // else, unable to build a nav link, so just show id + { + children: id, + }; + }) + : undefined; + }, [artifact.effectScope, policies]); + + return ( + + + + + + + + +

+ {artifact.description || getEmptyValue()} +

+
+
+ + + + + + +
+ ); + } +); + +ArtifactEntryCard.displayName = 'ArtifactEntryCard'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_header.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_header.tsx new file mode 100644 index 0000000000000..ca82c97d8b820 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_header.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { CommonProps, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { DateFieldValue } from './date_field_value'; +import { ActionsContextMenu, ActionsContextMenuProps } from '../../actions_context_menu'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +export interface CardHeaderProps extends Pick { + name: string; + createdDate: string; + updatedDate: string; + /** If defined, then an overflow menu will be shown with the actions provided */ + actions?: ActionsContextMenuProps['items']; +} + +export const CardHeader = memo( + ({ name, createdDate, updatedDate, actions, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + + + + +

{name}

+
+
+ + + + + + + + + + +
+
+ {actions && actions.length > 0 && ( + + + + )} +
+ ); + } +); + +CardHeader.displayName = 'CardHeader'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_sub_header.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_sub_header.tsx new file mode 100644 index 0000000000000..4bd86b9af0650 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/card_sub_header.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { CommonProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { TouchedByUsers, TouchedByUsersProps } from './touched_by_users'; +import { EffectScope, EffectScopeProps } from './effect_scope'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +export type SubHeaderProps = TouchedByUsersProps & + EffectScopeProps & + Pick; + +export const CardSubHeader = memo( + ({ createdBy, updatedBy, policies, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + + + + + + + + ); + } +); +CardSubHeader.displayName = 'CardSubHeader'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx new file mode 100644 index 0000000000000..292c06c4f5b8c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/criteria_conditions.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { CommonProps, EuiExpression } from '@elastic/eui'; +import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { + CONDITION_OS, + OS_LINUX, + OS_MAC, + OS_WINDOWS, + CONDITION_AND, + CONDITION_OPERATOR_TYPE_WILDCARD, + CONDITION_OPERATOR_TYPE_NESTED, + CONDITION_OPERATOR_TYPE_MATCH, + CONDITION_OPERATOR_TYPE_MATCH_ANY, + CONDITION_OPERATOR_TYPE_EXISTS, + CONDITION_OPERATOR_TYPE_LIST, +} from './translations'; +import { ArtifactInfo } from '../types'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +const OS_LABELS = Object.freeze({ + linux: OS_LINUX, + mac: OS_MAC, + windows: OS_WINDOWS, +}); + +const OPERATOR_TYPE_LABELS = Object.freeze({ + [ListOperatorTypeEnum.NESTED]: CONDITION_OPERATOR_TYPE_NESTED, + [ListOperatorTypeEnum.MATCH_ANY]: CONDITION_OPERATOR_TYPE_MATCH_ANY, + [ListOperatorTypeEnum.MATCH]: CONDITION_OPERATOR_TYPE_MATCH, + [ListOperatorTypeEnum.WILDCARD]: CONDITION_OPERATOR_TYPE_WILDCARD, + [ListOperatorTypeEnum.EXISTS]: CONDITION_OPERATOR_TYPE_EXISTS, + [ListOperatorTypeEnum.LIST]: CONDITION_OPERATOR_TYPE_LIST, +}); + +export type CriteriaConditionsProps = Pick & + Pick; + +export const CriteriaConditions = memo( + ({ os, entries, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( +
+
+ + + + +
+ {entries.map(({ field, type, value }) => { + return ( +
+ + +
+ ); + })} +
+ ); + } +); +CriteriaConditions.displayName = 'CriteriaConditions'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/date_field_value.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/date_field_value.tsx new file mode 100644 index 0000000000000..21a8be8a5ad63 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/date_field_value.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { CommonProps, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { CREATED, LAST_UPDATED } from './translations'; +import { + FormattedRelativePreferenceDate, + FormattedRelativePreferenceDateProps, +} from '../../../../common/components/formatted_date'; +import { TextValueDisplay } from './text_value_display'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +export interface DateFieldProps extends Pick { + date: FormattedRelativePreferenceDateProps['value']; + type: 'update' | 'create'; +} + +export const DateFieldValue = memo( + ({ date, type, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + + + + + + + {type === 'update' ? LAST_UPDATED : CREATED} + + + + + + + + + + ); + } +); +DateFieldValue.displayName = 'DateField'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/effect_scope.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/effect_scope.tsx new file mode 100644 index 0000000000000..e11c041c413d0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/effect_scope.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, PropsWithChildren, useMemo } from 'react'; +import { CommonProps, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; +import { GLOBAL_EFFECT_SCOPE, POLICY_EFFECT_SCOPE } from './translations'; +import { TextValueDisplay } from './text_value_display'; +import { ContextMenuWithRouterSupport } from '../../context_menu_with_router_support'; +import { ContextMenuItemNavByRouterProps } from '../../context_menu_with_router_support/context_menu_item_nav_by_rotuer'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +export interface EffectScopeProps extends Pick { + /** If set (even if empty), then effect scope will be policy specific. Else, it shows as global */ + policies?: ContextMenuItemNavByRouterProps[]; +} + +export const EffectScope = memo( + ({ policies, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + const [icon, label] = useMemo(() => { + return policies + ? ['partial', POLICY_EFFECT_SCOPE(policies.length)] + : ['globe', GLOBAL_EFFECT_SCOPE]; + }, [policies]); + + const effectiveScopeLabel = ( + + + + + + {label} + + + ); + + return policies && policies.length ? ( + + {effectiveScopeLabel} + + ) : ( + effectiveScopeLabel + ); + } +); +EffectScope.displayName = 'EffectScope'; + +type WithContextMenuProps = Pick & + PropsWithChildren<{ + policies: Required['policies']; + }>; + +export const WithContextMenu = memo( + ({ policies, children, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + {children} + + } + /> + ); + } +); +WithContextMenu.displayName = 'WithContextMenu'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/text_value_display.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/text_value_display.tsx new file mode 100644 index 0000000000000..f1d92f4c09778 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/text_value_display.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, PropsWithChildren } from 'react'; +import { EuiText } from '@elastic/eui'; + +export type TextValueDisplayProps = PropsWithChildren<{ + bold?: boolean; +}>; + +/** + * Common component for displaying consistent text across the card. Changes here could impact all of + * display of values on the card + */ +export const TextValueDisplay = memo(({ bold, children }) => { + return {bold ? {children} : children}; +}); +TextValueDisplay.displayName = 'TextValueDisplay'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/touched_by_users.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/touched_by_users.tsx new file mode 100644 index 0000000000000..d897b6caaa45d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/touched_by_users.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { CommonProps, EuiAvatar, EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { CREATED_BY, LAST_UPDATED_BY } from './translations'; +import { TextValueDisplay } from './text_value_display'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +export interface TouchedByUsersProps extends Pick { + createdBy: string; + updatedBy: string; +} + +export const TouchedByUsers = memo( + ({ createdBy, updatedBy, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + + + + + + + + ); + } +); +TouchedByUsers.displayName = 'TouchedByUsers'; + +interface UserNameProps extends Pick { + label: string; + value: string; +} + +const UserName = memo(({ label, value, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + + {label} + + + + + + + + {value} + + + + + ); +}); +UserName.displayName = 'UserName'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts new file mode 100644 index 0000000000000..4d0f981e2267f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/components/translations.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const LAST_UPDATED = i18n.translate('xpack.securitySolution.artifactCard.lastUpdated', { + defaultMessage: 'Last updated', +}); + +export const CREATED = i18n.translate('xpack.securitySolution.artifactCard.created', { + defaultMessage: 'Created', +}); + +export const LAST_UPDATED_BY = i18n.translate('xpack.securitySolution.artifactCard.lastUpdatedBy', { + defaultMessage: 'Updated by', +}); + +export const CREATED_BY = i18n.translate('xpack.securitySolution.artifactCard.createdBy', { + defaultMessage: 'Created by', +}); + +export const GLOBAL_EFFECT_SCOPE = i18n.translate( + 'xpack.securitySolution.artifactCard.globalEffectScope', + { + defaultMessage: 'Applied globally', + } +); + +export const POLICY_EFFECT_SCOPE = (policyCount = 0) => { + return i18n.translate('xpack.securitySolution.artifactCard.policyEffectScope', { + defaultMessage: 'Applied to {count} policies', + values: { + count: policyCount, + }, + }); +}; + +export const CONDITION_OPERATOR_TYPE_MATCH = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.matchOperator', + { + defaultMessage: 'IS', + } +); + +export const CONDITION_OPERATOR_TYPE_WILDCARD = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.wildcardOperator', + { + defaultMessage: 'MATCHES', + } +); + +export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.nestedOperator', + { + defaultMessage: 'has', + } +); + +export const CONDITION_OPERATOR_TYPE_MATCH_ANY = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.matchAnyOperator', + { + defaultMessage: 'is any', + } +); + +export const CONDITION_OPERATOR_TYPE_EXISTS = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.existsOperator', + { + defaultMessage: 'exists', + } +); + +export const CONDITION_OPERATOR_TYPE_LIST = i18n.translate( + 'xpack.securitySolution.artifactCard.conditions.listOperator', + { + defaultMessage: 'included in', + } +); + +export const CONDITION_OS = i18n.translate('xpack.securitySolution.artifactCard.conditions.os', { + defaultMessage: 'OS', +}); + +export const CONDITION_AND = i18n.translate('xpack.securitySolution.artifactCard.conditions.and', { + defaultMessage: 'AND', +}); + +export const OS_WINDOWS = i18n.translate('xpack.securitySolution.artifactCard.conditions.windows', { + defaultMessage: 'Windows', +}); + +export const OS_LINUX = i18n.translate('xpack.securitySolution.artifactCard.conditions.linux', { + defaultMessage: 'Linux', +}); + +export const OS_MAC = i18n.translate('xpack.securitySolution.artifactCard.conditions.macos', { + defaultMessage: 'Mac', +}); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/hooks/use_normalized_artifact.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/hooks/use_normalized_artifact.ts new file mode 100644 index 0000000000000..175731ee57acb --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/hooks/use_normalized_artifact.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { useMemo } from 'react'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { AnyArtifact, ArtifactInfo } from '../types'; +import { TrustedApp } from '../../../../../common/endpoint/types'; + +/** + * Takes in any artifact and return back a new data structure used internally with by the card's components + * + * @param item + */ +export const useNormalizedArtifact = (item: AnyArtifact): ArtifactInfo => { + return useMemo(() => { + const { + name, + created_by, + created_at, + updated_at, + updated_by, + description = '', + entries, + } = item; + return { + name, + created_by, + created_at, + updated_at, + updated_by, + description, + entries: (entries as unknown) as ArtifactInfo['entries'], + os: isTrustedApp(item) ? item.os : getOsFromExceptionItem(item), + effectScope: isTrustedApp(item) ? item.effectScope : { type: 'global' }, + }; + }, [item]); +}; + +const isTrustedApp = (item: AnyArtifact): item is TrustedApp => { + return 'effectScope' in item; +}; + +const getOsFromExceptionItem = (item: ExceptionListItemSchema): string => { + // FYI: Exceptions seem to allow for items to be assigned to more than one OS, unlike Event Filters and Trusted Apps + return item.os_types.join(', '); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts new file mode 100644 index 0000000000000..58c0e160f760d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './artifact_entry_card'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts new file mode 100644 index 0000000000000..c59a2bde94589 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/types.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { EffectScope, TrustedApp } from '../../../../common/endpoint/types'; + +export type AnyArtifact = ExceptionListItemSchema | TrustedApp; + +/** + * A normalized structured that is used internally through out the card's components. + */ +export interface ArtifactInfo + extends Pick< + ExceptionListItemSchema, + 'name' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by' | 'description' + > { + effectScope: EffectScope; + os: string; + entries: Array<{ + field: string; + type: string; + operator: string; + value: string; + }>; +} diff --git a/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx index 78c854d933584..c71eea2aaf9db 100644 --- a/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx +++ b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx @@ -7,7 +7,7 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiButtonEmpty } from '@elastic/eui'; +import { CommonProps, EuiButtonEmpty } from '@elastic/eui'; import styled from 'styled-components'; import { ListPageRouteState } from '../../../../common/endpoint/types'; @@ -15,7 +15,7 @@ import { ListPageRouteState } from '../../../../common/endpoint/types'; import { useNavigateToAppEventHandler } from '../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; const EuiButtonEmptyStyled = styled(EuiButtonEmpty)` - margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; + margin-bottom: ${({ theme }) => theme.eui.paddingSizes.s}; .euiIcon { width: ${({ theme }) => theme.eui.euiIconSizes.small}; @@ -24,22 +24,25 @@ const EuiButtonEmptyStyled = styled(EuiButtonEmpty)` .text { font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; + margin-inline-start: ${({ theme }) => theme.eui.paddingSizes.xs}; } `; -export const BackToExternalAppButton = memo( - ({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo }) => { +export type BackToExternalAppButtonProps = CommonProps & ListPageRouteState; +export const BackToExternalAppButton = memo( + ({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo, ...commonProps }) => { const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!); return ( {backButtonLabel || ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/context_menu_item_nav_by_rotuer.tsx b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_item_nav_by_rotuer.tsx similarity index 66% rename from x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/context_menu_item_nav_by_rotuer.tsx rename to x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_item_nav_by_rotuer.tsx index f4f9a7542415d..cc95235831c2e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/context_menu_item_nav_by_rotuer.tsx +++ b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_item_nav_by_rotuer.tsx @@ -8,11 +8,13 @@ import React, { memo } from 'react'; import { EuiContextMenuItem, EuiContextMenuItemProps } from '@elastic/eui'; import { NavigateToAppOptions } from 'kibana/public'; -import { useNavigateToAppEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; +import { useNavigateToAppEventHandler } from '../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; export interface ContextMenuItemNavByRouterProps extends EuiContextMenuItemProps { - navigateAppId: string; - navigateOptions: NavigateToAppOptions; + /** The Kibana (plugin) app id */ + navigateAppId?: string; + /** Additional options for the navigation action via react-router */ + navigateOptions?: NavigateToAppOptions; children: React.ReactNode; } @@ -22,13 +24,16 @@ export interface ContextMenuItemNavByRouterProps extends EuiContextMenuItemProps */ export const ContextMenuItemNavByRouter = memo( ({ navigateAppId, navigateOptions, onClick, children, ...otherMenuItemProps }) => { - const handleOnClick = useNavigateToAppEventHandler(navigateAppId, { + const handleOnClickViaNavigateToApp = useNavigateToAppEventHandler(navigateAppId ?? '', { ...navigateOptions, onClick, }); return ( - + {children} ); diff --git a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx new file mode 100644 index 0000000000000..8fbb7eca60a38 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useCallback, useMemo, useState } from 'react'; +import { + CommonProps, + EuiContextMenuPanel, + EuiContextMenuPanelProps, + EuiPopover, + EuiPopoverProps, +} from '@elastic/eui'; +import { + ContextMenuItemNavByRouter, + ContextMenuItemNavByRouterProps, +} from './context_menu_item_nav_by_rotuer'; +import { useTestIdGenerator } from '../hooks/use_test_id_generator'; + +export interface ContextMenuWithRouterSupportProps + extends CommonProps, + Pick { + items: ContextMenuItemNavByRouterProps[]; +} + +/** + * A context menu that allows for items in the menu to route to other Kibana destinations using the Router + * (thus avoiding full page refreshes). + * Menu also supports automatically closing the popup when an item is clicked. + */ +export const ContextMenuWithRouterSupport = memo( + ({ items, button, panelPaddingSize, anchorPosition, ...commonProps }) => { + const getTestId = useTestIdGenerator(commonProps['data-test-subj']); + const [isOpen, setIsOpen] = useState(false); + + const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); + const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); + + const panelProps: EuiPopoverProps['panelProps'] = useMemo(() => { + return { 'data-test-subj': getTestId('popoverPanel') }; + }, [getTestId]); + + const menuItems: EuiContextMenuPanelProps['items'] = useMemo(() => { + return items.map((itemProps) => { + return ( + { + handleCloseMenu(); + if (itemProps.onClick) { + return itemProps.onClick(ev); + } + }} + /> + ); + }); + }, [handleCloseMenu, items]); + + return ( + + {button} + + } + isOpen={isOpen} + closePopover={handleCloseMenu} + > + + + ); + } +); +ContextMenuWithRouterSupport.displayName = 'ContextMenuWithRouterSupport'; diff --git a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/index.ts b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/index.ts new file mode 100644 index 0000000000000..56c6009ccf1b2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './context_menu_with_router_support'; diff --git a/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx b/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx index a6b2683316efe..ee0bf02f730f4 100644 --- a/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx +++ b/x-pack/plugins/security_solution/public/management/components/paginated_content/paginated_content.tsx @@ -153,11 +153,14 @@ export const PaginatedContent = memo( [pagination?.pageSize, pagination?.totalItemCount] ); + // If loading is done, + // then check to ensure the pagination numbers are correct based by ensuring that the + // `pageIndex` is not higher than the number of available pages. useEffect(() => { - if (pageCount > 0 && pageCount < (pagination?.pageIndex || 0) + 1) { + if (!loading && pageCount > 0 && pageCount < (pagination?.pageIndex || 0) + 1) { onChange({ pageIndex: pageCount - 1, pageSize: pagination?.pageSize || 0 }); } - }, [pageCount, onChange, pagination]); + }, [pageCount, onChange, pagination, loading]); const handleItemsPerPageChange: EuiTablePaginationProps['onChangeItemsPerPage'] = useCallback( (pageSize) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx index 5a2ad6cf4c60b..6df5413c1eb3c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx @@ -14,7 +14,7 @@ import { EuiPopoverProps, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ContextMenuItemNavByRouter } from './context_menu_item_nav_by_rotuer'; +import { ContextMenuItemNavByRouter } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_rotuer'; import { HostMetadata } from '../../../../../../common/endpoint/types'; import { useEndpointActionItems } from '../hooks'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx index c778f4f2a08ec..412db3dc2a63e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx @@ -10,7 +10,7 @@ import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { useEndpointActionItems, useEndpointSelector } from '../../hooks'; import { detailsData } from '../../../store/selectors'; -import { ContextMenuItemNavByRouter } from '../../components/context_menu_item_nav_by_rotuer'; +import { ContextMenuItemNavByRouter } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_rotuer'; export const ActionsMenu = React.memo<{}>(() => { const endpointDetails = useEndpointSelector(detailsData); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx index b15c6b9ba0020..bbe0a6f3afcd1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx @@ -144,9 +144,7 @@ export const LogEntry = memo(({ logEntry }: { logEntry: Immutable} event={{displayResponseEvent ? responseEventTitle : actionEventTitle}} timelineIcon={ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 34423767578d9..8f19fea818fc6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -14,7 +14,7 @@ import { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/ import { useEndpointSelector } from './hooks'; import { agentPolicies, uiQueryParams } from '../../store/selectors'; import { useAppUrl } from '../../../../../common/lib/kibana/hooks'; -import { ContextMenuItemNavByRouterProps } from '../components/context_menu_item_nav_by_rotuer'; +import { ContextMenuItemNavByRouterProps } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_rotuer'; import { isEndpointHostIsolated } from '../../../../../common/utils/validators'; import { useLicense } from '../../../../../common/hooks/use_license'; import { isIsolationSupported } from '../../../../../../common/endpoint/service/host_isolation/utils'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts index 7d296c2ea0ae0..6d767df73cd1c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts @@ -72,14 +72,6 @@ export interface PolicyDetailsState { license?: ILicense; } -/** - * The URL search params that are supported by the Policy List page view - */ -export interface PolicyListUrlSearchParams { - page_index: number; - page_size: number; -} - export enum OS { windows = 'windows', mac = 'mac', 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 0aed93500453b..3292bc0c44cb9 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 @@ -98,9 +98,8 @@ describe('Policy Details', () => { it('should display back to list button and policy title', () => { policyView.update(); - const backToListLink = policyView.find('LinkIcon[dataTestSubj="policyDetailsBackLink"]'); - expect(backToListLink.prop('iconType')).toBe('arrowLeft'); - expect(backToListLink.prop('href')).toBe(`/app/security${endpointListPath}`); + const backToListLink = policyView.find('BackToExternalAppButton'); + expect(backToListLink.prop('backButtonUrl')).toBe(`/app/security${endpointListPath}`); expect(backToListLink.text()).toBe('Back to endpoint hosts'); const pageTitle = policyView.find('span[data-test-subj="header-page-title"]'); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 4538e86a841d9..660dda6493c39 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -5,23 +5,32 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; +import { useLocation } from 'react-router-dom'; import { usePolicyDetailsSelector } from './policy_hooks'; import { policyDetails, agentStatusSummary } from '../store/policy_details/selectors'; import { AgentsSummary } from './agents_summary'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -import { SecurityPageName } from '../../../../app/types'; -import { HeaderLinkBack } from '../../../../common/components/header_page'; import { PolicyTabs } from './tabs'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { PolicyFormLayout } from './policy_forms/components'; +import { + BackToExternalAppButton, + BackToExternalAppButtonProps, +} from '../../../components/back_to_external_app_button/back_to_external_app_button'; +import { PolicyDetailsRouteState } from '../../../../../common/endpoint/types'; +import { getEndpointListPath } from '../../../common/routing'; +import { useAppUrl } from '../../../../common/lib/kibana'; +import { APP_ID } from '../../../../../common/constants'; export const PolicyDetails = React.memo(() => { // TODO: Remove this and related code when removing FF const isTrustedAppsByPolicyEnabled = useIsExperimentalFeatureEnabled( 'trustedAppsByPolicyEnabled' ); + const { state: routeState = {} } = useLocation(); + const { getAppUrl } = useAppUrl(); // Store values const policyItem = usePolicyDetailsSelector(policyDetails); @@ -31,6 +40,34 @@ export const PolicyDetails = React.memo(() => { const policyName = policyItem?.name ?? ''; const policyDescription = policyItem?.description ?? undefined; + const backLinkOptions = useMemo(() => { + if (routeState?.backLink) { + return { + onBackButtonNavigateTo: routeState.backLink.navigateTo, + backButtonLabel: routeState.backLink.label, + backButtonUrl: routeState.backLink.href, + }; + } + + const endpointListPath = getEndpointListPath({ name: 'endpointList' }); + + return { + backButtonLabel: i18n.translate( + 'xpack.securitySolution.endpoint.policy.details.backToListTitle', + { + defaultMessage: 'Back to endpoint hosts', + } + ), + backButtonUrl: getAppUrl({ path: endpointListPath }), + onBackButtonNavigateTo: [ + APP_ID, + { + path: endpointListPath, + }, + ], + }; + }, [getAppUrl, routeState?.backLink]); + const headerRightContent = ( { ); const backToEndpointList = ( - + ); return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts index 388d8b5e6ed66..1565dc1310b18 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts @@ -7,7 +7,12 @@ import { createSelector } from 'reselect'; import { ServerApiError } from '../../../../common/types'; -import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types'; +import { + Immutable, + NewTrustedApp, + PolicyData, + TrustedApp, +} from '../../../../../common/endpoint/types'; import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants'; import { @@ -226,6 +231,18 @@ export const listOfPolicies: ( return isLoadedResourceState(policies) ? policies.data.items : []; }); +export const getMapOfPoliciesById: ( + state: Immutable +) => Immutable>> = createSelector( + listOfPolicies, + (policies) => { + return policies.reduce>>((mapById, policy) => { + mapById[policy.id] = policy; + return mapById; + }, {}) as Immutable>>; + } +); + export const isEdit: (state: Immutable) => boolean = createSelector( getCurrentLocation, ({ show }) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap deleted file mode 100644 index cbeea78f51040..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,249 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`trusted_app_card TrustedAppCard should render correctly 1`] = ` - - - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - - - Edit - - - Remove - - -`; - -exports[`trusted_app_card TrustedAppCard should trim long texts 1`] = ` - - - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - - - Edit - - - Remove - - -`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx deleted file mode 100644 index e2e426caa1654..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { storiesOf, addDecorator } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; - -import { KibanaContextProvider } from '../../../../../../../../../../src/plugins/kibana_react/public'; -import { - ConditionEntryField, - TrustedApp, - WindowsConditionEntry, -} from '../../../../../../../common/endpoint/types'; - -import { createSampleTrustedApp } from '../../../test_utils'; - -import { TrustedAppCard } from '.'; - -addDecorator((storyFn) => ( - 'MMM D, YYYY @ HH:mm:ss.SSS' } }}> - ({ eui: euiLightVars, darkMode: false })}> - {storyFn()} - - -)); - -const PATH_CONDITION: WindowsConditionEntry = { - field: ConditionEntryField.PATH, - operator: 'included', - type: 'match', - value: '/some/path/on/file/system', -}; - -const SIGNER_CONDITION: WindowsConditionEntry = { - field: ConditionEntryField.SIGNER, - operator: 'included', - type: 'match', - value: 'Elastic', -}; - -storiesOf('TrustedApps/TrustedAppCard', module) - .add('default', () => { - const trustedApp: TrustedApp = createSampleTrustedApp(5); - trustedApp.created_at = '2020-09-17T14:52:33.899Z'; - trustedApp.entries = [PATH_CONDITION]; - - return ( - - ); - }) - .add('multiple entries', () => { - const trustedApp: TrustedApp = createSampleTrustedApp(5); - trustedApp.created_at = '2020-09-17T14:52:33.899Z'; - trustedApp.entries = [PATH_CONDITION, SIGNER_CONDITION]; - - return ( - - ); - }) - .add('longs texts', () => { - const trustedApp: TrustedApp = createSampleTrustedApp(5, true); - trustedApp.created_at = '2020-09-17T14:52:33.899Z'; - trustedApp.entries = [PATH_CONDITION, SIGNER_CONDITION]; - - return ( - - ); - }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.test.tsx deleted file mode 100644 index 0b1d8e0d7ac98..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; - -import { TrustedAppCard } from '.'; -import { createSampleTrustedApp } from '../../../test_utils'; - -describe('trusted_app_card', () => { - describe('TrustedAppCard', () => { - it('should render correctly', () => { - const element = shallow( - {}} - onEdit={() => {}} - /> - ); - - expect(element).toMatchSnapshot(); - }); - - it('should trim long texts', () => { - const element = shallow( - {}} - onEdit={() => {}} - /> - ); - - expect(element).toMatchSnapshot(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx deleted file mode 100644 index 419d8aaedfe03..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useCallback, useMemo } from 'react'; -import { isEmpty } from 'lodash/fp'; -import { EuiTableFieldDataColumnType } from '@elastic/eui'; - -import { - Immutable, - TrustedApp, - MacosLinuxConditionEntry, - WindowsConditionEntry, -} from '../../../../../../../common/endpoint/types'; - -import { FormattedDate } from '../../../../../../common/components/formatted_date'; -import { ConditionsTable } from '../../../../../../common/components/conditions_table'; -import { TextFieldValue } from '../../../../../../common/components/text_field_value'; -import { - ItemDetailsAction, - ItemDetailsCard, - ItemDetailsCardProps, - ItemDetailsPropertySummary, -} from '../../../../../../common/components/item_details_card'; - -import { - OS_TITLES, - PROPERTY_TITLES, - ENTRY_PROPERTY_TITLES, - CARD_DELETE_BUTTON_LABEL, - CONDITION_FIELD_TITLE, - OPERATOR_TITLES, - CARD_EDIT_BUTTON_LABEL, -} from '../../translations'; - -type Entry = MacosLinuxConditionEntry | WindowsConditionEntry; - -const getEntriesColumnDefinitions = (): Array> => [ - { - field: 'field', - name: ENTRY_PROPERTY_TITLES.field, - sortable: false, - truncateText: true, - textOnly: true, - width: '30%', - render(field: Entry['field'], _entry: Entry) { - return CONDITION_FIELD_TITLE[field]; - }, - }, - { - field: 'operator', - name: ENTRY_PROPERTY_TITLES.operator, - sortable: false, - truncateText: true, - width: '20%', - render(_field: Entry['operator'], entry: Entry) { - return entry.type === 'wildcard' ? OPERATOR_TITLES.matches : OPERATOR_TITLES.is; - }, - }, - { - field: 'value', - name: ENTRY_PROPERTY_TITLES.value, - sortable: false, - width: '60%', - 'data-test-subj': 'conditionValue', - render(field: Entry['value'], entry: Entry) { - return ( - - ); - }, - }, -]; - -export type TrustedAppCardProps = Pick & { - trustedApp: Immutable; - onDelete: (trustedApp: Immutable) => void; - onEdit: (trustedApp: Immutable) => void; -}; - -export const TrustedAppCard = memo( - ({ trustedApp, onDelete, onEdit, ...otherProps }) => { - const handleDelete = useCallback(() => onDelete(trustedApp), [onDelete, trustedApp]); - const handleEdit = useCallback(() => onEdit(trustedApp), [onEdit, trustedApp]); - - return ( - - - } - /> - } - /> - - } - /> - - } - /> - - } - /> - - } - /> - {!isEmpty(trustedApp.description) && ( - - } - /> - )} - - getEntriesColumnDefinitions(), [])} - items={useMemo(() => [...trustedApp.entries], [trustedApp.entries])} - badge="and" - className="trustedAppsConditionsTable" - responsive - /> - - - {CARD_EDIT_BUTTON_LABEL} - - - - {CARD_DELETE_BUTTON_LABEL} - - - ); - } -); - -TrustedAppCard.displayName = 'TrustedAppCard'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap index 1355594831a24..3087914a438ef 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap @@ -372,35 +372,6 @@ exports[`TrustedAppsGrid renders correctly when failed loading data for the seco `; exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` -.c2 { - background-color: #f5f7fa; - padding: 16px; -} - -.c5 { - padding: 12px 24px 24px 0; -} - -.c5.c5.c5 { - margin-left: 0; -} - -.c5 .trustedAppsConditionsTable { - margin-left: 16px; -} - -.c3.c3.c3 { - width: 40%; - margin-top: 0; - margin-bottom: 8px; -} - -.c4.c4.c4 { - width: 60%; - margin-top: 0; - margin-bottom: 8px; -} - .c1 { position: relative; padding-top: 4px; @@ -414,6 +385,10 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` position: relative; } +.c3.artifactEntryCard + .c2.artifactEntryCard { + margin-top: 24px; +} + .c0 .trusted-app + .trusted-app { margin-top: 24px; } @@ -432,3194 +407,4074 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` class="body-content undefined" >
-
-
- Name -
-
- - +

trusted app 0 - - -

-
- OS -
-
- - - Windows - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
- - - someone - - -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - - someone - - -
-
- Description -
-
- +
+
- - Trusted App 0 - - - - -
-
-
-
-
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - + + +
+
+
+ +
+
+
+
+
+ Created +
+
+
+
-
- - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
+ + 1 minute ago + +
+
+
+
+
+
+
+ +
+
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
+
+ +
+
+
+ Applied globally +
+
+
+
+
+
+
+

+ Trusted App 0 +

- -
+
-
-
- Name -
-
- - - trusted app 1 - - -
-
- OS -
-
- - - Mac - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
+ - - someone + + + + OS -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - someone + + IS - -
-
- Description -
-
- - - Trusted App 1 + + + Windows -
-
+ +
+
+ +
+
-
+
+

+ trusted app 1 +

+
+
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - - - - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
-
-
-
-
-
-
-
- -
-
-
-
- +
+
+ Created +
+
+
+
+ + 1 minute ago + +
+
+
+
+
-
-
- -
-
-
-
-
- Name -
-
- - - trusted app 2 - - -
-
- OS -
-
- - - Linux - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
- - - someone - - -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 2 - - -
-
+ +
+
+
-
+
+ + + + Created by + + + +
+
-
+
- +
+
+ someone
- - - - - - - - - - - - - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
-
-
-
- +
-
-
-
- + +
+
+
+
+ someone +
+
+
+
+
+
+ +
+
+
+ Applied globally +
+
+
+
+
+
+
+

+ Trusted App 1 +

-
-
+
-
-
- Name -
-
- - - trusted app 3 - - -
-
- OS -
-
- - - Windows - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
+ - - someone + + + + OS -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - someone + + IS - -
-
- Description -
-
- - - Trusted App 3 + + + macos -
-
+ +
+
+ +
+
-
+
+

+ trusted app 2 +

+
+
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - + + +
+
+
+ +
+
+
+
+
+ Created +
+
+
+
-
- - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
+ + 1 minute ago + +
+
+
+
+
+
+
+ +
+
+
+
+
+
-
- +
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
- Name -
-
- - - trusted app 4 - - -
-
- OS -
-
- - - Mac - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
- - - someone - - -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 4 - - -
-
-
-
-
-
-
-
-
-
-
+ +
+
+
-
-
-
-
+ someone
- - - - - - - - - - - - - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
-
-
-
- +
-
-
-
- + +
+
+
+
+ someone +
+
+
+
+
+
+ +
+
+
+ Applied globally +
+
+
+
+
+
+
+

+ Trusted App 2 +

- -
+
-
-
- Name -
-
- - - trusted app 5 - - -
-
- OS -
-
- - - Linux - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
+ - - someone + + + + OS -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - someone + + IS - -
-
- Description -
-
- - - Trusted App 5 + + + Linux -
-
+ +
+
+ +
+
-
+
+

+ trusted app 3 +

+
+
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - + + +
+
+
+ +
+
+
+
+
+ Created +
+
+
+
-
- - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
+ + 1 minute ago + +
+
+
+
+
+
+
+ +
+
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
-
- +
-
-
-
-
-
-
- -
-
-
-
-
- Name -
-
- - - trusted app 6 - - -
-
- OS -
-
- - - Windows - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
- - - someone - - -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 6 - - -
-
-
-
-
-
-
-
-
+
- +
+
+ someone
- - - - - - - - - - - - - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
+
+
+ +
+
-
- -
-
-
-
- -
+ Applied globally
+
+
+

+ Trusted App 3 +

+
-
-
+
-
-
- Name -
-
- - - trusted app 7 - - -
-
- OS -
-
- - - Mac - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
+ - - someone + + + + OS -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - someone + + IS - -
-
- Description -
-
- - - Trusted App 7 + + + Windows -
-
+ +
+
+
+
+
-
+
+

+ trusted app 4 +

+
+
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - + + +
+
+
+ +
+
+
+
+
-
- - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
+ Created +
+
+
+
+ + 1 minute ago + +
+
+
+
+
+
+
+ +
+
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
-
-
-
-
-
-
-
-
- Name -
-
- - - trusted app 8 - - -
-
- OS -
-
- +
+
- - Linux - - - -
- Date created -
-
- 1 minute ago -
-
- Created by -
-
+
+ Applied globally +
+
+
+
+
+
+
+

+ Trusted App 4 +

+
+
+
+
+
+
+ - - someone + + + + OS - -
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - someone + + IS - -
-
- Description -
-
- - - Trusted App 8 + + + macos -
- +
+
+
+
+
+
-
+
+

+ trusted app 5 +

+
+
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - + + +
+
+
+ +
+
+
+
+
+ Created +
+
+
+
-
- - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
+ + 1 minute ago + +
+
+
+
+
+
+
+ +
+
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
-
- + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
+
+ +
+
+
+ Applied globally +
+
+
+
+
+
+
+

+ Trusted App 5 +

-
-
+
-
-
- Name -
-
- - - trusted app 9 - - -
-
- OS -
-
- - - Windows - - -
-
- Date created -
-
- 1 minute ago -
-
- Created by -
-
+ - - someone + + + + OS -
-
- Date modified -
-
- 1 minute ago -
-
- Modified by -
-
- - someone + + IS - -
-
- Description -
-
- - - Trusted App 9 + + + Linux -
-
+ +
+
+ +
+
-
+
+

+ trusted app 6 +

+
+
-
+
+ +
+
+ class="euiText euiText--small" + > + Last updated +
+
+
+ class="euiText euiText--small" + > + + 1 minute ago + +
- - - - - - - - - - - - - -
-
- - - Field - - - - - - Operator - - - - - - Value - - -
-
- - No items found - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
+
- - Remove - - - +
+
+ Created +
+
+
+
+ + 1 minute ago + +
+
+
+
+
+
+
+
+ +
+
+
-
- - - -
-
-
-
-
- +
+
+ + + + Created by + + + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
+
+
+
+
+ + + + Updated by + + + +
+
+
+
+
+ +
+
+
+
+ someone +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ Applied globally +
+
+
+
+
+
+
+

+ Trusted App 6 +

-
-
-
+
+ +
+
+
+
- - -
  • +
  • +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - +
    +
    + + +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 7 +

    +
    +
    +
    +
    +
    +
    + + + - 5 + OS - - -
  • - … -
  • -
  • - -
  • - - - +
    +
    +
    +
    - - - - - -`; - -exports[`TrustedAppsGrid renders correctly when loading data for the first time 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
    -
    +
    +
    +
    +
    +
    +
    +

    + trusted app 8 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 8 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Linux + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + trusted app 9 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 9 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Windows + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +`; + +exports[`TrustedAppsGrid renders correctly when loading data for the first time 1`] = ` +.c1 { + position: relative; + padding-top: 4px; +} + +.c1 .body { + min-height: 40px; +} + +.c1 .body-content { + position: relative; +} + +.c0 .trusted-app + .trusted-app { + margin-top: 24px; +} + +
    +
    @@ -3639,35 +4494,6 @@ exports[`TrustedAppsGrid renders correctly when loading data for the first time `; exports[`TrustedAppsGrid renders correctly when loading data for the second time 1`] = ` -.c2 { - background-color: #f5f7fa; - padding: 16px; -} - -.c5 { - padding: 12px 24px 24px 0; -} - -.c5.c5.c5 { - margin-left: 0; -} - -.c5 .trustedAppsConditionsTable { - margin-left: 16px; -} - -.c3.c3.c3 { - width: 40%; - margin-top: 0; - margin-bottom: 8px; -} - -.c4.c4.c4 { - width: 60%; - margin-top: 0; - margin-bottom: 8px; -} - .c1 { position: relative; padding-top: 4px; @@ -3681,6 +4507,10 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time position: relative; } +.c3.artifactEntryCard + .c2.artifactEntryCard { + margin-top: 24px; +} + .c0 .trusted-app + .trusted-app { margin-top: 24px; } @@ -3702,6179 +4532,7914 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="body-content undefined" >
    +
    +
    +
    +
    +
    +

    + trusted app 0 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 0 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Windows + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + trusted app 1 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 1 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + macos + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + trusted app 2 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 2 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Linux + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + trusted app 3 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 3 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Windows + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + trusted app 4 +

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Created by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + Updated by + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 4 +

    +
    +
    +
    +
    -
    -
    - Name -
    -
    - - - trusted app 0 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 0 + + + macos -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 5 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - + + +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    -
    - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 5 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 1 - - -
    -
    - OS -
    -
    - - - Mac - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    + - - someone + + + + OS -
    -
    - Description -
    -
    - - Trusted App 1 + + IS + + + + Linux -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 6 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    +
    - - Remove - - - +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    -
    -
    - Name -
    -
    - - - trusted app 2 - - -
    -
    - OS -
    -
    - - - Linux - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - - someone - - -
    -
    - Description -
    -
    - - - Trusted App 2 - - -
    -
    + +
    +
    +
    -
    +
    + + + + Created by + + + +
    +
    -
    +
    - +
    +
    + someone
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    - +
    -
    -
    -
    - + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 6 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 3 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 3 + + + Windows -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 7 +

    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Created +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally
    +
    +
    +

    + Trusted App 7 +

    +
    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 4 - - -
    -
    - OS -
    -
    - - - Mac - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 4 + + + macos -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 8 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    +
    - - Remove - - - +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    -
    -
    - Name -
    -
    - - - trusted app 5 - - -
    -
    - OS -
    -
    - - - Linux - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - - someone - - -
    -
    - Description -
    -
    - - - Trusted App 5 - - -
    -
    + +
    +
    +
    -
    +
    + + + + Created by + + + +
    +
    -
    +
    - +
    +
    + someone
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    - +
    -
    -
    -
    - + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 8 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 6 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 6 + + + Linux -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 9 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - + + +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    -
    - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 9 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Windows + + + +
    +
    +
    +
    +
    +
    -
    -
    - Name -
    -
    + + Rows per page + : + 10 + + + +
    +
    +
    +
    +
    + + + + + +
    +
    +
    + + + +`; + +exports[`TrustedAppsGrid renders correctly when new page and page size set (not loading yet) 1`] = ` +.c1 { + position: relative; + padding-top: 4px; +} + +.c1 .body { + min-height: 40px; +} + +.c1 .body-content { + position: relative; +} + +.c3.artifactEntryCard + .c2.artifactEntryCard { + margin-top: 24px; +} + +.c0 .trusted-app + .trusted-app { + margin-top: 24px; +} + +
    +
    +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 0 +

    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Created +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 0 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 8 - - -
    -
    - OS -
    -
    - - - Linux - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 8 + + + Windows -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 1 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - + + +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    -
    - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 1 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 9 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 9 + + + macos -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 2 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - + + +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    -
    - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - +
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    -
    +
    +
    - 3 - - - - -
  • +
  • +
    +
    +
    +
    +
    +

    -

    +
    +
    +
    +
    +
    + + + - 4 + OS - - -
  • - -
  • -
  • - … -
  • -
  • - -
  • - - - +
    +
    +
    +
    - - - - - -`; - -exports[`TrustedAppsGrid renders correctly when new page and page size set (not loading yet) 1`] = ` -.c2 { - background-color: #f5f7fa; - padding: 16px; -} - -.c5 { - padding: 12px 24px 24px 0; -} - -.c5.c5.c5 { - margin-left: 0; -} - -.c5 .trustedAppsConditionsTable { - margin-left: 16px; -} - -.c3.c3.c3 { - width: 40%; - margin-top: 0; - margin-bottom: 8px; -} - -.c4.c4.c4 { - width: 60%; - margin-top: 0; - margin-bottom: 8px; -} - -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
    -
    -
    -
    -
    -
    -
    - Name -
    -
    - - - trusted app 0 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - + trusted app 3 + +
    +
    - - someone - - - -
    - Description -
    -
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - - - Trusted App 0 - - - - + +
    +
    +
    -
    +
    + + + + Created by + + + +
    +
    -
    +
    - +
    +
    + someone
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    - +
    -
    -
    -
    - + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 3 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 1 - - -
    -
    - OS -
    -
    - - - Mac - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 1 + + + Windows -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 4 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    +
    - - Remove - - - +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    -
    -
    - Name -
    -
    - - - trusted app 2 - - -
    -
    - OS -
    -
    - - - Linux - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - - someone - - -
    -
    - Description -
    -
    - - - Trusted App 2 - - -
    -
    + +
    +
    +
    -
    +
    + + + + Created by + + + +
    +
    -
    +
    +
    + +
    +
    +
    -
    -
    -
    -
    + someone
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    - +
    -
    -
    -
    - + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 4 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 3 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 3 + + + macos -
    -
    + +
    +
    + +
    +
    -
    +
    +

    + trusted app 5 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - + + +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    -
    - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 5 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 4 - - -
    -
    - OS -
    -
    - - - Mac - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 4 + + + Linux -
    -
    + +
    +
    + +
    +
    -
    +
    +

    + trusted app 6 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    +
    - - Remove - - - +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    -
    -
    - -
    -
    -
    -
    -
    - Name -
    -
    - - - trusted app 5 - - -
    -
    - OS -
    -
    - - - Linux - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - - someone - - -
    -
    - Description -
    -
    - - - Trusted App 5 - - -
    -
    + +
    +
    +
    -
    +
    + + + + Created by + + + +
    +
    -
    +
    - +
    +
    + someone
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    - +
    -
    -
    -
    - + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 6 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 6 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 6 + + + Windows -
    -
    + +
    +
    + +
    +
    -
    +
    +

    + trusted app 7 +

    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Last updated +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Created +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 7 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 7 - - -
    -
    - OS -
    -
    + - - Mac - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 7 + + + macos -
    -
    + +
    +
    +
    +
    +
    -
    +
    +

    + trusted app 8 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    +
    - - Remove - - - +
    +
    + Created +
    +
    +
    +
    + + 1 minute ago + +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    -
    -
    - Name -
    -
    - - - trusted app 8 - - -
    -
    - OS -
    -
    - - - Linux - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    - - - someone - - -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - - someone - - -
    -
    - Description -
    -
    - - - Trusted App 8 - - -
    -
    + +
    +
    +
    -
    +
    + + + + Created by + + + +
    +
    -
    +
    - +
    +
    + someone
    - - - - - - - - - - - - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    -
    -
    -
    - +
    -
    -
    -
    - + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 8 +

    -
    -
    +
    -
    -
    - Name -
    -
    - - - trusted app 9 - - -
    -
    - OS -
    -
    - - - Windows - - -
    -
    - Date created -
    -
    - 1 minute ago -
    -
    - Created by -
    -
    + - - someone + + + + OS -
    -
    - Date modified -
    -
    - 1 minute ago -
    -
    - Modified by -
    -
    - - someone + + IS - -
    -
    - Description -
    -
    - - - Trusted App 9 + + + Linux -
    -
    + +
    +
    + +
    +
    -
    +
    +

    + trusted app 9 +

    +
    +
    -
    +
    + +
    +
    + class="euiText euiText--small" + > + Last updated +
    +
    +
    + class="euiText euiText--small" + > + + 1 minute ago + +
    - - - - - - - - - - + + +
    +
    +
    + +
    +
    +
    +
    +
    + Created +
    +
    +
    +
    -
    - - -
    -
    - - - Field - - - - - - Operator - - - - - - Value - - -
    -
    - - No items found - -
    -
    + + 1 minute ago + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    -
    - + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + someone +
    +
    +
    +
    +
    +
    + +
    +
    +
    + Applied globally +
    +
    +
    +
    +
    +
    +
    +

    + Trusted App 9 +

    +
    +
    +
    +
    +
    +
    + + + + + + OS + + + + + IS + + + + Windows + + + +
    diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx index 74f3f0524b304..d5eca75f9c2b5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { render } from '@testing-library/react'; +import { render, act } from '@testing-library/react'; import React from 'react'; import { Provider } from 'react-redux'; @@ -18,7 +18,6 @@ import { createUserChangedUrlAction, createGlobalNoMiddlewareStore, } from '../../../test_utils'; - import { TrustedAppsGrid } from '.'; import { EuiThemeProvider } from '../../../../../../../../../../src/plugins/kibana_react/common'; @@ -26,6 +25,8 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ htmlIdGenerator: () => () => 'mockId', })); +jest.mock('../../../../../../common/lib/kibana'); + const now = 111111; const renderList = (store: ReturnType) => { @@ -129,7 +130,15 @@ describe('TrustedAppsGrid', () => { ); store.dispatch = jest.fn(); - (await renderList(store).findAllByTestId('trustedAppDeleteButton'))[0].click(); + const renderResult = renderList(store); + + await act(async () => { + (await renderResult.findAllByTestId('trustedAppCard-header-actions-button'))[0].click(); + }); + + await act(async () => { + (await renderResult.findByTestId('deleteTrustedAppAction')).click(); + }); expect(store.dispatch).toBeCalledWith({ type: 'trustedAppDeletionDialogStarted', diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx index 8d8b52ac62358..ba09d0c7ee0cc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx @@ -5,10 +5,13 @@ * 2.0. */ -import React, { memo, useCallback } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; +import { i18n } from '@kbn/i18n'; +import { useDispatch } from 'react-redux'; +import { Dispatch } from 'redux'; import { Pagination } from '../../../state'; import { @@ -17,28 +20,31 @@ import { getListItems, getListPagination, isListLoading, + getMapOfPoliciesById, } from '../../../store/selectors'; -import { - useTrustedAppsNavigateCallback, - useTrustedAppsSelector, - useTrustedAppsStoreActionCallback, -} from '../../hooks'; +import { useTrustedAppsNavigateCallback, useTrustedAppsSelector } from '../../hooks'; -import { TrustedAppCard, TrustedAppCardProps } from '../trusted_app_card'; -import { getTrustedAppsListPath } from '../../../../../common/routing'; +import { getPolicyDetailPath, getTrustedAppsListPath } from '../../../../../common/routing'; import { PaginatedContent, PaginatedContentProps, } from '../../../../../components/paginated_content'; -import { TrustedApp } from '../../../../../../../common/endpoint/types'; +import { PolicyDetailsRouteState, TrustedApp } from '../../../../../../../common/endpoint/types'; +import { + ArtifactEntryCard, + ArtifactEntryCardProps, +} from '../../../../../components/artifact_entry_card'; +import { AppAction } from '../../../../../../common/store/actions'; +import { APP_ID } from '../../../../../../../common/constants'; +import { useAppUrl } from '../../../../../../common/lib/kibana'; export interface PaginationBarProps { pagination: Pagination; onChange: (pagination: { size: number; index: number }) => void; } -type TrustedAppCardType = typeof TrustedAppCard; +type ArtifactEntryCardType = typeof ArtifactEntryCard; const RootWrapper = styled.div` .trusted-app + .trusted-app { @@ -46,52 +52,140 @@ const RootWrapper = styled.div` } `; +const BACK_TO_TRUSTED_APPS_LABEL = i18n.translate( + 'xpack.securitySolution.trustedapps.grid.policyDetailsLinkBackLabel', + { defaultMessage: 'Back to trusted Applications' } +); + +const EDIT_TRUSTED_APP_ACTION_LABEL = i18n.translate( + 'xpack.securitySolution.trustedapps.grid.cardAction.edit', + { + defaultMessage: 'Edit trusted application', + } +); + +const DELETE_TRUSTED_APP_ACTION_LABEL = i18n.translate( + 'xpack.securitySolution.trustedapps.grid.cardAction.delete', + { + defaultMessage: 'Delete trusted application', + } +); + export const TrustedAppsGrid = memo(() => { const history = useHistory(); + const dispatch = useDispatch>(); + const { getAppUrl } = useAppUrl(); + const pagination = useTrustedAppsSelector(getListPagination); const listItems = useTrustedAppsSelector(getListItems); const isLoading = useTrustedAppsSelector(isListLoading); const error = useTrustedAppsSelector(getListErrorMessage); const location = useTrustedAppsSelector(getCurrentLocation); - - const handleTrustedAppDelete = useTrustedAppsStoreActionCallback((trustedApp) => ({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: trustedApp }, - })); - - const handleTrustedAppEdit: TrustedAppCardProps['onEdit'] = useCallback( - (trustedApp) => { - history.push( - getTrustedAppsListPath({ - ...location, - show: 'edit', - id: trustedApp.id, - }) - ); - }, - [history, location] - ); + const policyListById = useTrustedAppsSelector(getMapOfPoliciesById); const handlePaginationChange: PaginatedContentProps< TrustedApp, - TrustedAppCardType + ArtifactEntryCardType >['onChange'] = useTrustedAppsNavigateCallback(({ pageIndex, pageSize }) => ({ page_index: pageIndex, page_size: pageSize, })); + const artifactCardPropsPerItem = useMemo(() => { + const cachedCardProps: Record = {}; + + // Casting `listItems` below to remove the `Immutable<>` from it in order to prevent errors + // with common component's props + for (const trustedApp of listItems as TrustedApp[]) { + let policies: ArtifactEntryCardProps['policies']; + + if (trustedApp.effectScope.type === 'policy' && trustedApp.effectScope.policies.length) { + policies = trustedApp.effectScope.policies.reduce< + Required['policies'] + >((policyToNavOptionsMap, policyId) => { + const currentPagePath = getTrustedAppsListPath({ + ...location, + }); + + const policyDetailsPath = getPolicyDetailPath(policyId); + + const routeState: PolicyDetailsRouteState = { + backLink: { + label: BACK_TO_TRUSTED_APPS_LABEL, + navigateTo: [ + APP_ID, + { + path: currentPagePath, + }, + ], + href: getAppUrl({ path: currentPagePath }), + }, + }; + + policyToNavOptionsMap[policyId] = { + navigateAppId: APP_ID, + navigateOptions: { + path: policyDetailsPath, + state: routeState, + }, + href: getAppUrl({ path: policyDetailsPath }), + children: policyListById[policyId]?.name ?? policyId, + }; + return policyToNavOptionsMap; + }, {}); + } + + cachedCardProps[trustedApp.id] = { + item: trustedApp, + policies, + 'data-test-subj': 'trustedAppCard', + actions: [ + { + icon: 'controlsHorizontal', + onClick: () => { + history.push( + getTrustedAppsListPath({ + ...location, + show: 'edit', + id: trustedApp.id, + }) + ); + }, + 'data-test-subj': 'editTrustedAppAction', + children: EDIT_TRUSTED_APP_ACTION_LABEL, + }, + { + icon: 'trash', + onClick: () => { + dispatch({ + type: 'trustedAppDeletionDialogStarted', + payload: { entry: trustedApp }, + }); + }, + 'data-test-subj': 'deleteTrustedAppAction', + children: DELETE_TRUSTED_APP_ACTION_LABEL, + }, + ], + }; + } + + return cachedCardProps; + }, [dispatch, getAppUrl, history, listItems, location, policyListById]); + + const handleArtifactCardProps = useCallback( + (trustedApp: TrustedApp) => { + return artifactCardPropsPerItem[trustedApp.id]; + }, + [artifactCardPropsPerItem] + ); + return ( - + items={listItems as TrustedApp[]} onChange={handlePaginationChange} - ItemComponent={TrustedAppCard} - itemComponentProps={(ta) => ({ - trustedApp: ta, - onDelete: handleTrustedAppDelete, - onEdit: handleTrustedAppEdit, - className: 'trusted-app', - })} + ItemComponent={ArtifactEntryCard} + itemComponentProps={handleArtifactCardProps} loading={isLoading} itemId="id" error={error} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index 9e2cad93fc51f..6ffcf5614a697 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; import { - TrustedApp, MacosLinuxConditionEntry, WindowsConditionEntry, ConditionEntryField, @@ -61,35 +60,6 @@ export const OPERATOR_TITLES: { [K in OperatorFieldIds]: string } = { }), }; -export const PROPERTY_TITLES: Readonly< - { [K in keyof Omit]: string } -> = { - name: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.name', { - defaultMessage: 'Name', - }), - os: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.os', { - defaultMessage: 'OS', - }), - created_at: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.createdAt', { - defaultMessage: 'Date created', - }), - created_by: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.createdBy', { - defaultMessage: 'Created by', - }), - updated_at: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.updatedAt', { - defaultMessage: 'Date modified', - }), - updated_by: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.updatedBy', { - defaultMessage: 'Modified by', - }), - description: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.description', { - defaultMessage: 'Description', - }), - effectScope: i18n.translate('xpack.securitySolution.trustedapps.trustedapp.effectScope', { - defaultMessage: 'Effect scope', - }), -}; - export const ENTRY_PROPERTY_TITLES: Readonly< { [K in keyof Omit]: string } > = { @@ -104,41 +74,6 @@ export const ENTRY_PROPERTY_TITLES: Readonly< }), }; -export const ACTIONS_COLUMN_TITLE = i18n.translate( - 'xpack.securitySolution.trustedapps.list.columns.actions', - { - defaultMessage: 'Actions', - } -); - -export const LIST_ACTIONS = { - delete: { - name: i18n.translate('xpack.securitySolution.trustedapps.list.actions.delete', { - defaultMessage: 'Remove', - }), - description: i18n.translate( - 'xpack.securitySolution.trustedapps.list.actions.delete.description', - { - defaultMessage: 'Remove this entry', - } - ), - }, -}; - -export const CARD_DELETE_BUTTON_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.card.removeButtonLabel', - { - defaultMessage: 'Remove', - } -); - -export const CARD_EDIT_BUTTON_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.card.editButtonLabel', - { - defaultMessage: 'Edit', - } -); - export const GRID_VIEW_TOGGLE_LABEL = i18n.translate( 'xpack.securitySolution.trustedapps.view.toggle.grid', { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index ff7ba8068b4ff..30e170575e2f4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -183,8 +183,13 @@ describe('When on the Trusted Apps Page', () => { beforeEach(async () => { renderResult = await renderWithListData(); + + await act(async () => { + (await renderResult.findAllByTestId('trustedAppCard-header-actions-button'))[0].click(); + }); + act(() => { - fireEvent.click(renderResult.getByTestId('trustedAppEditButton')); + fireEvent.click(renderResult.getByTestId('editTrustedAppAction')); }); }); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts index bfa3fe88f7ac8..3fb05c8bf1048 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts @@ -11,10 +11,14 @@ import { KbnClient } from '@kbn/test'; import bluebird from 'bluebird'; import { basename } from 'path'; import { TRUSTED_APPS_CREATE_API, TRUSTED_APPS_LIST_API } from '../../../common/endpoint/constants'; -import { NewTrustedApp, OperatingSystem, TrustedApp } from '../../../common/endpoint/types'; +import { TrustedApp } from '../../../common/endpoint/types'; +import { TrustedAppGenerator } from '../../../common/endpoint/data_generators/trusted_app_generator'; +import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; +import { setupFleetForEndpoint } from '../../../common/endpoint/data_loaders/setup_fleet_for_endpoint'; const defaultLogger = new ToolingLog({ level: 'info', writeTo: process.stdout }); const separator = '----------------------------------------'; +const trustedAppGenerator = new TrustedAppGenerator(); export const cli = async () => { const cliDefaults = { @@ -67,106 +71,62 @@ export const run: (options?: RunOptions) => Promise = async ({ }); // touch the Trusted Apps List so it can be created - await kbnClient.request({ - method: 'GET', - path: TRUSTED_APPS_LIST_API, - }); + // and + // setup fleet with endpoint integrations + logger.info('setting up Fleet with endpoint and creating trusted apps list'); + const [installedEndpointPackage] = await Promise.all([ + setupFleetForEndpoint(kbnClient).then((response) => response.endpointPackage), + + kbnClient.request({ + method: 'GET', + path: TRUSTED_APPS_LIST_API, + }), + ]); + + // Setup a list of read endpoint policies and return a method to randomly select one + const randomPolicyId: () => string = await (async () => { + const randomN = (max: number): number => Math.floor(Math.random() * max); + const policyIds: string[] = []; + + for (let i = 0, t = 5; i < t; i++) { + policyIds.push( + ( + await indexFleetEndpointPolicy( + kbnClient, + `Policy for Trusted App assignment ${i + 1}`, + installedEndpointPackage.version + ) + ).integrationPolicies[0].id + ); + } + + return () => policyIds[randomN(policyIds.length)]; + })(); return bluebird.map( Array.from({ length: count }), - () => - kbnClient + async () => { + const body = trustedAppGenerator.generateTrustedAppForCreate(); + + if (body.effectScope.type === 'policy') { + body.effectScope.policies = [randomPolicyId(), randomPolicyId()]; + } + + return kbnClient .request({ method: 'POST', path: TRUSTED_APPS_CREATE_API, - body: generateTrustedAppEntry(), + body, }) .then(({ data }) => { logger.write(data.id); return data; - }), + }); + }, { concurrency: 10 } ); }; -interface GenerateTrustedAppEntryOptions { - os?: OperatingSystem; - name?: string; -} -const generateTrustedAppEntry: (options?: GenerateTrustedAppEntryOptions) => object = ({ - os = randomOperatingSystem(), - name = randomName(), -} = {}): NewTrustedApp => { - const newTrustedApp: NewTrustedApp = { - description: `Generator says we trust ${name}`, - name, - os, - effectScope: { - type: 'global', - }, - entries: [ - { - // @ts-ignore - field: 'process.hash.*', - operator: 'included', - type: 'match', - value: '1234234659af249ddf3e40864e9fb241', - }, - { - // @ts-ignore - field: 'process.executable.caseless', - operator: 'included', - type: 'match', - value: '/one/two/three', - }, - ], - }; - - return newTrustedApp; -}; - -const randomN = (max: number): number => Math.floor(Math.random() * max); - -const randomName = (() => { - const names = [ - 'Symantec Endpoint Security', - 'Bitdefender GravityZone', - 'Malwarebytes', - 'Sophos Intercept X', - 'Webroot Business Endpoint Protection', - 'ESET Endpoint Security', - 'FortiClient', - 'Kaspersky Endpoint Security', - 'Trend Micro Apex One', - 'CylancePROTECT', - 'VIPRE', - 'Norton', - 'McAfee Endpoint Security', - 'AVG AntiVirus', - 'CrowdStrike Falcon', - 'Avast Business Antivirus', - 'Avira Antivirus', - 'Cisco AMP for Endpoints', - 'Eset Endpoint Antivirus', - 'VMware Carbon Black', - 'Palo Alto Networks Traps', - 'Trend Micro', - 'SentinelOne', - 'Panda Security for Desktops', - 'Microsoft Defender ATP', - ]; - const count = names.length; - - return () => names[randomN(count)]; -})(); - -const randomOperatingSystem = (() => { - const osKeys = Object.keys(OperatingSystem) as Array; - const count = osKeys.length; - - return () => OperatingSystem[osKeys[randomN(count)]]; -})(); - const createRunLogger = () => { let groupCount = 1; let itemCount = 0; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index aa9ea1a9b5b76..c71980f14d428 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23536,10 +23536,8 @@ "xpack.securitySolution.topN.closeButtonLabel": "閉じる", "xpack.securitySolution.topN.rawEventsSelectLabel": "未加工イベント", "xpack.securitySolution.trustedapps.aboutInfo": "パフォーマンスを改善したり、ホストで実行されている他のアプリケーションとの競合を解消したりするには、信頼できるアプリケーションを追加します。", - "xpack.securitySolution.trustedapps.card.editButtonLabel": "編集", "xpack.securitySolution.trustedapps.card.operator.is": "is", "xpack.securitySolution.trustedapps.card.operator.matches": "一致", - "xpack.securitySolution.trustedapps.card.removeButtonLabel": "削除", "xpack.securitySolution.trustedapps.create.conditionFieldInvalidHashMsg": "[{row}] 無効なハッシュ値", "xpack.securitySolution.trustedapps.create.conditionFieldInvalidPathMsg": "[{row}] パスの形式が正しくありません。値を検証してください", "xpack.securitySolution.trustedapps.create.conditionFieldValueRequiredMsg": "[{row}] フィールドエントリには値が必要です", @@ -23567,10 +23565,7 @@ "xpack.securitySolution.trustedapps.deletionError.title": "削除失敗", "xpack.securitySolution.trustedapps.deletionSuccess.text": "「{name}」は信頼できるアプリケーションリストから削除されました。", "xpack.securitySolution.trustedapps.deletionSuccess.title": "正常に削除されました", - "xpack.securitySolution.trustedapps.list.actions.delete": "削除", - "xpack.securitySolution.trustedapps.list.actions.delete.description": "このエントリを削除", "xpack.securitySolution.trustedapps.list.addButton": "信頼できるアプリケーションを追加", - "xpack.securitySolution.trustedapps.list.columns.actions": "アクション", "xpack.securitySolution.trustedapps.list.pageTitle": "信頼できるアプリケーション", "xpack.securitySolution.trustedapps.list.search.placeholder": "次のフィールドで検索:名前、説明、値", "xpack.securitySolution.trustedapps.listEmptyState.message": "現在、エンドポイントには信頼できるアプリケーションがありません。", @@ -23593,18 +23588,9 @@ "xpack.securitySolution.trustedapps.middleware.editIdMissing": "IDが指定されていません", "xpack.securitySolution.trustedapps.policySelect.globalSectionTitle": "割り当て", "xpack.securitySolution.trustedapps.policySelect.globalSwitchTitle": "信頼できるアプリケーションをグローバルに適用", - "xpack.securitySolution.trustedapps.trustedapp.createdAt": "日付が作成されました", - "xpack.securitySolution.trustedapps.trustedapp.createdBy": "作成者", - "xpack.securitySolution.trustedapps.trustedapp.description": "説明", - "xpack.securitySolution.trustedapps.trustedapp.effectScope": "効果範囲", "xpack.securitySolution.trustedapps.trustedapp.entry.field": "フィールド", "xpack.securitySolution.trustedapps.trustedapp.entry.operator": "演算子", "xpack.securitySolution.trustedapps.trustedapp.entry.value": "値", - "xpack.securitySolution.trustedapps.trustedapp.name": "名前", - "xpack.securitySolution.trustedapps.trustedapp.os": "OS", - "xpack.securitySolution.trustedapps.trustedapp.updatedAt": "変更日", - "xpack.securitySolution.trustedapps.trustedapp.updatedBy": "変更者:", - "xpack.securitySolution.trustedapps.updateSuccess.title": "成功!", "xpack.securitySolution.trustedapps.view.toggle.grid": "グリッドビュー", "xpack.securitySolution.trustedapps.view.toggle.list": "リストビュー", "xpack.securitySolution.trustedapps.viewTypeToggle.controlLegend": "ビュータイプ", @@ -27089,4 +27075,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 24f1d07049840..ae2793d56ca9b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23917,10 +23917,8 @@ "xpack.securitySolution.topN.closeButtonLabel": "关闭", "xpack.securitySolution.topN.rawEventsSelectLabel": "原始事件", "xpack.securitySolution.trustedapps.aboutInfo": "添加受信任的应用程序,以提高性能或缓解与主机上运行的其他应用程序的冲突。", - "xpack.securitySolution.trustedapps.card.editButtonLabel": "编辑", "xpack.securitySolution.trustedapps.card.operator.is": "是", "xpack.securitySolution.trustedapps.card.operator.matches": "匹配", - "xpack.securitySolution.trustedapps.card.removeButtonLabel": "移除", "xpack.securitySolution.trustedapps.create.conditionFieldInvalidHashMsg": "[{row}] 无效的哈希值", "xpack.securitySolution.trustedapps.create.conditionFieldInvalidPathMsg": "[{row}] 路径的格式可能不正确;请验证值", "xpack.securitySolution.trustedapps.create.conditionFieldValueRequiredMsg": "[{row}] 字段条目必须包含值", @@ -23948,10 +23946,7 @@ "xpack.securitySolution.trustedapps.deletionError.title": "移除失败", "xpack.securitySolution.trustedapps.deletionSuccess.text": "“{name}”已从受信任的应用程序列表中移除。", "xpack.securitySolution.trustedapps.deletionSuccess.title": "已成功移除", - "xpack.securitySolution.trustedapps.list.actions.delete": "移除", - "xpack.securitySolution.trustedapps.list.actions.delete.description": "移除此条目", "xpack.securitySolution.trustedapps.list.addButton": "添加受信任的应用程序", - "xpack.securitySolution.trustedapps.list.columns.actions": "操作", "xpack.securitySolution.trustedapps.list.pageTitle": "受信任的应用程序", "xpack.securitySolution.trustedapps.list.search.placeholder": "搜索下面的字段:name、description、value", "xpack.securitySolution.trustedapps.list.totalCount": "正在显示 {totalItemsCount, plural, other {# 个受信任的应用程序}}", @@ -23975,18 +23970,9 @@ "xpack.securitySolution.trustedapps.middleware.editIdMissing": "未提供 ID", "xpack.securitySolution.trustedapps.policySelect.globalSectionTitle": "分配", "xpack.securitySolution.trustedapps.policySelect.globalSwitchTitle": "全局应用受信任的应用程序", - "xpack.securitySolution.trustedapps.trustedapp.createdAt": "创建日期", - "xpack.securitySolution.trustedapps.trustedapp.createdBy": "创建者", - "xpack.securitySolution.trustedapps.trustedapp.description": "描述", - "xpack.securitySolution.trustedapps.trustedapp.effectScope": "作用范围", "xpack.securitySolution.trustedapps.trustedapp.entry.field": "字段", "xpack.securitySolution.trustedapps.trustedapp.entry.operator": "运算符", "xpack.securitySolution.trustedapps.trustedapp.entry.value": "值", - "xpack.securitySolution.trustedapps.trustedapp.name": "名称", - "xpack.securitySolution.trustedapps.trustedapp.os": "OS", - "xpack.securitySolution.trustedapps.trustedapp.updatedAt": "修改日期", - "xpack.securitySolution.trustedapps.trustedapp.updatedBy": "修改者", - "xpack.securitySolution.trustedapps.updateSuccess.title": "成功!", "xpack.securitySolution.trustedapps.view.toggle.grid": "网格视图", "xpack.securitySolution.trustedapps.view.toggle.list": "列表视图", "xpack.securitySolution.trustedapps.viewTypeToggle.controlLegend": "视图类型", @@ -27535,4 +27521,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +} diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts index 95fd914d32b07..684df902bb499 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts @@ -37,14 +37,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { SHA256 ); await testSubjects.click('addTrustedAppFlyout-createButton'); - expect(await testSubjects.getVisibleText('conditionValue')).to.equal(SHA256.toLowerCase()); + expect( + await testSubjects.getVisibleText('trustedAppCard-criteriaConditions-condition') + ).to.equal( + 'AND process.hash.*IS a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476' + ); await pageObjects.common.closeToast(); // Remove it - await testSubjects.click('trustedAppDeleteButton'); + await pageObjects.trustedApps.clickCardActionMenu(); + await testSubjects.click('deleteTrustedAppAction'); await testSubjects.click('trustedAppDeletionConfirm'); await testSubjects.waitForDeleted('trustedAppDeletionConfirm'); - expect(await testSubjects.existOrFail('trustedAppEmptyState')); + // We only expect one trusted app to have been visible + await testSubjects.missingOrFail('trustedAppCard'); }); }); }; diff --git a/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts b/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts index e32fa5e738f89..1678358acc11e 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts @@ -35,5 +35,13 @@ export function TrustedAppsPageProvider({ getService, getPageObjects }: FtrProvi await this.ensureIsOnTrustedAppsListPage(); return testSubjects.find('backToOrigin'); }, + + /** + * Clicks on the actions menu icon in the (only one) truated app card to show the popup with list of actions + */ + async clickCardActionMenu() { + await this.ensureIsOnTrustedAppsListPage(); + await testSubjects.click('trustedAppCard-header-actions-button'); + }, }; } diff --git a/x-pack/test/security_solution_endpoint/services/endpoint.ts b/x-pack/test/security_solution_endpoint/services/endpoint.ts index ae9e714315289..4b5b8d0717889 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint.ts @@ -189,7 +189,9 @@ export class EndpointTestResources extends FtrService { * installs (or upgrades) the Endpoint Fleet package * (NOTE: ensure that fleet is setup first before calling this function) */ - async installOrUpgradeEndpointFleetPackage(): Promise { + async installOrUpgradeEndpointFleetPackage(): ReturnType< + typeof installOrUpgradeEndpointFleetPackage + > { return installOrUpgradeEndpointFleetPackage(this.kbnClient); } }