[Security Solution][Endpoint] Displays Trusted apps card in policy fleet integration page - UI (#111708)

* Displays Trusted apps card in policy fleet integration page

* Fixes translation

* Use FF to display the card or not

* Revert FF to false by defaul

* Fix back external button props in router stats

* redesign TA card for fleet policy page

* Address pr comments. Added experimental feature singleton service. Added FTR test for TA card in fleet policy

* Change wrong fleet name and SIEM to Security Solutions

* Fix test-subject prop because test is failing

* Use different designs in fleet and integration. Pending to confirm by design team

* UI changes on summary cards

* Adds same card design in fleet policy and integration policy pages

* Fix ts error making prop as optional

* Use eui props for sizing. Use reverseRow to switch label/numbers depending on isSmall prop

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
David Sánchez 2021-09-23 15:06:40 +02:00 committed by GitHub
parent e4f8e64e71
commit d75df38786
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 396 additions and 155 deletions

View file

@ -0,0 +1,30 @@
/*
* 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 { ExperimentalFeatures } from '../../common/experimental_features';
export class ExperimentalFeaturesService {
private static experimentalFeatures?: ExperimentalFeatures;
public static init({ experimentalFeatures }: { experimentalFeatures: ExperimentalFeatures }) {
this.experimentalFeatures = experimentalFeatures;
}
public static get(): ExperimentalFeatures {
if (!this.experimentalFeatures) {
this.throwUninitializedError();
}
return this.experimentalFeatures;
}
private static throwUninitializedError(): never {
throw new Error(
'Experimental features services not initialized - are you trying to import this module from outside of the Security Solution app?'
);
}
}

View file

@ -5,9 +5,10 @@
* 2.0.
*/
import React, { FC, memo, useCallback } from 'react';
import { EuiBadge, EuiBadgeProps, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import React, { FC, memo } from 'react';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';
const SUMMARY_KEYS: Readonly<Array<keyof GetExceptionSummaryResponse>> = [
@ -36,46 +37,76 @@ const SUMMARY_LABELS: Readonly<{ [key in keyof GetExceptionSummaryResponse]: str
),
};
export const StyledEuiFlexGridGroup = styled(EuiFlexGroup)`
display: grid;
min-width: 240px;
grid-template-columns: 50% 50%;
`;
const StyledEuiFlexGroup = styled(EuiFlexGroup)<{
isSmall: boolean;
}>`
font-size: ${({ isSmall, theme }) => (isSmall ? theme.eui.euiFontSizeXS : 'innherit')};
font-weight: ${({ isSmall }) => (isSmall ? '1px' : 'innherit')};
`;
const CSS_BOLD: Readonly<React.CSSProperties> = { fontWeight: 'bold' };
interface ExceptionItemsSummaryProps {
stats: GetExceptionSummaryResponse | undefined;
isSmall?: boolean;
}
export const ExceptionItemsSummary = memo<ExceptionItemsSummaryProps>(({ stats }) => {
return (
<EuiFlexGroup alignItems="center" justifyContent="spaceAround">
{SUMMARY_KEYS.map((stat) => {
return (
<EuiFlexItem key={stat}>
<SummaryStat
value={stats?.[stat] ?? 0}
color={stat === 'total' ? 'primary' : 'default'}
key={stat}
>
{SUMMARY_LABELS[stat]}
</SummaryStat>
</EuiFlexItem>
);
})}
</EuiFlexGroup>
);
});
export const ExceptionItemsSummary = memo<ExceptionItemsSummaryProps>(
({ stats, isSmall = false }) => {
const getItem = useCallback(
(stat: keyof GetExceptionSummaryResponse) => (
<EuiFlexItem key={stat}>
<SummaryStat
value={stats?.[stat] ?? 0}
color={stat === 'total' ? 'primary' : 'default'}
key={stat}
isSmall={isSmall}
>
{SUMMARY_LABELS[stat]}
</SummaryStat>
</EuiFlexItem>
),
[stats, isSmall]
);
return (
<EuiFlexGroup
alignItems="center"
justifyContent={isSmall ? 'flexStart' : 'spaceAround'}
gutterSize={isSmall ? 's' : 'l'}
>
{SUMMARY_KEYS.map((stat) => getItem(stat))}
</EuiFlexGroup>
);
}
);
ExceptionItemsSummary.displayName = 'ExceptionItemsSummary';
const SummaryStat: FC<{ value: number; color?: EuiBadgeProps['color'] }> = memo(
({ children, value, color, ...commonProps }) => {
const SummaryStat: FC<{ value: number; color?: EuiBadgeProps['color']; isSmall?: boolean }> = memo(
({ children, value, color, isSmall = false, ...commonProps }) => {
return (
<EuiText className="eui-displayInlineBlock" size="s">
<EuiFlexGroup justifyContent="center" direction="row" alignItems="center">
<EuiText className="eui-displayInlineBlock" size={isSmall ? 'xs' : 's'}>
<StyledEuiFlexGroup
justifyContent={isSmall ? 'flexStart' : 'center'}
direction={isSmall ? 'rowReverse' : 'row'}
alignItems="center"
gutterSize={isSmall ? 'xs' : 'l'}
isSmall={isSmall}
>
<EuiFlexItem grow={false} style={color === 'primary' ? CSS_BOLD : undefined}>
{children}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBadge color={color}>{value}</EuiBadge>
</EuiFlexItem>
</EuiFlexGroup>
</StyledEuiFlexGroup>
</EuiText>
);
}

View file

@ -46,15 +46,17 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
setStats(summary);
}
} catch (error) {
toasts.addDanger(
i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError',
{
defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"',
values: { error },
}
)
);
if (isMounted.current) {
toasts.addDanger(
i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummaryError',
{
defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"',
values: { error },
}
)
);
}
}
};
fetchStats();
@ -78,12 +80,15 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
path: fleetPackageCustomUrlPath,
},
],
backButtonUrl: getAppUrl({ appId: INTEGRATIONS_PLUGIN_ID, path: fleetPackageCustomUrlPath }),
backButtonUrl: getAppUrl({
appId: INTEGRATIONS_PLUGIN_ID,
path: fleetPackageCustomUrlPath,
}),
};
}, [getAppUrl, pkgkey]);
return (
<EuiPanel paddingSize="l">
<EuiPanel hasShadow={false} paddingSize="l" hasBorder data-test-subj="fleedEventFiltersCard">
<StyledEuiFlexGridGroup alignItems="baseline" justifyContent="center">
<StyledEuiFlexGridItem gridarea="title" alignitems="flex-start">
<EuiText>

View file

@ -8,7 +8,7 @@
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { I18nProvider } from '@kbn/i18n/react';
import { FleetTrustedAppsCard } from './fleet_trusted_apps_card';
import { FleetTrustedAppsCardWrapper } from './fleet_trusted_apps_card_wrapper';
import * as reactTestingLibrary from '@testing-library/react';
import { TrustedAppsHttpService } from '../../../../../trusted_apps/service';
import { useToasts } from '../../../../../../../common/lib/kibana';
@ -67,7 +67,9 @@ describe('Fleet trusted apps card', () => {
</I18nProvider>
);
// @ts-ignore
const component = reactTestingLibrary.render(<FleetTrustedAppsCard />, { wrapper: Wrapper });
const component = reactTestingLibrary.render(<FleetTrustedAppsCardWrapper />, {
wrapper: Wrapper,
});
try {
// @ts-ignore
await reactTestingLibrary.act(() => promise);

View file

@ -9,116 +9,87 @@ import React, { memo, useMemo, useState, useEffect, useRef } from 'react';
import { EuiPanel, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
PackageCustomExtensionComponentProps,
pagePathGetters,
} from '../../../../../../../../../fleet/public';
import { getTrustedAppsListPath } from '../../../../../../common/routing';
import {
ListPageRouteState,
GetExceptionSummaryResponse,
} from '../../../../../../../../common/endpoint/types';
import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../../../fleet/common';
import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';
import { useAppUrl } from '../../../../../../../common/lib/kibana/hooks';
import { useKibana, useToasts } from '../../../../../../../common/lib/kibana';
import { LinkWithIcon } from './link_with_icon';
import { ExceptionItemsSummary } from './exception_items_summary';
import { TrustedAppsHttpService } from '../../../../../trusted_apps/service';
import { StyledEuiFlexGridGroup, StyledEuiFlexGridItem } from './styled_components';
export const FleetTrustedAppsCard = memo<PackageCustomExtensionComponentProps>(({ pkgkey }) => {
const { getAppUrl } = useAppUrl();
const {
services: { http },
} = useKibana();
const toasts = useToasts();
const [stats, setStats] = useState<GetExceptionSummaryResponse | undefined>();
const trustedAppsApi = useMemo(() => new TrustedAppsHttpService(http), [http]);
const isMounted = useRef<boolean>();
interface FleetTrustedAppsCardProps {
customLink: React.ReactNode;
policyId?: string;
cardSize?: 'm' | 'l';
}
useEffect(() => {
isMounted.current = true;
const fetchStats = async () => {
try {
const response = await trustedAppsApi.getTrustedAppsSummary();
if (isMounted) {
setStats(response);
export const FleetTrustedAppsCard = memo<FleetTrustedAppsCardProps>(
({ customLink, policyId, cardSize = 'l' }) => {
const {
services: { http },
} = useKibana();
const toasts = useToasts();
const [stats, setStats] = useState<GetExceptionSummaryResponse | undefined>();
const trustedAppsApi = useMemo(() => new TrustedAppsHttpService(http), [http]);
const isMounted = useRef<boolean>();
useEffect(() => {
isMounted.current = true;
const fetchStats = async () => {
try {
const response = await trustedAppsApi.getTrustedAppsSummary({
kuery: policyId
? `exception-list-agnostic.attributes.tags:"policy:${policyId}" OR exception-list-agnostic.attributes.tags:"policy:all"`
: undefined,
});
if (isMounted) {
setStats(response);
}
} catch (error) {
if (isMounted.current) {
toasts.addDanger(
i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummaryError',
{
defaultMessage:
'There was an error trying to fetch trusted apps stats: "{error}"',
values: { error },
}
)
);
}
}
} catch (error) {
toasts.addDanger(
i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummaryError',
{
defaultMessage: 'There was an error trying to fetch trusted apps stats: "{error}"',
values: { error },
}
)
);
}
};
fetchStats();
return () => {
isMounted.current = false;
};
}, [toasts, trustedAppsApi]);
const trustedAppsListUrlPath = getTrustedAppsListPath();
};
fetchStats();
return () => {
isMounted.current = false;
};
}, [toasts, trustedAppsApi, policyId]);
const trustedAppRouteState = useMemo<ListPageRouteState>(() => {
const fleetPackageCustomUrlPath = `#${
pagePathGetters.integration_details_custom({ pkgkey })[1]
}`;
const getTitleMessage = () => (
<FormattedMessage
id="xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsLabel"
defaultMessage="Trusted Applications"
/>
);
return {
backButtonLabel: i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel',
{ defaultMessage: 'Back to Endpoint Integration' }
),
onBackButtonNavigateTo: [
INTEGRATIONS_PLUGIN_ID,
{
path: fleetPackageCustomUrlPath,
},
],
backButtonUrl: getAppUrl({ appId: INTEGRATIONS_PLUGIN_ID, path: fleetPackageCustomUrlPath }),
};
}, [getAppUrl, pkgkey]);
return (
<EuiPanel paddingSize="l">
<StyledEuiFlexGridGroup alignItems="baseline" justifyContent="center">
<StyledEuiFlexGridItem gridarea="title" alignitems="flex-start">
<EuiText>
<h4>
<FormattedMessage
id="xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsLabel"
defaultMessage="Trusted Applications"
/>
</h4>
</EuiText>
</StyledEuiFlexGridItem>
<StyledEuiFlexGridItem gridarea="summary">
<ExceptionItemsSummary stats={stats} />
</StyledEuiFlexGridItem>
<StyledEuiFlexGridItem gridarea="link" alignitems="flex-end">
<>
<LinkWithIcon
href={getAppUrl({
path: trustedAppsListUrlPath,
})}
appPath={trustedAppsListUrlPath}
appState={trustedAppRouteState}
data-test-subj="linkToTrustedApps"
>
<FormattedMessage
id="xpack.securitySolution.endpoint.fleetCustomExtension.manageTrustedAppLinkLabel"
defaultMessage="Manage trusted applications"
/>
</LinkWithIcon>
</>
</StyledEuiFlexGridItem>
</StyledEuiFlexGridGroup>
</EuiPanel>
);
});
return (
<EuiPanel hasShadow={false} paddingSize="l" hasBorder data-test-subj="fleetTrustedAppsCard">
<StyledEuiFlexGridGroup alignItems="baseline" justifyContent="center" cardSize={cardSize}>
<StyledEuiFlexGridItem gridarea="title" alignitems="flex-start">
<EuiText>
{cardSize === 'l' ? <h4>{getTitleMessage()}</h4> : <h5>{getTitleMessage()}</h5>}
</EuiText>
</StyledEuiFlexGridItem>
<StyledEuiFlexGridItem gridarea="summary">
<ExceptionItemsSummary stats={stats} isSmall={cardSize === 'm'} />
</StyledEuiFlexGridItem>
<StyledEuiFlexGridItem gridarea="link" alignitems="flex-end">
{customLink}
</StyledEuiFlexGridItem>
</StyledEuiFlexGridGroup>
</EuiPanel>
);
}
);
FleetTrustedAppsCard.displayName = 'FleetTrustedAppsCard';

View file

@ -0,0 +1,73 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
PackageCustomExtensionComponentProps,
pagePathGetters,
} from '../../../../../../../../../fleet/public';
import { getTrustedAppsListPath } from '../../../../../../common/routing';
import { ListPageRouteState } from '../../../../../../../../common/endpoint/types';
import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../../../fleet/common';
import { useAppUrl } from '../../../../../../../common/lib/kibana/hooks';
import { LinkWithIcon } from './link_with_icon';
import { FleetTrustedAppsCard } from './fleet_trusted_apps_card';
export const FleetTrustedAppsCardWrapper = memo<PackageCustomExtensionComponentProps>(
({ pkgkey }) => {
const { getAppUrl } = useAppUrl();
const trustedAppsListUrlPath = getTrustedAppsListPath();
const trustedAppRouteState = useMemo<ListPageRouteState>(() => {
const fleetPackageCustomUrlPath = `#${
pagePathGetters.integration_details_custom({ pkgkey })[1]
}`;
return {
backButtonLabel: i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel',
{ defaultMessage: 'Back to Endpoint Integration' }
),
onBackButtonNavigateTo: [
INTEGRATIONS_PLUGIN_ID,
{
path: fleetPackageCustomUrlPath,
},
],
backButtonUrl: getAppUrl({
appId: INTEGRATIONS_PLUGIN_ID,
path: fleetPackageCustomUrlPath,
}),
};
}, [getAppUrl, pkgkey]);
const customLink = useMemo(
() => (
<LinkWithIcon
href={getAppUrl({
path: trustedAppsListUrlPath,
})}
appPath={trustedAppsListUrlPath}
appState={trustedAppRouteState}
data-test-subj="linkToTrustedApps"
>
<FormattedMessage
id="xpack.securitySolution.endpoint.fleetCustomExtension.manageTrustedAppLinkLabel"
defaultMessage="Manage trusted applications"
/>
</LinkWithIcon>
),
[getAppUrl, trustedAppRouteState, trustedAppsListUrlPath]
);
return <FleetTrustedAppsCard customLink={customLink} />;
}
);
FleetTrustedAppsCardWrapper.displayName = 'FleetTrustedAppsCardWrapper';

View file

@ -13,16 +13,23 @@ import {
LinkToAppProps,
} from '../../../../../../../common/components/endpoint/link_to_app';
const LinkLabel = styled.span`
const LinkLabel = styled.span<{
size?: 'm' | 'l';
}>`
display: inline-block;
padding-right: ${(props) => props.theme.eui.paddingSizes.s};
font-size: ${({ size, theme }) => (size === 'm' ? theme.eui.euiFontSizeXS : 'innherit')};
`;
export const LinkWithIcon: FC<LinkToAppProps> = memo(({ children, ...props }) => {
type ComponentProps = LinkToAppProps & {
size?: 'm' | 'l';
};
export const LinkWithIcon: FC<ComponentProps> = memo(({ children, size = 'l', ...props }) => {
return (
<LinkToApp {...props}>
<LinkLabel>{children}</LinkLabel>
<EuiIcon type="popout" />
<LinkLabel size={size}>{children}</LinkLabel>
<EuiIcon type={size === 'm' ? 'arrowRight' : 'popout'} />
</LinkToApp>
);
});

View file

@ -7,9 +7,12 @@
import styled from 'styled-components';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
export const StyledEuiFlexGridGroup = styled(EuiFlexGroup)`
export const StyledEuiFlexGridGroup = styled(EuiFlexGroup)<{
cardSize?: 'm' | 'l';
}>`
display: grid;
grid-template-columns: 25% 45% 30%;
grid-template-columns: ${({ cardSize = 'l' }) =>
cardSize === 'l' ? '25% 45% 30%' : '30% 35% 35%'};
grid-template-areas: 'title summary link';
`;

