mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solution][Endpoint] Add RBAC support to the endpoint artifact cards displayed in Fleet pages (#144747)
## Summary - Endpoint artifact cards, displayed in fleet under an Endpoint integration policy edit form, or under the Advanced settings tab when looking at the details of the Endpoint Integration details page: - will now only display the associated artifact card based on RBAC controls set via Kibana feature privileges - If no cards are displayed (due to missing authz), then a Permission empty state will be displayed - When the cards are displayed under the Fleet Edit Endpoint integration form, if the user does not have Endpoint policy management Authz, the link displayed on the Artifact Card will take the user to the Artifact list page filtered by the policy the user was viewing in fleet. If the user has authz to manage endpoint policies, then the link will continue to be the same as is pre-RBAC (takes user to the Endpoint Policy details, focused on the respective Artifact tab.
This commit is contained in:
parent
25c65358a0
commit
14cd9ce0ba
34 changed files with 748 additions and 561 deletions
|
@ -14,7 +14,6 @@ import { appendSearch } from '../../common/components/link_to/helpers';
|
|||
import type { ArtifactListPageUrlParams } from '../components/artifact_list_page';
|
||||
import { paginationFromUrlParams } from '../hooks/use_url_pagination';
|
||||
import type { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types';
|
||||
import type { EventFiltersPageLocation } from '../pages/event_filters/types';
|
||||
import type { PolicyDetailsArtifactsPageLocation } from '../pages/policy/types';
|
||||
import { AdministrationSubTab } from '../types';
|
||||
import {
|
||||
|
@ -168,29 +167,6 @@ const normalizePolicyDetailsArtifactsListPageLocation = (
|
|||
}
|
||||
};
|
||||
|
||||
const normalizeEventFiltersPageLocation = (
|
||||
location?: Partial<EventFiltersPageLocation>
|
||||
): Partial<EventFiltersPageLocation> => {
|
||||
if (location) {
|
||||
return {
|
||||
...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE)
|
||||
? { page_index: location.page_index }
|
||||
: {}),
|
||||
...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE)
|
||||
? { page_size: location.page_size }
|
||||
: {}),
|
||||
...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}),
|
||||
...(!isDefaultOrMissing(location.id, undefined) ? { id: location.id } : {}),
|
||||
...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''),
|
||||
...(!isDefaultOrMissing(location.included_policies, '')
|
||||
? { included_policies: location.included_policies }
|
||||
: ''),
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an object with url params, and a given key, return back only the first param value (case multiples were defined)
|
||||
* @param query
|
||||
|
@ -265,14 +241,12 @@ export const getPolicyDetailsArtifactsListPath = (
|
|||
)}`;
|
||||
};
|
||||
|
||||
export const getEventFiltersListPath = (location?: Partial<EventFiltersPageLocation>): string => {
|
||||
export const getEventFiltersListPath = (location?: Partial<ArtifactListPageUrlParams>): string => {
|
||||
const path = generatePath(MANAGEMENT_ROUTING_EVENT_FILTERS_PATH, {
|
||||
tabName: AdministrationSubTab.eventFilters,
|
||||
});
|
||||
|
||||
return `${path}${appendSearch(
|
||||
querystring.stringify(normalizeEventFiltersPageLocation(location))
|
||||
)}`;
|
||||
return getArtifactListPageUrlPath(path, location);
|
||||
};
|
||||
|
||||
export const getHostIsolationExceptionsListPath = (
|
||||
|
|
|
@ -1,16 +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.
|
||||
*/
|
||||
|
||||
export interface EventFiltersPageLocation {
|
||||
page_index: number;
|
||||
page_size: number;
|
||||
show?: 'create' | 'edit';
|
||||
/** Used for editing. The ID of the selected event filter */
|
||||
id?: string;
|
||||
filter: string;
|
||||
included_policies: string;
|
||||
}
|
|
@ -16,7 +16,6 @@ import { usePolicyDetailsArtifactsNavigateCallback } from '../../policy_hooks';
|
|||
import { useGetLinkTo } from './use_policy_artifacts_empty_hooks';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import type { POLICY_ARTIFACT_EMPTY_UNASSIGNED_LABELS } from './translations';
|
||||
import type { EventFiltersPageLocation } from '../../../../event_filters/types';
|
||||
import type { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page';
|
||||
interface CommonProps {
|
||||
policyId: string;
|
||||
|
@ -24,9 +23,7 @@ interface CommonProps {
|
|||
listId: string;
|
||||
labels: typeof POLICY_ARTIFACT_EMPTY_UNASSIGNED_LABELS;
|
||||
getPolicyArtifactsPath: (policyId: string) => string;
|
||||
getArtifactPath: (
|
||||
location?: Partial<EventFiltersPageLocation> | Partial<ArtifactListPageUrlParams>
|
||||
) => string;
|
||||
getArtifactPath: (location?: Partial<ArtifactListPageUrlParams>) => string;
|
||||
}
|
||||
|
||||
export const PolicyArtifactsEmptyUnassigned = memo<CommonProps>(
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { useGetLinkTo } from './use_policy_artifacts_empty_hooks';
|
||||
import type { POLICY_ARTIFACT_EMPTY_UNEXISTING_LABELS } from './translations';
|
||||
import type { EventFiltersPageLocation } from '../../../../event_filters/types';
|
||||
import type { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page';
|
||||
|
||||
interface CommonProps {
|
||||
|
@ -21,9 +20,7 @@ interface CommonProps {
|
|||
policyName: string;
|
||||
labels: typeof POLICY_ARTIFACT_EMPTY_UNEXISTING_LABELS;
|
||||
getPolicyArtifactsPath: (policyId: string) => string;
|
||||
getArtifactPath: (
|
||||
location?: Partial<EventFiltersPageLocation> | Partial<ArtifactListPageUrlParams>
|
||||
) => string;
|
||||
getArtifactPath: (location?: Partial<ArtifactListPageUrlParams>) => string;
|
||||
}
|
||||
|
||||
export const PolicyArtifactsEmptyUnexisting = memo<CommonProps>(
|
||||
|
|
|
@ -10,16 +10,13 @@ import { i18n } from '@kbn/i18n';
|
|||
import { useNavigateToAppEventHandler } from '../../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
|
||||
import { useAppUrl } from '../../../../../../common/lib/kibana/hooks';
|
||||
import { APP_UI_ID } from '../../../../../../../common/constants';
|
||||
import type { EventFiltersPageLocation } from '../../../../event_filters/types';
|
||||
import type { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page';
|
||||
|
||||
export const useGetLinkTo = (
|
||||
policyId: string,
|
||||
policyName: string,
|
||||
getPolicyArtifactsPath: (policyId: string) => string,
|
||||
getArtifactPath: (
|
||||
location?: Partial<EventFiltersPageLocation> | Partial<ArtifactListPageUrlParams>
|
||||
) => string,
|
||||
getArtifactPath: (location?: Partial<ArtifactListPageUrlParams>) => string,
|
||||
location?: Partial<{ show: 'create' }>
|
||||
) => {
|
||||
const { getAppUrl } = useAppUrl();
|
||||
|
|
|
@ -32,7 +32,6 @@ import { PolicyArtifactsFlyout } from '../flyout';
|
|||
import type { PolicyArtifactsPageLabels } from '../translations';
|
||||
import { policyArtifactsPageLabels } from '../translations';
|
||||
import { PolicyArtifactsDeleteModal } from '../delete_modal';
|
||||
import type { EventFiltersPageLocation } from '../../../../event_filters/types';
|
||||
import type { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page';
|
||||
|
||||
interface PolicyArtifactsLayoutProps {
|
||||
|
@ -41,9 +40,7 @@ interface PolicyArtifactsLayoutProps {
|
|||
labels: PolicyArtifactsPageLabels;
|
||||
getExceptionsListApiClient: () => ExceptionsListApiClient;
|
||||
searchableFields: readonly string[];
|
||||
getArtifactPath: (
|
||||
location?: Partial<EventFiltersPageLocation> | Partial<ArtifactListPageUrlParams>
|
||||
) => string;
|
||||
getArtifactPath: (location?: Partial<ArtifactListPageUrlParams>) => string;
|
||||
getPolicyArtifactsPath: (policyId: string) => string;
|
||||
/** A boolean to check extra privileges for restricted actions, true when it's allowed, false when not */
|
||||
externalPrivileges?: boolean;
|
||||
|
|
|
@ -27,16 +27,13 @@ import { useGetLinkTo } from '../empty/use_policy_artifacts_empty_hooks';
|
|||
import type { ExceptionsListApiClient } from '../../../../../services/exceptions_list/exceptions_list_api_client';
|
||||
import { useListArtifact } from '../../../../../hooks/artifacts';
|
||||
import type { POLICY_ARTIFACT_LIST_LABELS } from './translations';
|
||||
import type { EventFiltersPageLocation } from '../../../../event_filters/types';
|
||||
import type { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page';
|
||||
|
||||
interface PolicyArtifactsListProps {
|
||||
policy: ImmutableObject<PolicyData>;
|
||||
apiClient: ExceptionsListApiClient;
|
||||
searchableFields: string[];
|
||||
getArtifactPath: (
|
||||
location?: Partial<EventFiltersPageLocation> | Partial<ArtifactListPageUrlParams>
|
||||
) => string;
|
||||
getArtifactPath: (location?: Partial<ArtifactListPageUrlParams>) => string;
|
||||
getPolicyArtifactsPath: (policyId: string) => string;
|
||||
labels: typeof POLICY_ARTIFACT_LIST_LABELS;
|
||||
onDeleteActionCallback: (item: ExceptionListItemSchema) => void;
|
||||
|
|
|
@ -10,8 +10,8 @@ import { ThemeProvider } from 'styled-components';
|
|||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { ExceptionItemsSummary } from './exception_items_summary';
|
||||
import * as reactTestingLibrary from '@testing-library/react';
|
||||
import { getMockTheme } from '../../../../../../../common/lib/kibana/kibana_react.mock';
|
||||
import type { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';
|
||||
import { getMockTheme } from '../../../../../../common/lib/kibana/kibana_react.mock';
|
||||
import type { GetExceptionSummaryResponse } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
const mockTheme = getMockTheme({
|
||||
eui: {
|
|
@ -9,9 +9,9 @@ import type { FC } from 'react';
|
|||
import React, { memo, useCallback } from 'react';
|
||||
import type { EuiBadgeProps } from '@elastic/eui';
|
||||
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import styled from 'styled-components';
|
||||
import type { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { GetExceptionSummaryResponse } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
const SUMMARY_KEYS: Readonly<Array<keyof GetExceptionSummaryResponse>> = [
|
||||
'windows',
|
|
@ -9,8 +9,8 @@ import styled from 'styled-components';
|
|||
import type { FC } from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import type { LinkToAppProps } from '../../../../../../../common/components/endpoint/link_to_app';
|
||||
import { LinkToApp } from '../../../../../../../common/components/endpoint/link_to_app';
|
||||
import type { LinkToAppProps } from '../../../../../../common/components/endpoint/link_to_app';
|
||||
import { LinkToApp } from '../../../../../../common/components/endpoint/link_to_app';
|
||||
|
||||
const LinkLabel = styled.span<{
|
||||
size?: 'm' | 'l';
|
|
@ -9,11 +9,11 @@ import type { PropsWithChildren } from 'react';
|
|||
import React, { memo } from 'react';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
import type { Store } from 'redux';
|
||||
import type { SecuritySolutionQueryClient } from '../../../../../../common/containers/query_client/query_client_provider';
|
||||
import { ReactQueryClientProvider } from '../../../../../../common/containers/query_client/query_client_provider';
|
||||
import { SecuritySolutionStartDependenciesContext } from '../../../../../../common/components/user_privileges/endpoint/security_solution_start_dependencies';
|
||||
import { CurrentLicense } from '../../../../../../common/components/current_license';
|
||||
import type { StartPlugins } from '../../../../../../types';
|
||||
import type { SecuritySolutionQueryClient } from '../../../../../../../common/containers/query_client/query_client_provider';
|
||||
import { ReactQueryClientProvider } from '../../../../../../../common/containers/query_client/query_client_provider';
|
||||
import { SecuritySolutionStartDependenciesContext } from '../../../../../../../common/components/user_privileges/endpoint/security_solution_start_dependencies';
|
||||
import { CurrentLicense } from '../../../../../../../common/components/current_license';
|
||||
import type { StartPlugins } from '../../../../../../../types';
|
||||
|
||||
export type RenderContextProvidersProps = PropsWithChildren<{
|
||||
store: Store;
|
|
@ -8,14 +8,14 @@
|
|||
import type { Dispatch, Middleware, PreloadedState, ReducersMapObject } from 'redux';
|
||||
import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { managementReducer } from '../../../../../store/reducer';
|
||||
import { appReducer } from '../../../../../../common/store/app';
|
||||
import { ExperimentalFeaturesService } from '../../../../../../common/experimental_features_service';
|
||||
import { managementMiddlewareFactory } from '../../../../../store/middleware';
|
||||
import type { StartPlugins } from '../../../../../../types';
|
||||
import type { State } from '../../../../../../common/store';
|
||||
import type { AppAction } from '../../../../../../common/store/actions';
|
||||
import type { Immutable } from '../../../../../../../common/endpoint/types';
|
||||
import { managementReducer } from '../../../../../../store/reducer';
|
||||
import { appReducer } from '../../../../../../../common/store/app';
|
||||
import { ExperimentalFeaturesService } from '../../../../../../../common/experimental_features_service';
|
||||
import { managementMiddlewareFactory } from '../../../../../../store/middleware';
|
||||
import type { StartPlugins } from '../../../../../../../types';
|
||||
import type { State } from '../../../../../../../common/store';
|
||||
import type { AppAction } from '../../../../../../../common/store/actions';
|
||||
import type { Immutable } from '../../../../../../../../common/endpoint/types';
|
||||
|
||||
type ComposeType = typeof compose;
|
||||
declare global {
|
|
@ -8,7 +8,7 @@
|
|||
import type { ComponentType } from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import type { StartPlugins } from '../../../../../../types';
|
||||
import type { StartPlugins } from '../../../../../../../types';
|
||||
import { createFleetContextReduxStore } from './store';
|
||||
import { RenderContextProviders } from './render_context_providers';
|
||||
|
|
@ -15,8 +15,8 @@ import { INTEGRATIONS_PLUGIN_ID } from '@kbn/fleet-plugin/common';
|
|||
import type { ListPageRouteState } from '../../../../../../../../common/endpoint/types';
|
||||
import { useToasts } from '../../../../../../../common/lib/kibana';
|
||||
import { useAppUrl } from '../../../../../../../common/lib/kibana/hooks';
|
||||
import { LinkWithIcon } from './link_with_icon';
|
||||
import { ExceptionItemsSummary } from './exception_items_summary';
|
||||
import { LinkWithIcon } from '../../components/link_with_icon';
|
||||
import { ExceptionItemsSummary } from '../../components/exception_items_summary';
|
||||
import { StyledEuiFlexGridGroup, StyledEuiFlexGridItem } from './styled_components';
|
||||
import { useSummaryArtifact } from '../../../../../../hooks/artifacts';
|
||||
import type { ExceptionsListApiClient } from '../../../../../../services/exceptions_list/exceptions_list_api_client';
|
||||
|
|
|
@ -59,8 +59,13 @@ describe('When displaying the EndpointPackageCustomExtension fleet UI extension'
|
|||
|
||||
it('should NOT show artifact cards if no endpoint management authz', async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock(),
|
||||
canAccessEndpointManagement: false,
|
||||
...getEndpointPrivilegesInitialStateMock({
|
||||
canReadBlocklist: false,
|
||||
canReadEventFilters: false,
|
||||
canReadHostIsolationExceptions: false,
|
||||
canReadTrustedApplications: false,
|
||||
canIsolateHost: false,
|
||||
}),
|
||||
});
|
||||
render();
|
||||
|
||||
|
@ -68,7 +73,7 @@ describe('When displaying the EndpointPackageCustomExtension fleet UI extension'
|
|||
artifactCards.forEach((artifactCard) => {
|
||||
expect(renderResult.queryByTestId(artifactCard)).toBeNull();
|
||||
});
|
||||
expect(renderResult.queryByTestId('noIngestPermissions')).toBeTruthy();
|
||||
expect(renderResult.queryByTestId('noPrivilegesPage')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ReactElement } from 'react';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiSpacer, EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import type { PackageCustomExtensionComponentProps } from '@kbn/fleet-plugin/public';
|
||||
import { useHttp } from '../../../../../../common/lib/kibana';
|
||||
import { NoPrivileges } from '../../../../../../common/components/no_privileges';
|
||||
import { useCanAccessSomeArtifacts } from '../hooks/use_can_access_some_artifacts';
|
||||
import { useHttp, useKibana } from '../../../../../../common/lib/kibana';
|
||||
import { TrustedAppsApiClient } from '../../../../trusted_apps/service/api_client';
|
||||
import { EventFiltersApiClient } from '../../../../event_filters/service/api_client';
|
||||
import { HostIsolationExceptionsApiClient } from '../../../../host_isolation_exceptions/host_isolation_exceptions_api_client';
|
||||
|
@ -27,91 +30,155 @@ import {
|
|||
TRUSTED_APPS_LABELS,
|
||||
} from './translations';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint';
|
||||
import { NoPermissions } from '../../../../../components/no_permissons';
|
||||
|
||||
const TrustedAppsArtifactCard = memo<PackageCustomExtensionComponentProps>((props) => {
|
||||
const http = useHttp();
|
||||
const trustedAppsApiClientInstance = useMemo(
|
||||
() => TrustedAppsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
return (
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={trustedAppsApiClientInstance}
|
||||
getArtifactsPath={getTrustedAppsListPath}
|
||||
labels={TRUSTED_APPS_LABELS}
|
||||
data-test-subj="trustedApps"
|
||||
/>
|
||||
);
|
||||
});
|
||||
TrustedAppsArtifactCard.displayName = 'TrustedAppsArtifactCard';
|
||||
|
||||
const EventFiltersArtifactCard = memo<PackageCustomExtensionComponentProps>((props) => {
|
||||
const http = useHttp();
|
||||
const eventFiltersApiClientInstance = useMemo(
|
||||
() => EventFiltersApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
return (
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={eventFiltersApiClientInstance}
|
||||
getArtifactsPath={getEventFiltersListPath}
|
||||
labels={EVENT_FILTERS_LABELS}
|
||||
data-test-subj="eventFilters"
|
||||
/>
|
||||
);
|
||||
});
|
||||
EventFiltersArtifactCard.displayName = 'EventFiltersArtifactCard';
|
||||
|
||||
const HostIsolationExceptionsArtifactCard = memo<PackageCustomExtensionComponentProps>((props) => {
|
||||
const http = useHttp();
|
||||
const hostIsolationExceptionsApiClientInstance = useMemo(
|
||||
() => HostIsolationExceptionsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
return (
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={hostIsolationExceptionsApiClientInstance}
|
||||
getArtifactsPath={getHostIsolationExceptionsListPath}
|
||||
labels={HOST_ISOLATION_EXCEPTIONS_LABELS}
|
||||
data-test-subj="hostIsolationExceptions"
|
||||
/>
|
||||
);
|
||||
});
|
||||
HostIsolationExceptionsArtifactCard.displayName = 'HostIsolationExceptionsArtifactCard';
|
||||
|
||||
const BlockListArtifactCard = memo<PackageCustomExtensionComponentProps>((props) => {
|
||||
const http = useHttp();
|
||||
const blocklistsApiClientInstance = useMemo(() => BlocklistsApiClient.getInstance(http), [http]);
|
||||
|
||||
return (
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={blocklistsApiClientInstance}
|
||||
getArtifactsPath={getBlocklistsListPath}
|
||||
labels={BLOCKLISTS_LABELS}
|
||||
data-test-subj="blocklists"
|
||||
/>
|
||||
);
|
||||
});
|
||||
BlockListArtifactCard.displayName = 'BlockListArtifactCard';
|
||||
|
||||
/**
|
||||
* The UI displayed in Fleet's Endpoint integration page, under the `Advanced` tab
|
||||
*/
|
||||
export const EndpointPackageCustomExtension = memo<PackageCustomExtensionComponentProps>(
|
||||
(props) => {
|
||||
const http = useHttp();
|
||||
const { loading, canAccessEndpointManagement, canReadHostIsolationExceptions } =
|
||||
useEndpointPrivileges();
|
||||
const {
|
||||
loading,
|
||||
canReadBlocklist,
|
||||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
} = useEndpointPrivileges();
|
||||
const { docLinks } = useKibana().services;
|
||||
|
||||
const trustedAppsApiClientInstance = useMemo(
|
||||
() => TrustedAppsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const userCanAccessContent = useCanAccessSomeArtifacts();
|
||||
|
||||
const eventFiltersApiClientInstance = useMemo(
|
||||
() => EventFiltersApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const artifactCards: ReactElement = useMemo(() => {
|
||||
if (loading) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const hostIsolationExceptionsApiClientInstance = useMemo(
|
||||
() => HostIsolationExceptionsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
if (!userCanAccessContent) {
|
||||
return <NoPrivileges documentationUrl={docLinks.links.securitySolution.privileges} />;
|
||||
}
|
||||
|
||||
const blocklistsApiClientInstance = useMemo(
|
||||
() => BlocklistsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
const artifactCards = useMemo(
|
||||
() => (
|
||||
return (
|
||||
<div data-test-subj="fleetEndpointPackageCustomContent">
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={trustedAppsApiClientInstance}
|
||||
getArtifactsPath={getTrustedAppsListPath}
|
||||
labels={TRUSTED_APPS_LABELS}
|
||||
data-test-subj="trustedApps"
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={eventFiltersApiClientInstance}
|
||||
getArtifactsPath={getEventFiltersListPath}
|
||||
labels={EVENT_FILTERS_LABELS}
|
||||
data-test-subj="eventFilters"
|
||||
/>
|
||||
{canReadHostIsolationExceptions && (
|
||||
{canReadTrustedApplications && (
|
||||
<>
|
||||
<TrustedAppsArtifactCard {...props} />
|
||||
<EuiSpacer />
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={hostIsolationExceptionsApiClientInstance}
|
||||
getArtifactsPath={getHostIsolationExceptionsListPath}
|
||||
labels={HOST_ISOLATION_EXCEPTIONS_LABELS}
|
||||
data-test-subj="hostIsolationExceptions"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<EuiSpacer />
|
||||
<FleetArtifactsCard
|
||||
{...props}
|
||||
artifactApiClientInstance={blocklistsApiClientInstance}
|
||||
getArtifactsPath={getBlocklistsListPath}
|
||||
labels={BLOCKLISTS_LABELS}
|
||||
data-test-subj="blocklists"
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
[
|
||||
blocklistsApiClientInstance,
|
||||
canReadHostIsolationExceptions,
|
||||
eventFiltersApiClientInstance,
|
||||
hostIsolationExceptionsApiClientInstance,
|
||||
trustedAppsApiClientInstance,
|
||||
props,
|
||||
]
|
||||
);
|
||||
|
||||
return loading ? (
|
||||
<EuiLoadingSpinner data-test-subj="endpointExtensionLoadingSpinner" />
|
||||
) : canAccessEndpointManagement ? (
|
||||
artifactCards
|
||||
) : (
|
||||
<NoPermissions />
|
||||
);
|
||||
{canReadEventFilters && (
|
||||
<>
|
||||
<EventFiltersArtifactCard {...props} />
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
{canReadHostIsolationExceptions && (
|
||||
<>
|
||||
<HostIsolationExceptionsArtifactCard {...props} />
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
|
||||
{canReadBlocklist && <BlockListArtifactCard {...props} />}
|
||||
</div>
|
||||
);
|
||||
}, [
|
||||
canReadBlocklist,
|
||||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
docLinks.links.securitySolution.privileges,
|
||||
loading,
|
||||
props,
|
||||
userCanAccessContent,
|
||||
]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center" justifyContent={'spaceAround'}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="xl" />
|
||||
<EuiLoadingSpinner size="l" data-test-subj="endpointExtensionLoadingSpinner" />
|
||||
<EuiSpacer size="xl" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return artifactCards;
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
export const TRUSTED_APPS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummarySummary.error',
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch trusted applications stats: "{error}"',
|
||||
values: { error },
|
||||
|
@ -29,7 +29,7 @@ export const TRUSTED_APPS_LABELS = {
|
|||
export const EVENT_FILTERS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummarySummary.error',
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"',
|
||||
values: { error },
|
||||
|
@ -46,7 +46,7 @@ export const EVENT_FILTERS_LABELS = {
|
|||
export const HOST_ISOLATION_EXCEPTIONS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.hostIsolationExceptionsSummarySummary.error',
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.hostIsolationExceptionsSummary.error',
|
||||
{
|
||||
defaultMessage:
|
||||
'There was an error trying to fetch host isolation exceptions stats: "{error}"',
|
||||
|
@ -63,13 +63,10 @@ export const HOST_ISOLATION_EXCEPTIONS_LABELS = {
|
|||
|
||||
export const BLOCKLISTS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetCustomExtension.blocklistsSummarySummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch blocklist stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
i18n.translate('xpack.securitySolution.endpoint.fleetCustomExtension.blocklistsSummary.error', {
|
||||
defaultMessage: 'There was an error trying to fetch blocklist stats: "{error}"',
|
||||
values: { error },
|
||||
}),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.blocklists.fleetIntegration.title"
|
||||
|
|
|
@ -1,331 +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, useEffect, useState, useMemo } from 'react';
|
||||
import { EuiCallOut, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type {
|
||||
PackagePolicyEditExtensionComponentProps,
|
||||
NewPackagePolicy,
|
||||
} from '@kbn/fleet-plugin/public';
|
||||
import { useHttp } from '../../../../../common/lib/kibana/hooks';
|
||||
import {
|
||||
getPolicyDetailPath,
|
||||
getPolicyTrustedAppsPath,
|
||||
getPolicyBlocklistsPath,
|
||||
getPolicyHostIsolationExceptionsPath,
|
||||
getPolicyEventFiltersPath,
|
||||
} from '../../../../common/routing';
|
||||
import { PolicyDetailsForm } from '../policy_details_form';
|
||||
import type { AppAction } from '../../../../../common/store/actions';
|
||||
import { usePolicyDetailsSelector } from '../policy_hooks';
|
||||
import {
|
||||
apiError,
|
||||
policyDetails,
|
||||
policyDetailsForUpdate,
|
||||
} from '../../store/policy_details/selectors';
|
||||
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
import { FleetIntegrationArtifactsCard } from './endpoint_package_custom_extension/components/fleet_integration_artifacts_card';
|
||||
import { BlocklistsApiClient } from '../../../blocklist/services';
|
||||
import { HostIsolationExceptionsApiClient } from '../../../host_isolation_exceptions/host_isolation_exceptions_api_client';
|
||||
import { EventFiltersApiClient } from '../../../event_filters/service/api_client';
|
||||
import { TrustedAppsApiClient } from '../../../trusted_apps/service/api_client';
|
||||
import { SEARCHABLE_FIELDS as BLOCKLIST_SEARCHABLE_FIELDS } from '../../../blocklist/constants';
|
||||
import { SEARCHABLE_FIELDS as HOST_ISOLATION_EXCEPTIONS_SEARCHABLE_FIELDS } from '../../../host_isolation_exceptions/constants';
|
||||
import { SEARCHABLE_FIELDS as EVENT_FILTERS_SEARCHABLE_FIELDS } from '../../../event_filters/constants';
|
||||
import { SEARCHABLE_FIELDS as TRUSTED_APPS_SEARCHABLE_FIELDS } from '../../../trusted_apps/constants';
|
||||
import { useEndpointPrivileges } from '../../../../../common/components/user_privileges/endpoint';
|
||||
|
||||
export const BLOCKLISTS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate('xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsSummary.error', {
|
||||
defaultMessage: 'There was an error trying to fetch blocklists stats: "{error}"',
|
||||
values: { error },
|
||||
}),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.blocklist.fleetIntegration.title"
|
||||
defaultMessage="Blocklist"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsManageLabel"
|
||||
defaultMessage="Manage blocklist"
|
||||
/>
|
||||
),
|
||||
};
|
||||
export const HOST_ISOLATION_EXCEPTIONS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsSummary.error',
|
||||
{
|
||||
defaultMessage:
|
||||
'There was an error trying to fetch host isolation exceptions stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.hostIsolationExceptions.fleetIntegration.title"
|
||||
defaultMessage="Host isolation exceptions"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsManageLabel"
|
||||
defaultMessage="Manage host isolation exceptions"
|
||||
/>
|
||||
),
|
||||
};
|
||||
export const EVENT_FILTERS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersSummarySummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.eventFilters.fleetIntegration.title"
|
||||
defaultMessage="Event filters"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersManageLabel"
|
||||
defaultMessage="Manage event filters"
|
||||
/>
|
||||
),
|
||||
};
|
||||
export const TRUSTED_APPS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsSummarySummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch trusted apps stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.trustedApps.fleetIntegration.title"
|
||||
defaultMessage="Trusted applications"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsManageLabel"
|
||||
defaultMessage="Manage trusted applications"
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* Exports Endpoint-specific package policy instructions
|
||||
* for use in the Ingest app create / edit package policy
|
||||
*/
|
||||
export const EndpointPolicyEditExtension = memo<PackagePolicyEditExtensionComponentProps>(
|
||||
({ policy, onChange }) => {
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<WrappedPolicyDetailsForm policyId={policy.id} onChange={onChange} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
EndpointPolicyEditExtension.displayName = 'EndpointPolicyEditExtension';
|
||||
|
||||
const WrappedPolicyDetailsForm = memo<{
|
||||
policyId: string;
|
||||
onChange: PackagePolicyEditExtensionComponentProps['onChange'];
|
||||
}>(({ policyId, onChange }) => {
|
||||
const dispatch = useDispatch<(a: AppAction) => void>();
|
||||
const updatedPolicy = usePolicyDetailsSelector(policyDetailsForUpdate);
|
||||
const endpointPolicyDetails = usePolicyDetailsSelector(policyDetails);
|
||||
const endpointDetailsLoadingError = usePolicyDetailsSelector(apiError);
|
||||
const [, setLastUpdatedPolicy] = useState(updatedPolicy);
|
||||
const privileges = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const http = useHttp();
|
||||
const blocklistsApiClientInstance = useMemo(() => BlocklistsApiClient.getInstance(http), [http]);
|
||||
const { canAccessEndpointManagement } = useEndpointPrivileges();
|
||||
|
||||
const hostIsolationExceptionsApiClientInstance = useMemo(
|
||||
() => HostIsolationExceptionsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
const eventFiltersApiClientInstance = useMemo(
|
||||
() => EventFiltersApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
const trustedAppsApiClientInstance = useMemo(
|
||||
() => TrustedAppsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
|
||||
// When the form is initially displayed, trigger the Redux middleware which is based on
|
||||
// the location information stored via the `userChangedUrl` action.
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
hash: '',
|
||||
pathname: getPolicyDetailPath(policyId, ''),
|
||||
search: '',
|
||||
},
|
||||
});
|
||||
|
||||
// When form is unloaded, reset the redux store
|
||||
return () => {
|
||||
dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
hash: '',
|
||||
pathname: '/',
|
||||
search: '',
|
||||
},
|
||||
});
|
||||
};
|
||||
}, [dispatch, policyId]);
|
||||
|
||||
useEffect(() => {
|
||||
// Currently, the `onChange` callback provided by the fleet UI extension is regenerated every
|
||||
// time the policy data is updated, which means this will go into a continious loop if we don't
|
||||
// actually check to see if an update should be reported back to fleet
|
||||
setLastUpdatedPolicy((prevState) => {
|
||||
if (prevState === updatedPolicy) {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
if (updatedPolicy) {
|
||||
onChange({
|
||||
isValid: true,
|
||||
// send up only the updated policy data which is stored in the `inputs` section.
|
||||
// All other attributes (like name, id) are updated from the Fleet form, so we want to
|
||||
// ensure we don't override it.
|
||||
updatedPolicy: {
|
||||
// Casting is needed due to the use of `Immutable<>` in our store data
|
||||
inputs: updatedPolicy.inputs as unknown as NewPackagePolicy['inputs'],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return updatedPolicy;
|
||||
});
|
||||
}, [onChange, updatedPolicy]);
|
||||
|
||||
const artifactCards = useMemo(
|
||||
() => (
|
||||
<>
|
||||
<div>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.artifacts.title"
|
||||
defaultMessage="Artifacts"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={trustedAppsApiClientInstance}
|
||||
getArtifactsPath={getPolicyTrustedAppsPath}
|
||||
searchableFields={TRUSTED_APPS_SEARCHABLE_FIELDS}
|
||||
labels={TRUSTED_APPS_LABELS}
|
||||
data-test-subj="trustedApps"
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={eventFiltersApiClientInstance}
|
||||
getArtifactsPath={getPolicyEventFiltersPath}
|
||||
searchableFields={EVENT_FILTERS_SEARCHABLE_FIELDS}
|
||||
labels={EVENT_FILTERS_LABELS}
|
||||
data-test-subj="eventFilters"
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={hostIsolationExceptionsApiClientInstance}
|
||||
getArtifactsPath={getPolicyHostIsolationExceptionsPath}
|
||||
searchableFields={HOST_ISOLATION_EXCEPTIONS_SEARCHABLE_FIELDS}
|
||||
labels={HOST_ISOLATION_EXCEPTIONS_LABELS}
|
||||
privileges={privileges.canIsolateHost}
|
||||
data-test-subj="hostIsolationExceptions"
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={blocklistsApiClientInstance}
|
||||
getArtifactsPath={getPolicyBlocklistsPath}
|
||||
searchableFields={BLOCKLIST_SEARCHABLE_FIELDS}
|
||||
labels={BLOCKLISTS_LABELS}
|
||||
data-test-subj="blocklists"
|
||||
/>
|
||||
</div>
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
),
|
||||
[
|
||||
blocklistsApiClientInstance,
|
||||
eventFiltersApiClientInstance,
|
||||
hostIsolationExceptionsApiClientInstance,
|
||||
policyId,
|
||||
privileges.canIsolateHost,
|
||||
trustedAppsApiClientInstance,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-test-subj="endpointIntegrationPolicyForm">
|
||||
<>
|
||||
{canAccessEndpointManagement && artifactCards}
|
||||
<div>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.settings.title"
|
||||
defaultMessage="Policy settings"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{endpointDetailsLoadingError ? (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.loadError"
|
||||
defaultMessage="Failed to load endpoint policy settings"
|
||||
/>
|
||||
}
|
||||
iconType="alert"
|
||||
color="warning"
|
||||
data-test-subj="endpiontPolicySettingsLoadingError"
|
||||
>
|
||||
{endpointDetailsLoadingError.message}
|
||||
</EuiCallOut>
|
||||
) : !endpointPolicyDetails ? (
|
||||
<EuiLoadingSpinner size="l" className="essentialAnimation" />
|
||||
) : (
|
||||
<PolicyDetailsForm />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
WrappedPolicyDetailsForm.displayName = 'WrappedPolicyDetailsForm';
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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 { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiLoadingContent, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import {
|
||||
BLOCKLISTS_LABELS,
|
||||
EVENT_FILTERS_LABELS,
|
||||
HOST_ISOLATION_EXCEPTIONS_LABELS,
|
||||
TRUSTED_APPS_LABELS,
|
||||
} from '../translations';
|
||||
import { useCanAccessSomeArtifacts } from '../../hooks/use_can_access_some_artifacts';
|
||||
import { BlocklistsApiClient } from '../../../../../blocklist/services';
|
||||
import { HostIsolationExceptionsApiClient } from '../../../../../host_isolation_exceptions/host_isolation_exceptions_api_client';
|
||||
import { EventFiltersApiClient } from '../../../../../event_filters/service/api_client';
|
||||
import { TrustedAppsApiClient } from '../../../../../trusted_apps/service';
|
||||
import {
|
||||
getBlocklistsListPath,
|
||||
getEventFiltersListPath,
|
||||
getHostIsolationExceptionsListPath,
|
||||
getPolicyBlocklistsPath,
|
||||
getPolicyEventFiltersPath,
|
||||
getPolicyHostIsolationExceptionsPath,
|
||||
getPolicyTrustedAppsPath,
|
||||
getTrustedAppsListPath,
|
||||
} from '../../../../../../common/routing';
|
||||
import { SEARCHABLE_FIELDS as TRUSTED_APPS_SEARCHABLE_FIELDS } from '../../../../../trusted_apps/constants';
|
||||
import type { FleetIntegrationArtifactCardProps } from './fleet_integration_artifacts_card';
|
||||
import { FleetIntegrationArtifactsCard } from './fleet_integration_artifacts_card';
|
||||
import { SEARCHABLE_FIELDS as EVENT_FILTERS_SEARCHABLE_FIELDS } from '../../../../../event_filters/constants';
|
||||
import { SEARCHABLE_FIELDS as HOST_ISOLATION_EXCEPTIONS_SEARCHABLE_FIELDS } from '../../../../../host_isolation_exceptions/constants';
|
||||
import { SEARCHABLE_FIELDS as BLOCKLIST_SEARCHABLE_FIELDS } from '../../../../../blocklist/constants';
|
||||
import { useHttp } from '../../../../../../../common/lib/kibana';
|
||||
import { useEndpointPrivileges } from '../../../../../../../common/components/user_privileges/endpoint';
|
||||
|
||||
interface PolicyArtifactCardProps {
|
||||
policyId: string;
|
||||
}
|
||||
|
||||
const TrustedAppsPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
||||
const http = useHttp();
|
||||
const trustedAppsApiClientInstance = useMemo(
|
||||
() => TrustedAppsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
if (canReadPolicyManagement) {
|
||||
return getPolicyTrustedAppsPath(policyId);
|
||||
}
|
||||
|
||||
return getTrustedAppsListPath({ includedPolicies: `${policyId},global` });
|
||||
}, [canReadPolicyManagement, policyId]);
|
||||
|
||||
return (
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={trustedAppsApiClientInstance}
|
||||
getArtifactsPath={getArtifactPathHandler}
|
||||
searchableFields={TRUSTED_APPS_SEARCHABLE_FIELDS}
|
||||
labels={TRUSTED_APPS_LABELS}
|
||||
data-test-subj="trustedApps"
|
||||
/>
|
||||
);
|
||||
});
|
||||
TrustedAppsPolicyCard.displayName = 'TrustedAppsPolicyCard';
|
||||
|
||||
const EventFiltersPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
||||
const http = useHttp();
|
||||
const eventFiltersApiClientInstance = useMemo(
|
||||
() => EventFiltersApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
if (canReadPolicyManagement) {
|
||||
return getPolicyEventFiltersPath(policyId);
|
||||
}
|
||||
|
||||
return getEventFiltersListPath({ includedPolicies: `${policyId},global` });
|
||||
}, [canReadPolicyManagement, policyId]);
|
||||
|
||||
return (
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={eventFiltersApiClientInstance}
|
||||
getArtifactsPath={getArtifactPathHandler}
|
||||
searchableFields={EVENT_FILTERS_SEARCHABLE_FIELDS}
|
||||
labels={EVENT_FILTERS_LABELS}
|
||||
data-test-subj="eventFilters"
|
||||
/>
|
||||
);
|
||||
});
|
||||
EventFiltersPolicyCard.displayName = 'EventFiltersPolicyCard';
|
||||
|
||||
const HostIsolationExceptionsPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
||||
const http = useHttp();
|
||||
const hostIsolationExceptionsApiClientInstance = useMemo(
|
||||
() => HostIsolationExceptionsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
if (canReadPolicyManagement) {
|
||||
return getPolicyHostIsolationExceptionsPath(policyId);
|
||||
}
|
||||
|
||||
return getHostIsolationExceptionsListPath({ includedPolicies: `${policyId},global` });
|
||||
}, [canReadPolicyManagement, policyId]);
|
||||
|
||||
return (
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={hostIsolationExceptionsApiClientInstance}
|
||||
getArtifactsPath={getArtifactPathHandler}
|
||||
searchableFields={HOST_ISOLATION_EXCEPTIONS_SEARCHABLE_FIELDS}
|
||||
labels={HOST_ISOLATION_EXCEPTIONS_LABELS}
|
||||
data-test-subj="hostIsolationExceptions"
|
||||
/>
|
||||
);
|
||||
});
|
||||
HostIsolationExceptionsPolicyCard.displayName = 'HostIsolationExceptionsPolicyCard';
|
||||
|
||||
const BlocklistPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
||||
const http = useHttp();
|
||||
const blocklistsApiClientInstance = useMemo(() => BlocklistsApiClient.getInstance(http), [http]);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
if (canReadPolicyManagement) {
|
||||
return getPolicyBlocklistsPath(policyId);
|
||||
}
|
||||
|
||||
return getBlocklistsListPath({ includedPolicies: `${policyId},global` });
|
||||
}, [canReadPolicyManagement, policyId]);
|
||||
return (
|
||||
<FleetIntegrationArtifactsCard
|
||||
policyId={policyId}
|
||||
artifactApiClientInstance={blocklistsApiClientInstance}
|
||||
getArtifactsPath={getArtifactPathHandler}
|
||||
searchableFields={BLOCKLIST_SEARCHABLE_FIELDS}
|
||||
labels={BLOCKLISTS_LABELS}
|
||||
data-test-subj="blocklists"
|
||||
/>
|
||||
);
|
||||
});
|
||||
BlocklistPolicyCard.displayName = 'BlocklistPolicyCard';
|
||||
|
||||
export interface EndpointPolicyArtifactCardsProps {
|
||||
policyId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the Artifact cards on the Edit Integration Policy form within Fleet according to the
|
||||
* current user's authz
|
||||
*/
|
||||
export const EndpointPolicyArtifactCards = memo<EndpointPolicyArtifactCardsProps>(
|
||||
({ policyId }) => {
|
||||
const {
|
||||
loading,
|
||||
canReadBlocklist,
|
||||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
} = useEndpointPrivileges();
|
||||
const canAccessArtifactContent = useCanAccessSomeArtifacts();
|
||||
|
||||
if (loading) {
|
||||
return <EuiLoadingContent lines={4} />;
|
||||
}
|
||||
|
||||
if (!canAccessArtifactContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.artifacts.title"
|
||||
defaultMessage="Artifacts"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
{canReadTrustedApplications && (
|
||||
<>
|
||||
<TrustedAppsPolicyCard policyId={policyId} />
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{canReadEventFilters && (
|
||||
<>
|
||||
<EventFiltersPolicyCard policyId={policyId} />
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{canReadHostIsolationExceptions && (
|
||||
<>
|
||||
<HostIsolationExceptionsPolicyCard policyId={policyId} />
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{canReadBlocklist && <BlocklistPolicyCard policyId={policyId} />}
|
||||
</div>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
EndpointPolicyArtifactCards.displayName = 'EndpointPolicyArtifactCards';
|
|
@ -19,7 +19,7 @@ import type { PolicyData } from '../../../../../../../../common/endpoint/types';
|
|||
import { getSummaryExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_summary_schema.mock';
|
||||
import { EventFiltersApiClient } from '../../../../../event_filters/service/api_client';
|
||||
import { SEARCHABLE_FIELDS } from '../../../../../event_filters/constants';
|
||||
import { EVENT_FILTERS_LABELS } from '../../endpoint_policy_edit_extension';
|
||||
import { EVENT_FILTERS_LABELS } from '../translations';
|
||||
|
||||
const endpointGenerator = new EndpointDocGenerator('seed');
|
||||
|
|
@ -6,16 +6,16 @@
|
|||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { INTEGRATIONS_PLUGIN_ID } from '@kbn/fleet-plugin/common';
|
||||
import { pagePathGetters } from '@kbn/fleet-plugin/public';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { PolicyDetailsRouteState } from '../../../../../../../../common/endpoint/types';
|
||||
import { useAppUrl, useToasts } from '../../../../../../../common/lib/kibana';
|
||||
import { ExceptionItemsSummary } from './exception_items_summary';
|
||||
import { LinkWithIcon } from './link_with_icon';
|
||||
import { StyledEuiFlexItem } from './styled_components';
|
||||
import { ExceptionItemsSummary } from '../../components/exception_items_summary';
|
||||
import { LinkWithIcon } from '../../components/link_with_icon';
|
||||
import { StyledEuiFlexItem } from '../../endpoint_package_custom_extension/components/styled_components';
|
||||
import { useSummaryArtifact } from '../../../../../../hooks/artifacts';
|
||||
import type { ExceptionsListApiClient } from '../../../../../../services/exceptions_list/exceptions_list_api_client';
|
||||
import { useTestIdGenerator } from '../../../../../../hooks/use_test_id_generator';
|
||||
|
@ -42,7 +42,7 @@ const ARTIFACTS_LABELS = {
|
|||
|
||||
export type ARTIFACTS_LABELS_TYPE = typeof ARTIFACTS_LABELS;
|
||||
|
||||
export const FleetIntegrationArtifactsCard = memo<{
|
||||
export interface FleetIntegrationArtifactCardProps {
|
||||
policyId: string;
|
||||
artifactApiClientInstance: ExceptionsListApiClient;
|
||||
getArtifactsPath: (policyId: string) => string;
|
||||
|
@ -50,7 +50,9 @@ export const FleetIntegrationArtifactsCard = memo<{
|
|||
labels?: ARTIFACTS_LABELS_TYPE;
|
||||
privileges?: boolean;
|
||||
'data-test-subj': string;
|
||||
}>(
|
||||
}
|
||||
|
||||
export const FleetIntegrationArtifactsCard = memo<FleetIntegrationArtifactCardProps>(
|
||||
({
|
||||
policyId,
|
||||
artifactApiClientInstance,
|
|
@ -9,20 +9,20 @@ import React from 'react';
|
|||
import { waitFor } from '@testing-library/react';
|
||||
import type { PackagePolicy, NewPackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
|
||||
import { useEndpointPrivileges } from '../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { composeHttpHandlerMocks } from '../../../../../common/mock/endpoint/http_handler_mock_factory';
|
||||
import type { AppContextTestRender } from '../../../../../common/mock/endpoint';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { composeHttpHandlerMocks } from '../../../../../../common/mock/endpoint/http_handler_mock_factory';
|
||||
import type { AppContextTestRender } from '../../../../../../common/mock/endpoint';
|
||||
import {
|
||||
fleetGetAgentStatusHttpMock,
|
||||
fleetGetEndpointPackagePolicyHttpMock,
|
||||
} from '../../../../mocks';
|
||||
} from '../../../../../mocks';
|
||||
import { EndpointPolicyEditExtension } from './endpoint_policy_edit_extension';
|
||||
import { createFleetContextRendererMock } from './mocks';
|
||||
import { createFleetContextRendererMock } from '../mocks';
|
||||
|
||||
jest.mock('../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
jest.mock('../../../../../common/components/user_privileges');
|
||||
jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
jest.mock('../../../../../../common/components/user_privileges');
|
||||
const useEndpointPrivilegesMock = useEndpointPrivileges as jest.Mock;
|
||||
const useUserPrivilegesMock = useUserPrivileges as jest.Mock;
|
||||
|
||||
|
@ -65,8 +65,12 @@ describe('When displaying the EndpointPolicyEditExtension fleet UI extension', (
|
|||
|
||||
it('should NOT show artifact cards if no endpoint management authz', async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock(),
|
||||
canAccessEndpointManagement: false,
|
||||
...getEndpointPrivilegesInitialStateMock({
|
||||
canReadTrustedApplications: false,
|
||||
canReadEventFilters: false,
|
||||
canReadBlocklist: false,
|
||||
canReadHostIsolationExceptions: false,
|
||||
}),
|
||||
});
|
||||
const renderResult = render();
|
||||
|
||||
|
@ -76,4 +80,28 @@ describe('When displaying the EndpointPolicyEditExtension fleet UI extension', (
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
['trustedApps', 'trusted_apps'],
|
||||
['eventFilters', 'event_filters'],
|
||||
['hostIsolationExceptions', 'host_isolation_exceptions'],
|
||||
['blocklists', 'blocklist'],
|
||||
])(
|
||||
'should link to the %s list page if no Authz for policy management',
|
||||
async (artifactTestIdPrefix, pageUrlName) => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock({
|
||||
canReadPolicyManagement: false,
|
||||
}),
|
||||
});
|
||||
|
||||
const { getByTestId } = render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
getByTestId(`${artifactTestIdPrefix}-link-to-exceptions`).getAttribute('href')
|
||||
).toEqual(`/app/security/administration/${pageUrlName}?includedPolicies=someid%2Cglobal`);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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, useEffect, useState } from 'react';
|
||||
import { EuiCallOut, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type {
|
||||
PackagePolicyEditExtensionComponentProps,
|
||||
NewPackagePolicy,
|
||||
} from '@kbn/fleet-plugin/public';
|
||||
import { EndpointPolicyArtifactCards } from './components/endpoint_policy_artifact_cards';
|
||||
import { getPolicyDetailPath } from '../../../../../common/routing';
|
||||
import { PolicyDetailsForm } from '../../policy_details_form';
|
||||
import type { AppAction } from '../../../../../../common/store/actions';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import {
|
||||
apiError,
|
||||
policyDetails,
|
||||
policyDetailsForUpdate,
|
||||
} from '../../../store/policy_details/selectors';
|
||||
|
||||
/**
|
||||
* Exports Endpoint-specific package policy instructions
|
||||
* for use in the Ingest app create / edit package policy
|
||||
*/
|
||||
export const EndpointPolicyEditExtension = memo<PackagePolicyEditExtensionComponentProps>(
|
||||
({ policy, onChange }) => {
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<WrappedPolicyDetailsForm policyId={policy.id} onChange={onChange} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
EndpointPolicyEditExtension.displayName = 'EndpointPolicyEditExtension';
|
||||
|
||||
const WrappedPolicyDetailsForm = memo<{
|
||||
policyId: string;
|
||||
onChange: PackagePolicyEditExtensionComponentProps['onChange'];
|
||||
}>(({ policyId, onChange }) => {
|
||||
const dispatch = useDispatch<(a: AppAction) => void>();
|
||||
const updatedPolicy = usePolicyDetailsSelector(policyDetailsForUpdate);
|
||||
const endpointPolicyDetails = usePolicyDetailsSelector(policyDetails);
|
||||
const endpointDetailsLoadingError = usePolicyDetailsSelector(apiError);
|
||||
const [, setLastUpdatedPolicy] = useState(updatedPolicy);
|
||||
|
||||
// When the form is initially displayed, trigger the Redux middleware which is based on
|
||||
// the location information stored via the `userChangedUrl` action.
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
hash: '',
|
||||
pathname: getPolicyDetailPath(policyId, ''),
|
||||
search: '',
|
||||
},
|
||||
});
|
||||
|
||||
// When form is unloaded, reset the redux store
|
||||
return () => {
|
||||
dispatch({
|
||||
type: 'userChangedUrl',
|
||||
payload: {
|
||||
hash: '',
|
||||
pathname: '/',
|
||||
search: '',
|
||||
},
|
||||
});
|
||||
};
|
||||
}, [dispatch, policyId]);
|
||||
|
||||
useEffect(() => {
|
||||
// Currently, the `onChange` callback provided by the fleet UI extension is regenerated every
|
||||
// time the policy data is updated, which means this will go into a continuous loop if we don't
|
||||
// actually check to see if an update should be reported back to fleet
|
||||
setLastUpdatedPolicy((prevState) => {
|
||||
if (prevState === updatedPolicy) {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
if (updatedPolicy) {
|
||||
onChange({
|
||||
isValid: true,
|
||||
// send up only the updated policy data which is stored in the `inputs` section.
|
||||
// All other attributes (like name, id) are updated from the Fleet form, so we want to
|
||||
// ensure we don't override it.
|
||||
updatedPolicy: {
|
||||
// Casting is needed due to the use of `Immutable<>` in our store data
|
||||
inputs: updatedPolicy.inputs as unknown as NewPackagePolicy['inputs'],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return updatedPolicy;
|
||||
});
|
||||
}, [onChange, updatedPolicy]);
|
||||
|
||||
return (
|
||||
<div data-test-subj="endpointIntegrationPolicyForm">
|
||||
<>
|
||||
<EndpointPolicyArtifactCards policyId={policyId} />
|
||||
<div>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.settings.title"
|
||||
defaultMessage="Policy settings"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{endpointDetailsLoadingError ? (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.loadError"
|
||||
defaultMessage="Failed to load endpoint policy settings"
|
||||
/>
|
||||
}
|
||||
iconType="alert"
|
||||
color="warning"
|
||||
data-test-subj="endpiontPolicySettingsLoadingError"
|
||||
>
|
||||
{endpointDetailsLoadingError.message}
|
||||
</EuiCallOut>
|
||||
) : !endpointPolicyDetails ? (
|
||||
<EuiLoadingSpinner size="l" className="essentialAnimation" />
|
||||
) : (
|
||||
<PolicyDetailsForm />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
WrappedPolicyDetailsForm.displayName = 'WrappedPolicyDetailsForm';
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React from 'react';
|
||||
|
||||
export const BLOCKLISTS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate('xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsSummary.error', {
|
||||
defaultMessage: 'There was an error trying to fetch blocklist stats: "{error}"',
|
||||
values: { error },
|
||||
}),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.blocklist.fleetIntegration.title"
|
||||
defaultMessage="Blocklist"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsManageLabel"
|
||||
defaultMessage="Manage blocklist"
|
||||
/>
|
||||
),
|
||||
};
|
||||
export const HOST_ISOLATION_EXCEPTIONS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsSummary.error',
|
||||
{
|
||||
defaultMessage:
|
||||
'There was an error trying to fetch host isolation exceptions stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.hostIsolationExceptions.fleetIntegration.title"
|
||||
defaultMessage="Host isolation exceptions"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsManageLabel"
|
||||
defaultMessage="Manage host isolation exceptions"
|
||||
/>
|
||||
),
|
||||
};
|
||||
export const EVENT_FILTERS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersSummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.eventFilters.fleetIntegration.title"
|
||||
defaultMessage="Event filters"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersManageLabel"
|
||||
defaultMessage="Manage event filters"
|
||||
/>
|
||||
),
|
||||
};
|
||||
export const TRUSTED_APPS_LABELS = {
|
||||
artifactsSummaryApiError: (error: string) =>
|
||||
i18n.translate(
|
||||
'xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsSummary.error',
|
||||
{
|
||||
defaultMessage: 'There was an error trying to fetch trusted apps stats: "{error}"',
|
||||
values: { error },
|
||||
}
|
||||
),
|
||||
cardTitle: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.trustedApps.fleetIntegration.title"
|
||||
defaultMessage="Trusted applications"
|
||||
/>
|
||||
),
|
||||
linkLabel: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsManageLabel"
|
||||
defaultMessage="Manage trusted applications"
|
||||
/>
|
||||
),
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { useMemo } from 'react';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint';
|
||||
|
||||
/**
|
||||
* Checks to see if the current user can access at least one artifact page.
|
||||
* Note that this hook will return `false` if the Authz is still being loaded.
|
||||
*/
|
||||
export const useCanAccessSomeArtifacts = (): boolean => {
|
||||
const {
|
||||
canReadBlocklist,
|
||||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
} = useEndpointPrivileges();
|
||||
|
||||
return useMemo(() => {
|
||||
return (
|
||||
canReadBlocklist ||
|
||||
canReadEventFilters ||
|
||||
canReadTrustedApplications ||
|
||||
canReadHostIsolationExceptions
|
||||
);
|
||||
}, [
|
||||
canReadBlocklist,
|
||||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
]);
|
||||
};
|
|
@ -19,7 +19,7 @@ export const getLazyEndpointGenericErrorsListExtension = (
|
|||
) => {
|
||||
return lazy<PackageGenericErrorsListComponent>(async () => {
|
||||
const [{ withSecurityContext }, { EndpointGenericErrorsList }] = await Promise.all([
|
||||
import('./with_security_context/with_security_context'),
|
||||
import('./components/with_security_context/with_security_context'),
|
||||
import('./endpoint_generic_errors_list'),
|
||||
]);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ export const getLazyEndpointPackageCustomExtension = (
|
|||
) => {
|
||||
return lazy<PackageCustomExtensionComponent>(async () => {
|
||||
const [{ withSecurityContext }, { EndpointPackageCustomExtension }] = await Promise.all([
|
||||
import('./with_security_context/with_security_context'),
|
||||
import('./components/with_security_context/with_security_context'),
|
||||
import('./endpoint_package_custom_extension'),
|
||||
]);
|
||||
return {
|
||||
|
|
|
@ -19,8 +19,8 @@ export const getLazyEndpointPolicyEditExtension = (
|
|||
) => {
|
||||
return lazy<PackagePolicyEditExtensionComponent>(async () => {
|
||||
const [{ withSecurityContext }, { EndpointPolicyEditExtension }] = await Promise.all([
|
||||
import('./with_security_context/with_security_context'),
|
||||
import('./endpoint_policy_edit_extension'),
|
||||
import('./components/with_security_context/with_security_context'),
|
||||
import('./endpoint_policy_edit_extension/endpoint_policy_edit_extension'),
|
||||
]);
|
||||
|
||||
return {
|
||||
|
|
|
@ -19,7 +19,7 @@ export const getLazyEndpointPolicyResponseExtension = (
|
|||
) => {
|
||||
return lazy<PackagePolicyResponseExtensionComponent>(async () => {
|
||||
const [{ withSecurityContext }, { EndpointPolicyResponseExtension }] = await Promise.all([
|
||||
import('./with_security_context/with_security_context'),
|
||||
import('./components/with_security_context/with_security_context'),
|
||||
import('./endpoint_policy_response_extension'),
|
||||
]);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import type { Action, Reducer } from 'redux';
|
||||
import type { RenderOptions } from '@testing-library/react';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
|
@ -17,7 +17,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { SecuritySolutionQueryClient } from '../../../../../common/containers/query_client/query_client_provider';
|
||||
import type { AppContextTestRender, UiRender } from '../../../../../common/mock/endpoint';
|
||||
import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint';
|
||||
import { createFleetContextReduxStore } from './with_security_context/store';
|
||||
import { createFleetContextReduxStore } from './components/with_security_context/store';
|
||||
import type { ExperimentalFeatures } from '../../../../../../common/experimental_features';
|
||||
import { allowedExperimentalValues } from '../../../../../../common/experimental_features';
|
||||
import type { State } from '../../../../../common/store';
|
||||
|
@ -25,7 +25,7 @@ import { mockGlobalState } from '../../../../../common/mock';
|
|||
import { managementReducer } from '../../../../store/reducer';
|
||||
import { appReducer } from '../../../../../common/store/app';
|
||||
import { ExperimentalFeaturesService } from '../../../../../common/experimental_features_service';
|
||||
import { RenderContextProviders } from './with_security_context/render_context_providers';
|
||||
import { RenderContextProviders } from './components/with_security_context/render_context_providers';
|
||||
import type { AppAction } from '../../../../../common/store/actions';
|
||||
|
||||
// Defined a private custom reducer that reacts to an action that enables us to update the
|
||||
|
@ -84,17 +84,6 @@ export const createFleetContextRendererMock = (): AppContextTestRender => {
|
|||
const queryClient = new SecuritySolutionQueryClient();
|
||||
|
||||
const Wrapper: RenderOptions['wrapper'] = ({ children }) => {
|
||||
const services = useMemo(() => {
|
||||
const { http, notifications, application } = mockedContext.coreStart;
|
||||
|
||||
return {
|
||||
http,
|
||||
notifications,
|
||||
application,
|
||||
data: mockedContext.depsStart.data,
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// When the component un-mounts, reset the Experimental features since
|
||||
|
@ -108,7 +97,7 @@ export const createFleetContextRendererMock = (): AppContextTestRender => {
|
|||
return (
|
||||
<I18nProvider>
|
||||
<EuiThemeProvider>
|
||||
<KibanaContextProvider services={services}>
|
||||
<KibanaContextProvider services={mockedContext.startServices}>
|
||||
<RenderContextProviders
|
||||
store={store}
|
||||
depsStart={mockedContext.depsStart}
|
||||
|
|
|
@ -25486,15 +25486,9 @@
|
|||
"xpack.securitySolution.endpoint.details.policy.revisionNumber": "rév. {revNumber}",
|
||||
"xpack.securitySolution.endpoint.details.policyStatusValue": "{policyStatus, select, success {Succès} warning {Avertissement} failure {Échec} other {Inconnu}}",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.artifactsSummaryError": "Une erreur s'est produite lors de la tentative de récupération des statistiques d'artefacts : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.blocklistsSummarySummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques de liste noire : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummarySummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques de filtres d'événements : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.hostIsolationExceptionsSummarySummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques d'exceptions d'isolation de l'hôte : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummarySummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques des applications de confiance : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.artifactsSummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques d'artefacts : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsSummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques de listes noires : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersSummarySummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques de filtres d'événements : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsSummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques d'exceptions d'isolation de l'hôte : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsSummarySummary.error": "Une erreur s'est produite lors de la tentative de récupération des statistiques des applications de confiance : \"{error}\"",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolateHost.casesAssociatedWithAlert": "{caseCount} {caseCount, plural, one {cas associé} other {cas associés}} à cet hôte",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolateThisHost": "Isoler l'hôte {hostName} du réseau.",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolation.successfulMessage": "L'isolation sur l'hôte {hostName} a été soumise avec succès",
|
||||
|
|
|
@ -25462,15 +25462,9 @@
|
|||
"xpack.securitySolution.endpoint.details.policy.revisionNumber": "rev. {revNumber}",
|
||||
"xpack.securitySolution.endpoint.details.policyStatusValue": "{policyStatus, select, success {成功} warning {警告} failure {失敗} other {不明}}",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.artifactsSummaryError": "アーティファクト統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.blocklistsSummarySummary.error": "ブロックリスト統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummarySummary.error": "イベントフィルター統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.hostIsolationExceptionsSummarySummary.error": "ホスト分離例外統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummarySummary.error": "信頼できるアプリケーション統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.artifactsSummary.error": "アーティファクト統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsSummary.error": "ブロックリスト統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersSummarySummary.error": "イベントフィルター統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsSummary.error": "ホスト分離例外統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsSummarySummary.error": "信頼できるアプリ統計情報の取得中にエラーが発生しました:\"{error}\"",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolateHost.casesAssociatedWithAlert": "{caseCount} {caseCount, plural, other {個のケース}}がこのホストに関連付けられています",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolateThisHost": "ホスト{hostName}をネットワークから分離します。",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolation.successfulMessage": "ホスト{hostName}での分離は正常に送信されました",
|
||||
|
|
|
@ -25495,15 +25495,9 @@
|
|||
"xpack.securitySolution.endpoint.details.policy.revisionNumber": "修订版 {revNumber}",
|
||||
"xpack.securitySolution.endpoint.details.policyStatusValue": "{policyStatus, select, success {成功} warning {警告} failure {失败} other {未知}}",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.artifactsSummaryError": "尝试提取项目统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.blocklistsSummarySummary.error": "尝试提取阻止列表统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummarySummary.error": "尝试提取事件筛选统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.hostIsolationExceptionsSummarySummary.error": "尝试提取主机隔离例外统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetCustomExtension.trustedAppsSummarySummary.error": "尝试提取受信任应用统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.artifactsSummary.error": "尝试提取项目统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.blocklistsSummary.error": "尝试提取阻止列表统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.eventFiltersSummarySummary.error": "尝试提取事件筛选统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.hostIsolationExceptionsSummary.error": "尝试提取主机隔离例外统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.fleetIntegrationCard.trustedAppsSummarySummary.error": "尝试提取受信任应用统计时出错:“{error}”",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolateHost.casesAssociatedWithAlert": "与此主机关联的 {caseCount} 个{caseCount, plural, other {案例}}",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolateThisHost": "从网络中隔离主机 {hostName}。",
|
||||
"xpack.securitySolution.endpoint.hostIsolation.isolation.successfulMessage": "已成功提交主机 {hostName} 的隔离",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue