[SECURITY_SOLUTION] Enable usage of the Endpoint Policy form from Fleet (#84684)
* Endpoint: add `withSecurityContext` HOC + refactor endpoint policy edit lazy component to use it * Endpoint: refactor Policy Details to separate form from view * Endpoint: Enable the Redux store for the Policy form when displayed via Fleet * Fleet: Allow partial package policy updates to be sent via `onChange()`
This commit is contained in:
parent
0e43beed4f
commit
2ffdf75b6e
|
@ -143,6 +143,7 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{
|
|||
description: e.target.value,
|
||||
})
|
||||
}
|
||||
data-test-subj="packagePolicyDescriptionInput"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer size="m" />
|
||||
|
|
|
@ -244,6 +244,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
|
|||
packagePolicyName: packagePolicy.name,
|
||||
},
|
||||
}),
|
||||
'data-test-subj': 'policyUpdateSuccessToast',
|
||||
text:
|
||||
agentCount && agentPolicy
|
||||
? i18n.translate('xpack.fleet.editPackagePolicy.updatedNotificationMessage', {
|
||||
|
@ -406,6 +407,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
|
|||
iconType="save"
|
||||
color="primary"
|
||||
fill
|
||||
data-test-subj="saveIntegration"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.editPackagePolicy.saveButton"
|
||||
|
|
|
@ -28,13 +28,17 @@ export interface PackagePolicyEditExtensionComponentProps {
|
|||
newPolicy: NewPackagePolicy;
|
||||
/**
|
||||
* A callback that should be executed anytime a change to the Integration Policy needs to
|
||||
* be reported back to the Fleet Policy Edit page
|
||||
* be reported back to the Fleet Policy Edit page.
|
||||
*
|
||||
* **NOTE:**
|
||||
* this callback will be recreated everytime the policy data changes, thus logic around its
|
||||
* invocation should take that into consideration in order to avoid an endless loop.
|
||||
*/
|
||||
onChange: (opts: {
|
||||
/** is current form state is valid */
|
||||
isValid: boolean;
|
||||
/** The updated Integration Policy to be merged back and included in the API call */
|
||||
updatedPolicy: NewPackagePolicy;
|
||||
updatedPolicy: Partial<NewPackagePolicy>;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,12 +58,12 @@ export const ConfigForm: FC<ConfigFormProps> = memo(
|
|||
<ConfigFormHeading>{TITLES.type}</ConfigFormHeading>
|
||||
<EuiText size="m">{type}</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<EuiFlexItem>
|
||||
<ConfigFormHeading>{TITLES.os}</ConfigFormHeading>
|
||||
<EuiText>{supportedOss.map((os) => OS_TITLES[os]).join(', ')}</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiShowFor sizes={['m', 'l', 'xl']}>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<EuiFlexGroup direction="row" gutterSize="none" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>{rightCorner}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { memo, useCallback, useMemo, useState } from 'react';
|
||||
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiCallOut,
|
||||
|
@ -19,9 +19,11 @@ import {
|
|||
EuiContextMenuPanelProps,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import {
|
||||
pagePathGetters,
|
||||
PackagePolicyEditExtensionComponentProps,
|
||||
NewPackagePolicy,
|
||||
} from '../../../../../../../fleet/public';
|
||||
import { getPolicyDetailPath, getTrustedAppsListPath } from '../../../../common/routing';
|
||||
import { MANAGEMENT_APP_ID } from '../../../../common/constants';
|
||||
|
@ -31,13 +33,17 @@ import {
|
|||
} from '../../../../../../common/endpoint/types';
|
||||
import { useKibana } from '../../../../../common/lib/kibana';
|
||||
import { useNavigateToAppEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
|
||||
import { PolicyDetailsForm } from '../policy_details_form';
|
||||
import { AppAction } from '../../../../../common/store/actions';
|
||||
import { usePolicyDetailsSelector } from '../policy_hooks';
|
||||
import { 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 }) => {
|
||||
({ policy, onChange }) => {
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
|
@ -46,12 +52,81 @@ export const EndpointPolicyEditExtension = memo<PackagePolicyEditExtensionCompon
|
|||
<EditFlowMessage agentPolicyId={policy.policy_id} integrationPolicyId={policy.id} />
|
||||
</EuiText>
|
||||
</EuiCallOut>
|
||||
<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 [, 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 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]);
|
||||
|
||||
return (
|
||||
<div data-test-subj="endpointIntegrationPolicyForm">
|
||||
<PolicyDetailsForm />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
WrappedPolicyDetailsForm.displayName = 'WrappedPolicyDetailsForm';
|
||||
|
||||
const EditFlowMessage = memo<{
|
||||
agentPolicyId: string;
|
||||
integrationPolicyId: string;
|
||||
|
@ -82,17 +157,6 @@ const EditFlowMessage = memo<{
|
|||
|
||||
const handleClosePopup = useCallback(() => setIsMenuOpen(false), []);
|
||||
|
||||
const handleSecurityPolicyAction = useNavigateToAppEventHandler<PolicyDetailsRouteState>(
|
||||
MANAGEMENT_APP_ID,
|
||||
{
|
||||
path: getPolicyDetailPath(integrationPolicyId),
|
||||
state: {
|
||||
onSaveNavigateTo: navigateBackToIngest,
|
||||
onCancelNavigateTo: navigateBackToIngest,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const handleTrustedAppsAction = useNavigateToAppEventHandler<TrustedAppsListPageRouteState>(
|
||||
MANAGEMENT_APP_ID,
|
||||
{
|
||||
|
@ -129,16 +193,6 @@ const EditFlowMessage = memo<{
|
|||
|
||||
const actionItems = useMemo<EuiContextMenuPanelProps['items']>(() => {
|
||||
return [
|
||||
<EuiContextMenuItem
|
||||
key="policyDetails"
|
||||
onClick={handleSecurityPolicyAction}
|
||||
data-test-subj="securityPolicy"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.fleet.editPackagePolicy.actionSecurityPolicy"
|
||||
defaultMessage="Edit Policy"
|
||||
/>
|
||||
</EuiContextMenuItem>,
|
||||
<EuiContextMenuItem
|
||||
key="trustedApps"
|
||||
onClick={handleTrustedAppsAction}
|
||||
|
@ -150,7 +204,7 @@ const EditFlowMessage = memo<{
|
|||
/>
|
||||
</EuiContextMenuItem>,
|
||||
];
|
||||
}, [handleSecurityPolicyAction, handleTrustedAppsAction]);
|
||||
}, [handleTrustedAppsAction]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
|
|
|
@ -5,14 +5,29 @@
|
|||
*/
|
||||
|
||||
import { lazy } from 'react';
|
||||
import { PackagePolicyEditExtensionComponent } from '../../../../../../../fleet/public';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import {
|
||||
PackagePolicyEditExtensionComponent,
|
||||
PackagePolicyEditExtensionComponentProps,
|
||||
} from '../../../../../../../fleet/public';
|
||||
import { StartPlugins } from '../../../../../types';
|
||||
|
||||
export const getLazyEndpointPolicyEditExtension = (
|
||||
coreStart: CoreStart,
|
||||
depsStart: Pick<StartPlugins, 'data' | 'fleet'>
|
||||
) => {
|
||||
return lazy<PackagePolicyEditExtensionComponent>(async () => {
|
||||
const [{ withSecurityContext }, { EndpointPolicyEditExtension }] = await Promise.all([
|
||||
import('./with_security_context'),
|
||||
import('./endpoint_policy_edit_extension'),
|
||||
]);
|
||||
|
||||
export const LazyEndpointPolicyEditExtension = lazy<PackagePolicyEditExtensionComponent>(
|
||||
async () => {
|
||||
const { EndpointPolicyEditExtension } = await import('./endpoint_policy_edit_extension');
|
||||
return {
|
||||
// FIXME: remove casting once old UI component registration is removed
|
||||
default: (EndpointPolicyEditExtension as unknown) as PackagePolicyEditExtensionComponent,
|
||||
default: withSecurityContext<PackagePolicyEditExtensionComponentProps>({
|
||||
coreStart,
|
||||
depsStart,
|
||||
WrappedComponent: EndpointPolicyEditExtension,
|
||||
}),
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { ComponentType, memo } from 'react';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { combineReducers, createStore, compose, applyMiddleware } from 'redux';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
import { StartPlugins } from '../../../../../types';
|
||||
import { managementReducer } from '../../../../store/reducer';
|
||||
import { managementMiddlewareFactory } from '../../../../store/middleware';
|
||||
|
||||
type ComposeType = typeof compose;
|
||||
declare global {
|
||||
interface Window {
|
||||
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__: ComposeType;
|
||||
}
|
||||
}
|
||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
interface WithSecurityContextProps<P extends {}> {
|
||||
coreStart: CoreStart;
|
||||
depsStart: Pick<StartPlugins, 'data' | 'fleet'>;
|
||||
WrappedComponent: ComponentType<P>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new component that wraps the provided `WrappedComponent` in a bare minimum set of rendering context
|
||||
* needed to render Security Solution components that may be dependent on a Redux store and/or Security Solution
|
||||
* specific context based functionality
|
||||
*
|
||||
* @param coreStart
|
||||
* @param depsStart
|
||||
* @param WrappedComponent
|
||||
*/
|
||||
export const withSecurityContext = <P extends {}>({
|
||||
coreStart,
|
||||
depsStart,
|
||||
WrappedComponent,
|
||||
}: WithSecurityContextProps<P>): ComponentType<P> => {
|
||||
let store: ReturnType<typeof createStore>; // created on first render
|
||||
|
||||
return memo((props) => {
|
||||
if (!store) {
|
||||
// Most of the code here was copied form
|
||||
// x-pack/plugins/security_solution/public/management/index.ts
|
||||
store = createStore(
|
||||
combineReducers({
|
||||
management: managementReducer,
|
||||
}),
|
||||
{ management: undefined },
|
||||
composeEnhancers(applyMiddleware(...managementMiddlewareFactory(coreStart, depsStart)))
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ReduxStoreProvider store={store}>
|
||||
<WrappedComponent {...props} />
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
});
|
||||
};
|
|
@ -10,7 +10,6 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiOverlayMask,
|
||||
EuiConfirmModal,
|
||||
|
@ -34,9 +33,6 @@ import {
|
|||
import { useKibana, toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { AgentsSummary } from './agents_summary';
|
||||
import { VerticalDivider } from './vertical_divider';
|
||||
import { WindowsEvents, MacEvents, LinuxEvents } from './policy_forms/events';
|
||||
import { MalwareProtections } from './policy_forms/protections/malware';
|
||||
import { AntivirusRegistrationForm } from './components/antivirus_registration_form';
|
||||
import { useToasts } from '../../../../common/lib/kibana';
|
||||
import { AppAction } from '../../../../common/store/actions';
|
||||
import { SpyRoute } from '../../../../common/utils/route/spy_routes';
|
||||
|
@ -48,7 +44,7 @@ import { MANAGEMENT_APP_ID } from '../../../common/constants';
|
|||
import { PolicyDetailsRouteState } from '../../../../../common/endpoint/types';
|
||||
import { WrapperPage } from '../../../../common/components/wrapper_page';
|
||||
import { HeaderPage } from '../../../../common/components/header_page';
|
||||
import { AdvancedPolicyForms } from './policy_advanced';
|
||||
import { PolicyDetailsForm } from './policy_details_form';
|
||||
|
||||
export const PolicyDetails = React.memo(() => {
|
||||
const dispatch = useDispatch<(action: AppAction) => void>();
|
||||
|
@ -71,7 +67,6 @@ export const PolicyDetails = React.memo(() => {
|
|||
// Local state
|
||||
const [showConfirm, setShowConfirm] = useState<boolean>(false);
|
||||
const [routeState, setRouteState] = useState<PolicyDetailsRouteState>();
|
||||
const [showAdvancedPolicy, setShowAdvancedPolicy] = useState<boolean>(false);
|
||||
const policyName = policyItem?.name ?? '';
|
||||
const hostListRouterPath = getEndpointListPath({ name: 'endpointList' });
|
||||
|
||||
|
@ -111,9 +106,11 @@ export const PolicyDetails = React.memo(() => {
|
|||
}
|
||||
}, [navigateToApp, toasts, policyName, policyUpdateStatus, routeState]);
|
||||
|
||||
const routingOnCancelNavigateTo = routeState?.onCancelNavigateTo;
|
||||
const navigateToAppArguments = useMemo((): Parameters<ApplicationStart['navigateToApp']> => {
|
||||
return routeState?.onCancelNavigateTo ?? [MANAGEMENT_APP_ID, { path: hostListRouterPath }];
|
||||
}, [hostListRouterPath, routeState?.onCancelNavigateTo]);
|
||||
return routingOnCancelNavigateTo ?? [MANAGEMENT_APP_ID, { path: hostListRouterPath }];
|
||||
}, [hostListRouterPath, routingOnCancelNavigateTo]);
|
||||
|
||||
const handleCancelOnClick = useNavigateToAppEventHandler(...navigateToAppArguments);
|
||||
|
||||
const handleSaveOnClick = useCallback(() => {
|
||||
|
@ -131,10 +128,6 @@ export const PolicyDetails = React.memo(() => {
|
|||
setShowConfirm(false);
|
||||
}, []);
|
||||
|
||||
const handleAdvancedPolicyClick = useCallback(() => {
|
||||
setShowAdvancedPolicy(!showAdvancedPolicy);
|
||||
}, [showAdvancedPolicy]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!routeState && locationRouteState) {
|
||||
setRouteState(locationRouteState);
|
||||
|
@ -224,48 +217,7 @@ export const PolicyDetails = React.memo(() => {
|
|||
{headerRightContent}
|
||||
</HeaderPage>
|
||||
|
||||
<EuiText size="xs" color="subdued">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.details.protections"
|
||||
defaultMessage="Protections"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
<MalwareProtections />
|
||||
<EuiSpacer size="l" />
|
||||
|
||||
<EuiText size="xs" color="subdued">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.details.settings"
|
||||
defaultMessage="Settings"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
<WindowsEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<MacEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<LinuxEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<AntivirusRegistrationForm />
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
<EuiButtonEmpty data-test-subj="advancedPolicyButton" onClick={handleAdvancedPolicyClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.advanced.show"
|
||||
defaultMessage="{action} advanced settings"
|
||||
values={{ action: showAdvancedPolicy ? 'Hide' : 'Show' }}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
{showAdvancedPolicy && <AdvancedPolicyForms />}
|
||||
<PolicyDetailsForm />
|
||||
</WrapperPage>
|
||||
|
||||
<SpyRoute pageName={SecurityPageName.administration} />
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiButtonEmpty, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import React, { memo, useCallback, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { MalwareProtections } from './policy_forms/protections/malware';
|
||||
import { LinuxEvents, MacEvents, WindowsEvents } from './policy_forms/events';
|
||||
import { AdvancedPolicyForms } from './policy_advanced';
|
||||
import { AntivirusRegistrationForm } from './components/antivirus_registration_form';
|
||||
|
||||
export const PolicyDetailsForm = memo(() => {
|
||||
const [showAdvancedPolicy, setShowAdvancedPolicy] = useState<boolean>(false);
|
||||
const handleAdvancedPolicyClick = useCallback(() => {
|
||||
setShowAdvancedPolicy(!showAdvancedPolicy);
|
||||
}, [showAdvancedPolicy]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiText size="xs" color="subdued">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.details.protections"
|
||||
defaultMessage="Protections"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
<MalwareProtections />
|
||||
<EuiSpacer size="l" />
|
||||
|
||||
<EuiText size="xs" color="subdued">
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.details.settings"
|
||||
defaultMessage="Settings"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
<WindowsEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<MacEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<LinuxEvents />
|
||||
<EuiSpacer size="l" />
|
||||
<AntivirusRegistrationForm />
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
<EuiButtonEmpty data-test-subj="advancedPolicyButton" onClick={handleAdvancedPolicyClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.advanced.show"
|
||||
defaultMessage="{action} advanced settings"
|
||||
values={{ action: showAdvancedPolicy ? 'Hide' : 'Show' }}
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiSpacer size="l" />
|
||||
{showAdvancedPolicy && <AdvancedPolicyForms />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
PolicyDetailsForm.displayName = 'PolicyDetailsForm';
|
|
@ -61,7 +61,7 @@ import {
|
|||
import { SecurityAppStore } from './common/store/store';
|
||||
import { getCaseConnectorUI } from './cases/components/connectors';
|
||||
import { licenseService } from './common/hooks/use_license';
|
||||
import { LazyEndpointPolicyEditExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_policy_edit_extension';
|
||||
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';
|
||||
|
||||
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
|
||||
|
@ -337,7 +337,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
registerExtension({
|
||||
package: 'endpoint',
|
||||
view: 'package-policy-edit',
|
||||
component: LazyEndpointPolicyEditExtension,
|
||||
component: getLazyEndpointPolicyEditExtension(core, plugins),
|
||||
});
|
||||
|
||||
registerExtension({
|
||||
|
|
|
@ -281,7 +281,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await actionsButton.click();
|
||||
const menuPanel = await testSubjects.find('endpointActionsMenuPanel');
|
||||
const actionItems = await menuPanel.findAllByTagName<'button'>('button');
|
||||
const expectedItems = ['Edit Policy', 'Edit Trusted Applications'];
|
||||
const expectedItems = ['Edit Trusted Applications'];
|
||||
|
||||
for (const action of actionItems) {
|
||||
const buttonText = await action.getVisibleText();
|
||||
|
@ -289,27 +289,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
}
|
||||
});
|
||||
|
||||
it('should navigate to Policy Details when the edit security policy action is clicked', async () => {
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy');
|
||||
await pageObjects.policy.ensureIsOnDetailsPage();
|
||||
});
|
||||
|
||||
it('should allow the user to navigate, edit, save Policy Details and be redirected back to ingest', async () => {
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy');
|
||||
await pageObjects.policy.ensureIsOnDetailsPage();
|
||||
await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns');
|
||||
await pageObjects.policy.confirmAndSave();
|
||||
|
||||
await testSubjects.existOrFail('policyDetailsSuccessMessage');
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail();
|
||||
});
|
||||
|
||||
it('should navigate back to Ingest Policy Edit package page on click of cancel button', async () => {
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy');
|
||||
await (await pageObjects.policy.findCancelButton()).click();
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail();
|
||||
});
|
||||
|
||||
it('should navigate to Trusted Apps', async () => {
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('trustedApps');
|
||||
await pageObjects.trustedApps.ensureIsOnTrustedAppsListPage();
|
||||
|
@ -321,6 +300,53 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await backButton.click();
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail();
|
||||
});
|
||||
|
||||
it('should show the endpoint policy form', async () => {
|
||||
await testSubjects.existOrFail('endpointIntegrationPolicyForm');
|
||||
});
|
||||
|
||||
it('should allow updates to policy items', async () => {
|
||||
const winDnsEventingCheckbox = await testSubjects.find('policyWindowsEvent_dns');
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow(
|
||||
winDnsEventingCheckbox
|
||||
);
|
||||
expect(await winDnsEventingCheckbox.isSelected()).to.be(true);
|
||||
await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns');
|
||||
expect(await winDnsEventingCheckbox.isSelected()).to.be(false);
|
||||
});
|
||||
|
||||
it('should preserve updates done from the Fleet form', async () => {
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.setPackagePolicyDescription(
|
||||
'protect everything'
|
||||
);
|
||||
|
||||
const winDnsEventingCheckbox = await testSubjects.find('policyWindowsEvent_dns');
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow(
|
||||
winDnsEventingCheckbox
|
||||
);
|
||||
await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns');
|
||||
|
||||
expect(
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.getPackagePolicyDescriptionValue()
|
||||
).to.be('protect everything');
|
||||
});
|
||||
|
||||
it('should include updated endpoint data when saved', async () => {
|
||||
const winDnsEventingCheckbox = await testSubjects.find('policyWindowsEvent_dns');
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.scrollToCenterOfWindow(
|
||||
winDnsEventingCheckbox
|
||||
);
|
||||
await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns');
|
||||
const wasSelected = await winDnsEventingCheckbox.isSelected();
|
||||
await (await pageObjects.ingestManagerCreatePackagePolicy.findSaveButton(true)).click();
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.waitForSaveSuccessNotification(true);
|
||||
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.navigateToAgentPolicyEditPackagePolicy(
|
||||
policyInfo.agentPolicy.id,
|
||||
policyInfo.packagePolicy.id
|
||||
);
|
||||
expect(await testSubjects.isSelected('policyWindowsEvent_dns')).to.be(wasSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
const newPolicyName = `endpoint policy ${Date.now()}`;
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.selectAgentPolicy();
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.setPackagePolicyName(newPolicyName);
|
||||
await (await pageObjects.ingestManagerCreatePackagePolicy.findDSaveButton()).click();
|
||||
await (await pageObjects.ingestManagerCreatePackagePolicy.findSaveButton()).click();
|
||||
await pageObjects.ingestManagerCreatePackagePolicy.waitForSaveSuccessNotification();
|
||||
await pageObjects.policy.ensureIsOnPolicyPage();
|
||||
await policyTestResources.deletePolicyByName(newPolicyName);
|
||||
|
|
|
@ -41,8 +41,10 @@ export function IngestManagerCreatePackagePolicy({
|
|||
/**
|
||||
* Finds and returns the save button on the sticky bottom bar
|
||||
*/
|
||||
async findDSaveButton() {
|
||||
return await testSubjects.find('createPackagePolicySaveButton');
|
||||
async findSaveButton(forEditPage: boolean = false) {
|
||||
return await testSubjects.find(
|
||||
forEditPage ? 'saveIntegration' : 'createPackagePolicySaveButton'
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -80,11 +82,22 @@ export function IngestManagerCreatePackagePolicy({
|
|||
await testSubjects.setValue('packagePolicyNameInput', name);
|
||||
},
|
||||
|
||||
async getPackagePolicyDescriptionValue() {
|
||||
return await testSubjects.getAttribute('packagePolicyDescriptionInput', 'value');
|
||||
},
|
||||
|
||||
async setPackagePolicyDescription(desc: string) {
|
||||
await this.scrollToCenterOfWindow('packagePolicyDescriptionInput');
|
||||
await testSubjects.setValue('packagePolicyDescriptionInput', desc);
|
||||
},
|
||||
|
||||
/**
|
||||
* Waits for the save Notification toast to be visible
|
||||
*/
|
||||
async waitForSaveSuccessNotification() {
|
||||
await testSubjects.existOrFail('packagePolicyCreateSuccessToast');
|
||||
async waitForSaveSuccessNotification(forEditPage: boolean = false) {
|
||||
await testSubjects.existOrFail(
|
||||
forEditPage ? 'policyUpdateSuccessToast' : 'packagePolicyCreateSuccessToast'
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -115,11 +128,13 @@ export function IngestManagerCreatePackagePolicy({
|
|||
|
||||
/**
|
||||
* Center a given Element on the Window viewport
|
||||
* @param element
|
||||
* @param element if defined as a string, it should be the test subject to find
|
||||
*/
|
||||
async scrollToCenterOfWindow(element: WebElementWrapper) {
|
||||
async scrollToCenterOfWindow(element: WebElementWrapper | string) {
|
||||
const ele = typeof element === 'string' ? await testSubjects.find(element) : element;
|
||||
|
||||
const [elementPosition, windowSize] = await Promise.all([
|
||||
element.getPosition(),
|
||||
ele.getPosition(),
|
||||
browser.getWindowSize(),
|
||||
]);
|
||||
await browser.execute(
|
||||
|
|
Loading…
Reference in a new issue