View file

@ -8,14 +8,14 @@
import { EuiSpacer } from '@elastic/eui';
import React, { memo } from 'react';
import { PackageCustomExtensionComponentProps } from '../../../../../../../../fleet/public';
import { FleetTrustedAppsCard } from './components/fleet_trusted_apps_card';
import { FleetTrustedAppsCardWrapper } from './components/fleet_trusted_apps_card_wrapper';
import { FleetEventFiltersCard } from './components/fleet_event_filters_card';
export const EndpointPackageCustomExtension = memo<PackageCustomExtensionComponentProps>(
(props) => {
return (
<div data-test-subj="fleetEndpointPackageCustomContent">
<FleetTrustedAppsCard {...props} />
<FleetTrustedAppsCardWrapper {...props} />
<EuiSpacer />
<FleetEventFiltersCard {...props} />
</div>

View file

@ -5,19 +5,27 @@
* 2.0.
*/
import React, { memo, useEffect, useState } from 'react';
import { EuiSpacer } from '@elastic/eui';
import React, { memo, useEffect, useState, useMemo } from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { useDispatch } from 'react-redux';
import {
PackagePolicyEditExtensionComponentProps,
NewPackagePolicy,
pagePathGetters,
} from '../../../../../../../fleet/public';
import { getPolicyDetailPath } from '../../../../common/routing';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../fleet/common';
import { useAppUrl } from '../../../../../common/lib/kibana/hooks';
import { PolicyDetailsRouteState } from '../../../../../../common/endpoint/types';
import { getPolicyDetailPath, getPolicyTrustedAppsPath } from '../../../../common/routing';
import { PolicyDetailsForm } from '../policy_details_form';
import { AppAction } from '../../../../../common/store/actions';
import { usePolicyDetailsSelector } from '../policy_hooks';
import { policyDetailsForUpdate } from '../../store/policy_details/selectors';
import { FleetTrustedAppsCard } from './endpoint_package_custom_extension/components/fleet_trusted_apps_card';
import { LinkWithIcon } from './endpoint_package_custom_extension/components/link_with_icon';
/**
* Exports Endpoint-specific package policy instructions
* for use in the Ingest app create / edit package policy
@ -40,7 +48,12 @@ const WrappedPolicyDetailsForm = memo<{
}>(({ policyId, onChange }) => {
const dispatch = useDispatch<(a: AppAction) => void>();
const updatedPolicy = usePolicyDetailsSelector(policyDetailsForUpdate);
const { getAppUrl } = useAppUrl();
const [, setLastUpdatedPolicy] = useState(updatedPolicy);
// TODO: Remove this and related code when removing FF
const isTrustedAppsByPolicyEnabled = useIsExperimentalFeatureEnabled(
'trustedAppsByPolicyEnabled'
);
// When the form is initially displayed, trigger the Redux middleware which is based on
// the location information stored via the `userChangedUrl` action.
@ -93,9 +106,91 @@ const WrappedPolicyDetailsForm = memo<{
});
}, [onChange, updatedPolicy]);
const policyTrustedAppsPath = useMemo(() => getPolicyTrustedAppsPath(policyId), [policyId]);
const policyTrustedAppRouteState = useMemo<PolicyDetailsRouteState>(() => {
const fleetPackageIntegrationCustomUrlPath = `#${
pagePathGetters.integration_policy_edit({ packagePolicyId: policyId })[1]
}`;
return {
backLink: {
label: i18n.translate(
'xpack.securitySolution.endpoint.fleetCustomExtension.artifacts.backButtonLabel',
{
defaultMessage: `Back to Fleet integration policy`,
}
),
navigateTo: [
INTEGRATIONS_PLUGIN_ID,
{
path: fleetPackageIntegrationCustomUrlPath,
},
],
href: getAppUrl({
appId: INTEGRATIONS_PLUGIN_ID,
path: fleetPackageIntegrationCustomUrlPath,
}),
},
};
}, [getAppUrl, policyId]);
const policyTrustedAppsLink = useMemo(
() => (
<LinkWithIcon
href={getAppUrl({
path: policyTrustedAppsPath,
})}
appPath={policyTrustedAppsPath}
appState={policyTrustedAppRouteState}
data-test-subj="linkToTrustedApps"
size="m"
>
<FormattedMessage
id="xpack.securitySolution.endpoint.fleetCustomExtension.manageTrustedAppLinkLabel"
defaultMessage="Manage trusted applications"
/>
</LinkWithIcon>
),
[getAppUrl, policyTrustedAppsPath, policyTrustedAppRouteState]
);
return (
<div data-test-subj="endpointIntegrationPolicyForm">
<PolicyDetailsForm />
{isTrustedAppsByPolicyEnabled ? (
<>
<div>
<EuiText>
<h5>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetails.artifacts.title"
defaultMessage="Artifacts"
/>
</h5>
</EuiText>
<EuiSpacer size="s" />
<FleetTrustedAppsCard
policyId={policyId}
cardSize="m"
customLink={policyTrustedAppsLink}
/>
</div>
<EuiSpacer size="l" />
<div>
<EuiText>
<h5>
<FormattedMessage
id="xpack.securitySolution.endpoint.policyDetails.settings.title"
defaultMessage="Policy settings"
/>
</h5>
</EuiText>
<EuiSpacer size="s" />
<PolicyDetailsForm />
</div>
</>
) : (
<PolicyDetailsForm />
)}
</div>
);
});

View file

@ -13,6 +13,8 @@ import { CurrentLicense } from '../../../../../common/components/current_license
import { StartPlugins } from '../../../../../types';
import { managementReducer } from '../../../../store/reducer';
import { managementMiddlewareFactory } from '../../../../store/middleware';
import { appReducer } from '../../../../../common/store/app';
import { ExperimentalFeaturesService } from '../../../../../common/experimental_features_service';
type ComposeType = typeof compose;
declare global {
@ -51,8 +53,15 @@ export const withSecurityContext = <P extends {}>({
store = createStore(
combineReducers({
management: managementReducer,
app: appReducer,
}),
{ management: undefined },
{
management: undefined,
// @ts-ignore ignore this error as we just need the enableExperimental and it's temporary
app: {
enableExperimental: ExperimentalFeaturesService.get(),
},
},
composeEnhancers(applyMiddleware(...managementMiddlewareFactory(coreStart, depsStart)))
);
}

View file

@ -28,6 +28,7 @@ import {
PutTrustedAppsRequestParams,
GetOneTrustedAppRequestParams,
GetOneTrustedAppResponse,
GetTrustedAppsSummaryRequest,
} from '../../../../../common/endpoint/types/trusted_apps';
import { resolvePathVariables } from '../../../../common/utils/resolve_path_variables';
@ -82,8 +83,10 @@ export class TrustedAppsHttpService implements TrustedAppsService {
);
}
async getTrustedAppsSummary() {
return this.http.get<GetTrustedAppsSummaryResponse>(TRUSTED_APPS_SUMMARY_API);
async getTrustedAppsSummary(request: GetTrustedAppsSummaryRequest) {
return this.http.get<GetTrustedAppsSummaryResponse>(TRUSTED_APPS_SUMMARY_API, {
query: request,
});
}
getPolicyList(options?: Parameters<typeof sendGetEndpointSpecificPackagePolicies>[1]) {

View file

@ -53,6 +53,7 @@ import {
import { SecurityAppStore } from './common/store/store';
import { licenseService } from './common/hooks/use_license';
import { SecuritySolutionUiConfigType } from './common/types';
import { ExperimentalFeaturesService } from './common/experimental_features_service';
import { getLazyEndpointPolicyEditExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension';
import { LazyEndpointPolicyCreateExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_create_extension';
@ -184,6 +185,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
public start(core: CoreStart, plugins: StartPlugins) {
KibanaServices.init({ ...core, ...plugins, kibanaVersion: this.kibanaVersion });
ExperimentalFeaturesService.init({ experimentalFeatures: this.experimentalFeatures });
if (plugins.fleet) {
const { registerExtension } = plugins.fleet;

View file

@ -879,6 +879,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
);
expect(await testSubjects.isSelected('policyWindowsEvent_dns')).to.be(wasSelected);
});
it('should show trusted apps card and link should go back to policy', async () => {
await testSubjects.existOrFail('fleetTrustedAppsCard');
await (await testSubjects.find('linkToTrustedApps')).click();
await testSubjects.existOrFail('policyDetailsPage');
await (await testSubjects.find('policyDetailsBackLink')).click();
await testSubjects.existOrFail('endpointIntegrationPolicyForm');
});
});
});
}

View file

@ -44,6 +44,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
// always install Endpoint package by default when Fleet sets up
`--xpack.fleet.packages.0.name=endpoint`,
`--xpack.fleet.packages.0.version=latest`,
// TODO: Remove feature flags once we're good to go
'--xpack.securitySolution.enableExperimental=["trustedAppsByPolicyEnabled"]',
],
},
layout: {