[Fleet] remove enabled feature flags (#206448)

## Summary

Closes https://github.com/elastic/kibana/issues/190844

Removed feature flags enabled by default, deleted conditionals where not
needed.
This commit is contained in:
Julia Bardi 2025-01-15 10:41:15 +01:00 committed by GitHub
parent 62308641bf
commit 21493f7990
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 686 additions and 1292 deletions

View file

@ -8,25 +8,8 @@
export type ExperimentalFeatures = typeof allowedExperimentalValues;
const _allowedExperimentalValues = {
createPackagePolicyMultiPageLayout: true,
packageVerification: true,
diagnosticFileUploadEnabled: true,
displayAgentMetrics: true,
showIntegrationsSubcategories: true,
agentFqdnMode: true,
showExperimentalShipperOptions: false,
agentTamperProtectionEnabled: true,
secretsStorage: true,
kafkaOutput: true,
outputSecretsStorage: true,
remoteESOutput: true,
enableStrictKQLValidation: true,
subfeaturePrivileges: true,
advancedPolicySettings: true,
useSpaceAwareness: false,
enableReusableIntegrationPolicies: true,
asyncDeployPolicies: true,
enableExportCSV: true,
};
/**

View file

@ -4,12 +4,12 @@ Fleet is in the process to become space aware. This means that different Fleet o
## How to enable the feature
The feature is behind a feature flag `useSpaceAwareness`. If you are testing space awareness you could also be interested in testing `subfeaturePrivileges` at the same time which enables granular privileges to different parts of Fleet.
The feature is behind a feature flag `useSpaceAwareness`.
In your `kibana.yml` config
```yaml
xpack.fleet.enableExperimental: ['useSpaceAwareness', 'subfeaturePrivileges']
xpack.fleet.enableExperimental: ['useSpaceAwareness']
```
After the feature flag is enabled you will have to do another step to opt-in for the feature, that call will migrate the current space agnostic saved objects to new space aware saved objects.

View file

@ -60,7 +60,6 @@ import { EnrollmentTokenListPage } from './sections/agents/enrollment_token_list
import { UninstallTokenListPage } from './sections/agents/uninstall_token_list_page';
import { SettingsApp } from './sections/settings';
import { DebugPage } from './sections/debug';
import { ExperimentalFeaturesService } from './services';
import { ErrorLayout, PermissionsError } from './layouts';
const FEEDBACK_URL = 'https://ela.st/fleet-feedback';
@ -307,8 +306,6 @@ export const AppRoutes = memo(
const flyoutContext = useFlyoutContext();
const fleetStatus = useFleetStatus();
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
const authz = useAuthz();
return (
@ -364,21 +361,19 @@ export const AppRoutes = memo(
</AppLayout>
)}
</Route>
{agentTamperProtectionEnabled && (
<Route path={FLEET_ROUTING_PATHS.uninstall_tokens}>
{authz.fleet.allAgents ? (
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
<UninstallTokenListPage />
</AppLayout>
) : (
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
<ErrorLayout isAddIntegrationsPath={false}>
<PermissionsError error="MISSING_PRIVILEGES" requiredFleetRole="Agents All" />
</ErrorLayout>
</AppLayout>
)}
</Route>
)}
<Route path={FLEET_ROUTING_PATHS.uninstall_tokens}>
{authz.fleet.allAgents ? (
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
<UninstallTokenListPage />
</AppLayout>
) : (
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
<ErrorLayout isAddIntegrationsPath={false}>
<PermissionsError error="MISSING_PRIVILEGES" requiredFleetRole="Agents All" />
</ErrorLayout>
</AppLayout>
)}
</Route>
<Route path={FLEET_ROUTING_PATHS.data_streams}>
<AppLayout setHeaderActionMenu={setHeaderActionMenu}>
<DataStreamApp />

View file

@ -14,8 +14,6 @@ import type { Section } from '../../sections';
import { useLink, useConfig, useAuthz, useStartServices } from '../../hooks';
import { WithHeaderLayout } from '../../../../layouts';
import { ExperimentalFeaturesService } from '../../services';
import { DefaultPageTitle } from './default_page_title';
interface Props {
@ -32,8 +30,6 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
const { getHref } = useLink();
const { agents } = useConfig();
const authz = useAuthz();
const { agentTamperProtectionEnabled, subfeaturePrivileges } = ExperimentalFeaturesService.get();
const { docLinks } = useStartServices();
const granularPrivilegesCallout = useDismissableTour('GRANULAR_PRIVILEGES');
@ -83,7 +79,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
isSelected: section === 'uninstall_tokens',
href: getHref('uninstall_tokens'),
'data-test-subj': 'fleet-uninstall-tokens-tab',
isHidden: !authz.fleet.allAgents || !agentTamperProtectionEnabled, // needed only for agentTamperProtectionEnabled feature flag
isHidden: !authz.fleet.allAgents,
},
{
name: (
@ -115,7 +111,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
return (
<>
{!subfeaturePrivileges || !authz.fleet.all || granularPrivilegesCallout.isHidden ? null : (
{!authz.fleet.all || granularPrivilegesCallout.isHidden ? null : (
<EuiCallOut
size="s"
iconType="cheer"

View file

@ -18,7 +18,7 @@ import {
} from '../../../components';
import { FLEET_SERVER_PACKAGE } from '../../../constants';
import { policyHasFleetServer, ExperimentalFeaturesService } from '../../../services';
import { policyHasFleetServer } from '../../../services';
import { AgentUpgradeAgentModal } from '../../agents/components';
@ -50,8 +50,6 @@ export const AgentPolicyActionMenu = memo<{
useState<boolean>(false);
const [isUpgradeAgentsModalOpen, setIsUpgradeAgentsModalOpen] = useState<boolean>(false);
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
const isFleetServerPolicy = useMemo(
() =>
agentPolicy.package_policies?.some(
@ -228,7 +226,6 @@ export const AgentPolicyActionMenu = memo<{
if (
authz.fleet.allAgents &&
agentTamperProtectionEnabled &&
!agentPolicy?.is_managed &&
!agentPolicy?.supports_agentless
) {

View file

@ -53,7 +53,6 @@ import { UninstallCommandFlyout } from '../../../../../../components';
import type { ValidationResults } from '../agent_policy_validation';
import { ExperimentalFeaturesService } from '../../../../services';
import { useAgentPolicyFormContext } from '../agent_policy_form';
import { policyHasEndpointSecurity as hasElasticDefend } from '../../../../../../../common/services';
@ -121,7 +120,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
// agent monitoring checkbox group can appear multiple times in the DOM, ids have to be unique to work correctly
const monitoringCheckboxIdSuffix = Date.now();
const { agentTamperProtectionEnabled } = ExperimentalFeaturesService.get();
const licenseService = useLicense();
const [isUninstallCommandFlyoutOpen, setIsUninstallCommandFlyoutOpen] = useState(false);
const policyHasElasticDefend = useMemo(() => hasElasticDefend(agentPolicy), [agentPolicy]);
@ -227,12 +225,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
);
const AgentTamperProtectionSection = useMemo(() => {
if (
agentTamperProtectionEnabled &&
licenseService.isPlatinum() &&
!agentPolicy.is_managed &&
!agentPolicy.supports_agentless
) {
if (licenseService.isPlatinum() && !agentPolicy.is_managed && !agentPolicy.supports_agentless) {
if (AgentTamperProtectionWrapper) {
return (
<Suspense fallback={null}>
@ -245,7 +238,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
return AgentTamperProtectionSectionContent;
}
}, [
agentTamperProtectionEnabled,
licenseService,
agentPolicy.is_managed,
AgentTamperProtectionWrapper,

View file

@ -23,8 +23,6 @@ import { useAuthz } from '../../../../../hooks';
import { ConfiguredSettings } from '../../../components/form_settings';
import { ExperimentalFeaturesService } from '../../../../../services';
import { AgentPolicyAdvancedOptionsContent } from './agent_policy_advanced_fields';
import { AgentPolicyGeneralFields } from './agent_policy_general_fields';
import { AgentPolicyFormSystemMonitoringCheckbox } from './agent_policy_system_monitoring_field';
@ -74,8 +72,6 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
const authz = useAuthz();
const isDisabled = !authz.fleet.allAgentPolicies;
const { advancedPolicySettings } = ExperimentalFeaturesService.get();
const generalSettingsWrapper = (children: JSX.Element[]) => (
<EuiDescribedFormGroup
fullWidth
@ -152,37 +148,6 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
updateAgentPolicy={updateAgentPolicy}
validation={validation}
/>
{advancedPolicySettings ? (
<>
<EuiSpacer size="xl" />
<EuiTitle>
<h3>
<FormattedMessage
id="xpack.fleet.agentPolicyForm.advancedSettingsTitle"
defaultMessage="Advanced settings"
/>
</h3>
</EuiTitle>
<EuiSpacer size="m" />
<ConfiguredSettings
configuredSettings={AGENT_POLICY_ADVANCED_SETTINGS}
disabled={isDisabled}
/>
</>
) : null}
</StyledEuiAccordion>
</>
) : (
<>
<AgentPolicyAdvancedOptionsContent
agentPolicy={agentPolicy}
updateAgentPolicy={updateAgentPolicy}
validation={validation}
disabled={isDisabled}
/>
{advancedPolicySettings ? (
<>
<EuiSpacer size="xl" />
@ -197,10 +162,36 @@ export const AgentPolicyForm: React.FunctionComponent<Props> = ({
<EuiSpacer size="m" />
<ConfiguredSettings
configuredSettings={AGENT_POLICY_ADVANCED_SETTINGS}
disabled={isDisabled || !!agentPolicy?.supports_agentless}
disabled={isDisabled}
/>
</>
) : null}
</StyledEuiAccordion>
</>
) : (
<>
<AgentPolicyAdvancedOptionsContent
agentPolicy={agentPolicy}
updateAgentPolicy={updateAgentPolicy}
validation={validation}
disabled={isDisabled}
/>
<>
<EuiSpacer size="xl" />
<EuiTitle>
<h3>
<FormattedMessage
id="xpack.fleet.agentPolicyForm.advancedSettingsTitle"
defaultMessage="Advanced settings"
/>
</h3>
</EuiTitle>
<EuiSpacer size="m" />
<ConfiguredSettings
configuredSettings={AGENT_POLICY_ADVANCED_SETTINGS}
disabled={isDisabled || !!agentPolicy?.supports_agentless}
/>
</>
<EuiSpacer size="xl" />
</>
)}

View file

@ -43,9 +43,7 @@ function renderActions({ agent, agentPolicy }: { agent: Agent; agentPolicy?: Age
describe('AgentDetailsActionMenu', () => {
beforeEach(() => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: true,
} as any);
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
mockedUseAuthz.mockReturnValue({
fleet: {
readAgents: true,
@ -72,17 +70,6 @@ describe('AgentDetailsActionMenu', () => {
return utils.queryByTestId('requestAgentDiagnosticsBtn');
}
it('should not render action if feature is disabled', async () => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: false,
} as any);
const res = renderAndGetDiagnosticsButton({
agent: {} as Agent,
agentPolicy: {} as AgentPolicy,
});
expect(res).toBe(null);
});
it('should render an active action button if agent version >= 8.7', async () => {
const res = renderAndGetDiagnosticsButton({
agent: {

View file

@ -23,7 +23,6 @@ import {
import { useAgentRefresh } from '../hooks';
import { isAgentUpgradeable, policyHasFleetServer } from '../../../../services';
import { AgentRequestDiagnosticsModal } from '../../components/agent_request_diagnostics_modal';
import { ExperimentalFeaturesService } from '../../../../services';
import { AgentDetailsJsonFlyout } from './agent_details_json_flyout';
@ -53,7 +52,6 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
);
const hasFleetServer = agentPolicy && policyHasFleetServer(agentPolicy);
const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get();
const onClose = useMemo(() => {
if (onCancelReassign) {
@ -126,7 +124,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
defaultMessage="View agent JSON"
/>
</EuiContextMenuItem>,
...(authz.fleet.readAgents && diagnosticFileUploadEnabled
...(authz.fleet.readAgents
? [
<EuiContextMenuItem
icon="download"

View file

@ -23,7 +23,7 @@ import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
import type { Agent, AgentPolicy } from '../../../../../types';
import { useAgentVersion, useGetInfoOutputsForPolicy } from '../../../../../hooks';
import { ExperimentalFeaturesService, isAgentUpgradeable } from '../../../../../services';
import { isAgentUpgradeable } from '../../../../../services';
import { AgentPolicySummaryLine } from '../../../../../components';
import { AgentHealth } from '../../../components';
import { Tags } from '../../../components/tags';
@ -42,7 +42,6 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
agentPolicy?: AgentPolicy;
}> = memo(({ agent, agentPolicy }) => {
const latestAgentVersion = useAgentVersion();
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
const outputRes = useGetInfoOutputsForPolicy(agentPolicy?.id);
const outputs = outputRes?.data?.item;
@ -55,84 +54,82 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
gutterSize="m"
data-test-subj="agentDetailsOverviewSection"
>
{displayAgentMetrics && (
<EuiFlexGroup>
<FlexItemWithMinWidth grow={5}>
<EuiFlexGroup direction="column" gutterSize="m">
{[
{
title: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentDetails.cpuTooltip"
defaultMessage="Average CPU usage in the last 5 minutes"
/>
}
>
<span>
<FormattedMessage
id="xpack.fleet.agentDetails.cpuTitle"
defaultMessage="CPU"
/>
&nbsp;
<EuiIcon type="iInCircle" />
</span>
<EuiFlexGroup>
<FlexItemWithMinWidth grow={5}>
<EuiFlexGroup direction="column" gutterSize="m">
{[
{
title: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentDetails.cpuTooltip"
defaultMessage="Average CPU usage in the last 5 minutes"
/>
}
>
<span>
<FormattedMessage
id="xpack.fleet.agentDetails.cpuTitle"
defaultMessage="CPU"
/>
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
description: formatAgentCPU(agent.metrics, agentPolicy),
},
{
title: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentDetails.memoryTooltip"
defaultMessage="Average memory usage in the last 5 minutes"
/>
}
>
<span>
<FormattedMessage
id="xpack.fleet.agentDetails.memoryTitle"
defaultMessage="Memory"
/>
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
description: formatAgentMemory(agent.metrics, agentPolicy),
},
].map(({ title, description }) => {
const tooltip =
typeof description === 'string' && description.length > 20 ? description : '';
return (
<EuiFlexGroup>
<FlexItemWithMinWidth grow={8}>
<EuiDescriptionListTitle>{title}</EuiDescriptionListTitle>
</FlexItemWithMinWidth>
<FlexItemWithMinWidth grow={4}>
<EuiToolTip position="top" content={tooltip}>
<EuiDescriptionListDescription className="eui-textTruncate">
{description}
</EuiDescriptionListDescription>
</EuiToolTip>
),
description: formatAgentCPU(agent.metrics, agentPolicy),
},
{
title: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentDetails.memoryTooltip"
defaultMessage="Average memory usage in the last 5 minutes"
/>
}
>
<span>
<FormattedMessage
id="xpack.fleet.agentDetails.memoryTitle"
defaultMessage="Memory"
/>
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
description: formatAgentMemory(agent.metrics, agentPolicy),
},
].map(({ title, description }) => {
const tooltip =
typeof description === 'string' && description.length > 20 ? description : '';
return (
<EuiFlexGroup>
<FlexItemWithMinWidth grow={8}>
<EuiDescriptionListTitle>{title}</EuiDescriptionListTitle>
</FlexItemWithMinWidth>
<FlexItemWithMinWidth grow={4}>
<EuiToolTip position="top" content={tooltip}>
<EuiDescriptionListDescription className="eui-textTruncate">
{description}
</EuiDescriptionListDescription>
</EuiToolTip>
</FlexItemWithMinWidth>
</EuiFlexGroup>
);
})}
</EuiFlexGroup>
</FlexItemWithMinWidth>
<FlexItemWithMinWidth grow={5}>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<AgentDashboardLink agent={agent} agentPolicy={agentPolicy} />
</EuiFlexItem>
</EuiFlexGroup>
</FlexItemWithMinWidth>
</EuiFlexGroup>
)}
</FlexItemWithMinWidth>
</EuiFlexGroup>
);
})}
</EuiFlexGroup>
</FlexItemWithMinWidth>
<FlexItemWithMinWidth grow={5}>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<AgentDashboardLink agent={agent} agentPolicy={agentPolicy} />
</EuiFlexItem>
</EuiFlexGroup>
</FlexItemWithMinWidth>
</EuiFlexGroup>
{[
{
title: i18n.translate('xpack.fleet.agentDetails.statusLabel', {

View file

@ -26,8 +26,6 @@ import {
} from '../../../hooks';
import { WithHeaderLayout } from '../../../layouts';
import { ExperimentalFeaturesService } from '../../../services';
import { AgentRefreshContext } from './hooks';
import {
AgentLogs,
@ -41,7 +39,6 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
params: { agentId, tabId = '' },
} = useRouteMatch<{ agentId: string; tabId?: string }>();
const { getHref } = useLink();
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
const {
isLoading,
isInitialRequest,
@ -51,7 +48,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
} = useGetOneAgent(agentId, {
pollIntervalMs: 5000,
query: {
withMetrics: displayAgentMetrics,
withMetrics: true,
},
});
const {
@ -72,7 +69,6 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
navigateToApp(routeState.onDoneNavigateTo[0], routeState.onDoneNavigateTo[1]);
}
}, [routeState, navigateToApp]);
const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get();
const host = agentData?.item?.local_metadata?.host;
@ -156,19 +152,17 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
href: getHref('agent_details_logs', { agentId, tabId: 'logs' }),
isSelected: tabId === 'logs',
},
];
if (diagnosticFileUploadEnabled) {
tabs.push({
{
id: 'diagnostics',
name: i18n.translate('xpack.fleet.agentDetails.subTabs.diagnosticsTab', {
defaultMessage: 'Diagnostics',
}),
href: getHref('agent_details_diagnostics', { agentId, tabId: 'diagnostics' }),
isSelected: tabId === 'diagnostics',
});
}
},
];
return tabs;
}, [getHref, agentId, tabId, diagnosticFileUploadEnabled]);
}, [getHref, agentId, tabId]);
return (
<AgentRefreshContext.Provider

View file

@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
import type { Agent, AgentPolicy } from '../../../../types';
import { isAgentUpgradeable, ExperimentalFeaturesService } from '../../../../services';
import { isAgentUpgradeable } from '../../../../services';
import { AgentHealth } from '../../components';
import type { Pagination } from '../../../../hooks';
@ -99,7 +99,6 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
} = props;
const authz = useAuthz();
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
const { getHref } = useLink();
const latestAgentVersion = useAgentVersion();
@ -224,65 +223,59 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
);
},
},
...(displayAgentMetrics
? [
{
field: AGENTS_TABLE_FIELDS.METRICS,
sortable: false,
name: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentList.cpuTooltip"
defaultMessage="Average CPU usage in the last 5 minutes. This includes usage from the Agent and the component it supervises. Possible value ranges from 0 to (number of available CPU cores * 100)"
/>
}
>
<span>
<FormattedMessage id="xpack.fleet.agentList.cpuTitle" defaultMessage="CPU" />
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
width: '75px',
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
formatAgentCPU(
agent.metrics,
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
),
},
{
field: AGENTS_TABLE_FIELDS.METRICS,
sortable: false,
name: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentList.memoryTooltip"
defaultMessage="Average memory usage in the last 5 minutes"
/>
}
>
<span>
<FormattedMessage
id="xpack.fleet.agentList.memoryTitle"
defaultMessage="Memory"
/>
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
width: '90px',
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
formatAgentMemory(
agent.metrics,
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
),
},
]
: []),
{
field: AGENTS_TABLE_FIELDS.METRICS,
sortable: false,
name: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentList.cpuTooltip"
defaultMessage="Average CPU usage in the last 5 minutes. This includes usage from the Agent and the component it supervises. Possible value ranges from 0 to (number of available CPU cores * 100)"
/>
}
>
<span>
<FormattedMessage id="xpack.fleet.agentList.cpuTitle" defaultMessage="CPU" />
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
width: '75px',
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
formatAgentCPU(
agent.metrics,
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
),
},
{
field: AGENTS_TABLE_FIELDS.METRICS,
sortable: false,
name: (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentList.memoryTooltip"
defaultMessage="Average memory usage in the last 5 minutes"
/>
}
>
<span>
<FormattedMessage id="xpack.fleet.agentList.memoryTitle" defaultMessage="Memory" />
&nbsp;
<EuiIcon type="iInCircle" />
</span>
</EuiToolTip>
),
width: '90px',
render: (metrics: AgentMetrics | undefined, agent: Agent) =>
formatAgentMemory(
agent.metrics,
agent.policy_id ? agentPoliciesIndexedById[agent.policy_id] : undefined
),
},
{
field: AGENTS_TABLE_FIELDS.LAST_CHECKIN,
sortable: true,

View file

@ -48,9 +48,7 @@ const defaultProps = {
describe('AgentBulkActions', () => {
beforeAll(() => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: true,
} as any);
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
});
beforeEach(() => {

View file

@ -25,7 +25,6 @@ import {
} from '../../components';
import { useLicense } from '../../../../hooks';
import { LICENSE_FOR_SCHEDULE_UPGRADE, AGENTS_PREFIX } from '../../../../../../../common/constants';
import { ExperimentalFeaturesService } from '../../../../services';
import { getCommonTags } from '../utils';
@ -105,9 +104,8 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
: nAgentsInTable - totalManagedAgentIds?.length;
const [tagsPopoverButton, setTagsPopoverButton] = useState<HTMLElement>();
const { diagnosticFileUploadEnabled, enableExportCSV } = ExperimentalFeaturesService.get();
const { generateReportingJobCSV } = useExportCSV(enableExportCSV);
const { generateReportingJobCSV } = useExportCSV();
const menuItems = [
{
@ -190,27 +188,23 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
setUpgradeModalState({ isOpen: true, isScheduled: false, isUpdating: true });
},
},
...(diagnosticFileUploadEnabled
? [
{
name: (
<FormattedMessage
id="xpack.fleet.agentBulkActions.requestDiagnostics"
data-test-subj="agentBulkActionsRequestDiagnostics"
defaultMessage="Request diagnostics for {agentCount, plural, one {# agent} other {# agents}}"
values={{
agentCount,
}}
/>
),
icon: <EuiIcon type="download" size="m" />,
onClick: () => {
closeMenu();
setIsRequestDiagnosticsModalOpen(true);
},
},
]
: []),
{
name: (
<FormattedMessage
id="xpack.fleet.agentBulkActions.requestDiagnostics"
data-test-subj="agentBulkActionsRequestDiagnostics"
defaultMessage="Request diagnostics for {agentCount, plural, one {# agent} other {# agents}}"
values={{
agentCount,
}}
/>
),
icon: <EuiIcon type="download" size="m" />,
onClick: () => {
closeMenu();
setIsRequestDiagnosticsModalOpen(true);
},
},
{
name: (
<FormattedMessage
@ -228,27 +222,23 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
setIsUnenrollModalOpen(true);
},
},
...(enableExportCSV
? [
{
name: (
<FormattedMessage
id="xpack.fleet.agentBulkActions.exportAgents"
data-test-subj="bulkAgentExportBtn"
defaultMessage="Export {agentCount, plural, one {# agent} other {# agents}} as CSV"
values={{
agentCount,
}}
/>
),
icon: <EuiIcon type="exportAction" size="m" />,
onClick: () => {
closeMenu();
setIsExportCSVModalOpen(true);
},
},
]
: []),
{
name: (
<FormattedMessage
id="xpack.fleet.agentBulkActions.exportAgents"
data-test-subj="bulkAgentExportBtn"
defaultMessage="Export {agentCount, plural, one {# agent} other {# agents}} as CSV"
values={{
agentCount,
}}
/>
),
icon: <EuiIcon type="exportAction" size="m" />,
onClick: () => {
closeMenu();
setIsExportCSVModalOpen(true);
},
},
];
const panels = [

View file

@ -52,9 +52,7 @@ function renderTableRowActions({
}
describe('TableRowActions', () => {
beforeEach(() => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: true,
} as any);
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
mockedUseAuthz.mockReturnValue({
fleet: {
all: true,
@ -82,17 +80,6 @@ describe('TableRowActions', () => {
return utils.queryByTestId('requestAgentDiagnosticsBtn');
}
it('should not render action if feature is disabled', async () => {
mockedExperimentalFeaturesService.get.mockReturnValue({
diagnosticFileUploadEnabled: false,
} as any);
const res = renderAndGetDiagnosticsButton({
agent: { active: true } as Agent,
agentPolicy: {} as AgentPolicy,
});
expect(res).toBe(null);
});
it('should not render action if authz do not have Agents:All', async () => {
mockedUseAuthz.mockReturnValue({
fleet: {

View file

@ -18,7 +18,6 @@ import { useLink } from '../../../../hooks';
import { useAuthz } from '../../../../../../hooks/use_authz';
import { ContextMenuActions } from '../../../../components';
import { isAgentUpgradeable } from '../../../../services';
import { ExperimentalFeaturesService } from '../../../../services';
export const TableRowActions: React.FunctionComponent<{
agent: Agent;
@ -44,8 +43,6 @@ export const TableRowActions: React.FunctionComponent<{
const isUnenrolling = agent.status === 'unenrolling';
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { diagnosticFileUploadEnabled, agentTamperProtectionEnabled } =
ExperimentalFeaturesService.get();
const menuItems = [
<EuiContextMenuItem
icon="inspect"
@ -138,12 +135,7 @@ export const TableRowActions: React.FunctionComponent<{
);
}
if (
authz.fleet.allAgents &&
agentTamperProtectionEnabled &&
agent.policy_id &&
!agentPolicy?.supports_agentless
) {
if (authz.fleet.allAgents && agent.policy_id && !agentPolicy?.supports_agentless) {
menuItems.push(
<EuiContextMenuItem
icon="minusInCircle"
@ -164,7 +156,7 @@ export const TableRowActions: React.FunctionComponent<{
}
}
if (authz.fleet.readAgents && diagnosticFileUploadEnabled) {
if (authz.fleet.readAgents) {
menuItems.push(
<EuiContextMenuItem
key="requestAgentDiagnosticsBtn"

View file

@ -46,7 +46,7 @@ describe('export_csv', () => {
function render() {
const renderer = createFleetTestRendererMock();
return renderer.renderHook(() => useExportCSV(true));
return renderer.renderHook(() => useExportCSV());
}
beforeEach(() => {

View file

@ -27,12 +27,12 @@ import type { ExportField } from '../../components/agent_export_csv_modal';
import { getSortFieldForAPI } from './use_fetch_agents_data';
export function useExportCSV(enableExportCSV?: boolean) {
export function useExportCSV() {
const startServices = useStartServices();
const { notifications, http, uiSettings } = startServices;
const kibanaVersion = useKibanaVersion();
const { data: runtimeFieldsResponse } = useGetAgentStatusRuntimeFieldQuery({
enabled: enableExportCSV,
enabled: true,
});
const runtimeFields = runtimeFieldsResponse ? runtimeFieldsResponse : 'emit("")';

View file

@ -106,9 +106,7 @@ describe('useFetchAgentsData', () => {
const mockErrorToast = startServices.notifications.toasts.addError as jest.Mock;
beforeAll(() => {
mockedExperimentalFeaturesService.get.mockReturnValue({
displayAgentMetrics: true,
} as any);
mockedExperimentalFeaturesService.get.mockReturnValue({} as any);
});
beforeEach(() => {

View file

@ -25,7 +25,7 @@ import {
sendGetActionStatus,
sendBulkGetAgentPolicies,
} from '../../../../hooks';
import { AgentStatusKueryHelper, ExperimentalFeaturesService } from '../../../../services';
import { AgentStatusKueryHelper } from '../../../../services';
import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../../../constants';
import { getKuery } from '../utils/get_kuery';
@ -97,7 +97,6 @@ export const getSortFieldForAPI = (field: string): string => {
export function useFetchAgentsData() {
const fullAgentPolicyFecher = useFullAgentPolicyFetcher();
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
const { notifications } = useStartServices();
@ -218,7 +217,7 @@ export function useFetchAgentsData() {
showInactive,
showUpgradeable,
getStatusSummary: true,
withMetrics: displayAgentMetrics,
withMetrics: true,
}),
sendGetAgentStatus({
kuery: AgentStatusKueryHelper.buildKueryForInactiveAgents(),
@ -362,7 +361,6 @@ export function useFetchAgentsData() {
sortOrder,
showInactive,
showUpgradeable,
displayAgentMetrics,
fullAgentPolicyFecher,
allTags,
latestAgentActionErrors,

View file

@ -190,9 +190,7 @@ describe('EditOutputFlyout', () => {
});
it('should populate secret input with plain text value when editing kafka output', async () => {
jest
.spyOn(ExperimentalFeaturesService, 'get')
.mockReturnValue({ outputSecretsStorage: true, kafkaOutput: true } as any);
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
mockedUseFleetStatus.mockReturnValue({
isLoading: false,
@ -230,9 +228,7 @@ describe('EditOutputFlyout', () => {
});
it('should populate secret password input with plain text value when editing kafka output', async () => {
jest
.spyOn(ExperimentalFeaturesService, 'get')
.mockReturnValue({ outputSecretsStorage: true, kafkaOutput: true } as any);
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
mockedUseFleetStatus.mockReturnValue({
isLoading: false,
@ -273,9 +269,7 @@ describe('EditOutputFlyout', () => {
});
it('should populate secret input with plain text value when editing logstash output', async () => {
jest
.spyOn(ExperimentalFeaturesService, 'get')
.mockReturnValue({ outputSecretsStorage: true } as any);
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
mockedUseFleetStatus.mockReturnValue({
isLoading: false,
@ -325,9 +319,7 @@ describe('EditOutputFlyout', () => {
});
it('should render the flyout if the output provided is a remote ES output', async () => {
jest
.spyOn(ExperimentalFeaturesService, 'get')
.mockReturnValue({ remoteESOutput: true, outputSecretsStorage: true } as any);
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
mockedUseFleetStatus.mockReturnValue({
isLoading: false,
@ -356,9 +348,7 @@ describe('EditOutputFlyout', () => {
});
it('should populate secret service token input with plain text value when editing remote ES output', async () => {
jest
.spyOn(ExperimentalFeaturesService, 'get')
.mockReturnValue({ remoteESOutput: true, outputSecretsStorage: true } as any);
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
mockedUseFleetStatus.mockReturnValue({
isLoading: false,
@ -394,7 +384,7 @@ describe('EditOutputFlyout', () => {
});
it('should not display remote ES output in type lists if serverless', async () => {
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ remoteESOutput: true } as any);
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any);
mockUseStartServices.mockReset();
mockStartServices(true);
const { utils } = renderFlyout({

View file

@ -41,8 +41,6 @@ import {
outputYmlIncludesReservedPerformanceKey,
} from '../../../../../../../common/services/output_helpers';
import { ExperimentalFeaturesService } from '../../../../../../services';
import { outputType, RESERVED_CONFIG_YML_KEYS } from '../../../../../../../common/constants';
import { MAX_FLYOUT_WIDTH } from '../../../../constants';
@ -105,9 +103,6 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
[proxies]
);
const { kafkaOutput: isKafkaOutputEnabled, remoteESOutput: isRemoteESOutputEnabled } =
ExperimentalFeaturesService.get();
const isRemoteESOutput = inputs.typeInput.value === outputType.RemoteElasticsearch;
const isESOutput = inputs.typeInput.value === outputType.Elasticsearch;
const supportsPresets = inputs.typeInput.value
@ -119,11 +114,11 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
const OUTPUT_TYPE_OPTIONS = [
{ value: outputType.Elasticsearch, text: 'Elasticsearch' },
...(isRemoteESOutputEnabled && isStateful
...(isStateful
? [{ value: outputType.RemoteElasticsearch, text: 'Remote Elasticsearch' }]
: []),
{ value: outputType.Logstash, text: 'Logstash' },
...(isKafkaOutputEnabled ? [{ value: outputType.Kafka, text: 'Kafka' }] : []),
{ value: outputType.Kafka, text: 'Kafka' },
];
const renderLogstashSection = () => {
@ -142,29 +137,23 @@ export const EditOutputFlyout: React.FunctionComponent<EditOutputFlyoutProps> =
};
const renderRemoteElasticsearchSection = () => {
if (isRemoteESOutputEnabled) {
return (
<OutputFormRemoteEsSection
inputs={inputs}
useSecretsStorage={useSecretsStorage}
onToggleSecretStorage={onToggleSecretStorage}
/>
);
}
return null;
return (
<OutputFormRemoteEsSection
inputs={inputs}
useSecretsStorage={useSecretsStorage}
onToggleSecretStorage={onToggleSecretStorage}
/>
);
};
const renderKafkaSection = () => {
if (isKafkaOutputEnabled) {
return (
<OutputFormKafkaSection
inputs={inputs}
useSecretsStorage={useSecretsStorage}
onToggleSecretStorage={onToggleSecretStorage}
/>
);
}
return null;
return (
<OutputFormKafkaSection
inputs={inputs}
useSecretsStorage={useSecretsStorage}
onToggleSecretStorage={onToggleSecretStorage}
/>
);
};
const renderOutputTypeSection = (type: string) => {

View file

@ -34,8 +34,6 @@ import type {
import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages';
import { ExperimentalFeaturesService } from '../../../../services';
import { promoteFeaturedIntegrations } from '../utils';
import { ControlsColumn } from './controls';
@ -111,8 +109,6 @@ export const PackageListGrid: FunctionComponent<PackageListGridProps> = ({
const MAX_SUBCATEGORIES_NUMBER = 6;
const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get();
const onButtonClick = () => {
setPopover(!isPopoverOpen);
};
@ -215,66 +211,65 @@ export const PackageListGrid: FunctionComponent<PackageListGridProps> = ({
</EuiFlexItem>
)}
{showIntegrationsSubcategories && availableSubCategories?.length ? <EuiSpacer /> : null}
{showIntegrationsSubcategories ? (
<EuiFlexItem grow={false}>
<EuiFlexGroup
data-test-subj="epmList.subcategoriesRow"
justifyContent="flexStart"
direction="row"
gutterSize="s"
style={{
maxWidth: 943,
}}
>
{visibleSubCategories?.map((subCategory) => {
const isSelected = subCategory.id === selectedSubCategory;
return (
<EuiFlexItem grow={false} key={subCategory.id}>
<EuiButton
css={isSelected ? 'color: white' : ''}
color={isSelected ? 'accent' : 'text'}
fill={isSelected}
aria-label={subCategory?.title}
onClick={() => onSubCategoryClick(subCategory.id)}
>
<FormattedMessage
id="xpack.fleet.epmList.subcategoriesButton"
defaultMessage="{subcategory}"
values={{
subcategory: subCategory.title,
}}
/>
</EuiButton>
</EuiFlexItem>
);
})}
{hiddenSubCategoriesItems?.length ? (
<EuiFlexItem grow={false}>
<EuiPopover
data-test-subj="epmList.showMoreSubCategoriesButton"
id="moreSubCategories"
button={
<EuiButtonIcon
display="base"
onClick={onButtonClick}
iconType="boxesHorizontal"
aria-label="Show more subcategories"
size="m"
/>
}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
{availableSubCategories?.length ? <EuiSpacer /> : null}
<EuiFlexItem grow={false}>
<EuiFlexGroup
data-test-subj="epmList.subcategoriesRow"
justifyContent="flexStart"
direction="row"
gutterSize="s"
style={{
maxWidth: 943,
}}
>
{visibleSubCategories?.map((subCategory) => {
const isSelected = subCategory.id === selectedSubCategory;
return (
<EuiFlexItem grow={false} key={subCategory.id}>
<EuiButton
css={isSelected ? 'color: white' : ''}
color={isSelected ? 'accent' : 'text'}
fill={isSelected}
aria-label={subCategory?.title}
onClick={() => onSubCategoryClick(subCategory.id)}
>
<EuiContextMenuPanel size="s" items={hiddenSubCategoriesItems} />
</EuiPopover>
<FormattedMessage
id="xpack.fleet.epmList.subcategoriesButton"
defaultMessage="{subcategory}"
values={{
subcategory: subCategory.title,
}}
/>
</EuiButton>
</EuiFlexItem>
) : null}
</EuiFlexGroup>
</EuiFlexItem>
) : null}
);
})}
{hiddenSubCategoriesItems?.length ? (
<EuiFlexItem grow={false}>
<EuiPopover
data-test-subj="epmList.showMoreSubCategoriesButton"
id="moreSubCategories"
button={
<EuiButtonIcon
display="base"
onClick={onButtonClick}
iconType="boxesHorizontal"
aria-label="Show more subcategories"
size="m"
/>
}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={hiddenSubCategoriesItems} />
</EuiPopover>
</EuiFlexItem>
) : null}
</EuiFlexGroup>
</EuiFlexItem>
{callout ? (
<>
<EuiSpacer size={calloutTopSpacerSize} />

View file

@ -48,7 +48,6 @@ import {
useGetSettingsQuery,
} from '../../../../hooks';
import { INTEGRATIONS_ROUTING_PATHS } from '../../../../constants';
import { ExperimentalFeaturesService } from '../../../../services';
import {
useGetPackageInfoByKeyQuery,
useLink,
@ -162,8 +161,6 @@ export function Detail() {
const services = useStartServices();
const isCloud = !!services?.cloud?.cloudId;
const { createPackagePolicyMultiPageLayout: isExperimentalAddIntegrationPageEnabled } =
ExperimentalFeaturesService.get();
const agentPolicyIdFromContext = getAgentPolicyId();
const isOverviewPage = panel === 'overview';
@ -412,7 +409,6 @@ export function Detail() {
currentPath,
integration,
isCloud,
isExperimentalAddIntegrationPageEnabled,
isFirstTimeAgentUser,
isGuidedOnboardingActive,
pkgkey,
@ -445,7 +441,6 @@ export function Detail() {
history,
integration,
isCloud,
isExperimentalAddIntegrationPageEnabled,
isFirstTimeAgentUser,
isGuidedOnboardingActive,
onboardingAppId,

View file

@ -27,7 +27,6 @@ interface GetInstallPkgRouteOptionsParams {
agentPolicyId?: string;
pkgkey: string;
isCloud: boolean;
isExperimentalAddIntegrationPageEnabled: boolean;
isFirstTimeAgentUser: boolean;
isGuidedOnboardingActive: boolean;
}
@ -50,16 +49,12 @@ export const getInstallPkgRouteOptions = ({
pkgkey,
isFirstTimeAgentUser,
isCloud,
isExperimentalAddIntegrationPageEnabled,
isGuidedOnboardingActive,
}: GetInstallPkgRouteOptionsParams): InstallPkgRouteOptions => {
const integrationOpts: { integration?: string } = integration ? { integration } : {};
const packageExemptFromStepsLayout = isPackageExemptFromStepsLayout(pkgkey);
const useMultiPageLayout =
isExperimentalAddIntegrationPageEnabled &&
isCloud &&
(isFirstTimeAgentUser || isGuidedOnboardingActive) &&
!packageExemptFromStepsLayout;
isCloud && (isFirstTimeAgentUser || isGuidedOnboardingActive) && !packageExemptFromStepsLayout;
const path = pagePathGetters.add_integration_to_policy({
pkgkey,
useMultiPageLayout,

View file

@ -23,7 +23,7 @@ import { useMergeEprPackagesWithReplacements } from '../../../../../hooks/use_me
import { mapToCard } from '..';
import type { PackageList, PackageListItem } from '../../../../../types';
import { doesPackageHaveIntegrations, ExperimentalFeaturesService } from '../../../../../services';
import { doesPackageHaveIntegrations } from '../../../../../services';
import {
isInputOnlyPolicyTemplate,
@ -140,7 +140,6 @@ export const useAvailablePackages = ({
}) => {
const [preference, setPreference] = useState<IntegrationPreferenceType>('recommended');
const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get();
const { isAgentlessEnabled } = useAgentless();
const {
@ -244,16 +243,12 @@ export const useAvailablePackages = ({
// Filter out subcategories
const mainCategories = useMemo(() => {
return showIntegrationsSubcategories
? allCategories.filter((category) => category.parent_id === undefined)
: allCategories;
}, [allCategories, showIntegrationsSubcategories]);
return allCategories.filter((category) => category.parent_id === undefined);
}, [allCategories]);
const availableSubCategories = useMemo(() => {
return showIntegrationsSubcategories
? allCategories?.filter((c) => c.parent_id === selectedCategory)
: [];
}, [allCategories, selectedCategory, showIntegrationsSubcategories]);
return allCategories?.filter((c) => c.parent_id === selectedCategory);
}, [allCategories, selectedCategory]);
return {
initialSelectedCategory,

View file

@ -49,18 +49,14 @@ export const getFleetDeepLinks: (
path: FLEET_ROUTING_PATHS.enrollment_tokens,
visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'],
},
...((experimentalFeatures.agentTamperProtectionEnabled
? [
{
id: FleetDeepLinkId.uninstallTokens,
title: i18n.translate('xpack.fleet.deepLinks.uninstallTokens.title', {
defaultMessage: 'Uninstall tokens',
}),
path: FLEET_ROUTING_PATHS.uninstall_tokens,
visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'],
},
]
: []) as AppDeepLink[]),
{
id: FleetDeepLinkId.uninstallTokens,
title: i18n.translate('xpack.fleet.deepLinks.uninstallTokens.title', {
defaultMessage: 'Uninstall tokens',
}),
path: FLEET_ROUTING_PATHS.uninstall_tokens,
visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'],
},
{
id: FleetDeepLinkId.dataStreams,
title: i18n.translate('xpack.fleet.deepLinks.dataStreams.title', {

View file

@ -6,7 +6,6 @@
*/
import { LICENCE_FOR_MULTIPLE_AGENT_POLICIES } from '../../common/constants';
import { ExperimentalFeaturesService } from '../services';
import { licenseService } from './use_license';
@ -15,9 +14,7 @@ export function useMultipleAgentPolicies() {
}
export function canUseMultipleAgentPolicies() {
const { enableReusableIntegrationPolicies } = ExperimentalFeaturesService.get();
const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES);
return Boolean(enableReusableIntegrationPolicies && hasEnterpriseLicence);
return Boolean(hasEnterpriseLicence);
}

View file

@ -333,7 +333,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
all: capabilities.fleet.all as boolean,
read: capabilities.fleet.read as boolean,
},
subfeatureEnabled: this.experimentalFeatures.subfeaturePrivileges ?? false,
subfeatureEnabled: true,
}),
packagePrivileges: calculatePackagePrivilegesFromCapabilities(capabilities),
endpointExceptionsPrivileges:

View file

@ -12,10 +12,6 @@ import { ElasticsearchAssetType } from '../../common/types';
import { hasDeferredInstallations } from './has_deferred_installations';
import { ExperimentalFeaturesService } from '.';
const mockGet = jest.spyOn(ExperimentalFeaturesService, 'get');
const createPackage = ({
installedEs = [],
}: {
@ -46,47 +42,38 @@ const createPackage = ({
});
describe('isPackageUnverified', () => {
describe('When experimental feature is disabled', () => {
beforeEach(() => {
// @ts-ignore don't want to define all experimental features here
mockGet.mockReturnValue({
packageVerification: false,
} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
it('Should return false for a package with no installationInfo', () => {
const noSoPkg = createPackage();
// @ts-ignore we know pkg has installationInfo but ts doesn't
delete noSoPkg.installationInfo;
expect(hasDeferredInstallations(noSoPkg)).toEqual(false);
});
it('Should return true for a package with at least one asset deferred', () => {
const pkgWithDeferredInstallations = createPackage({
installedEs: [
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
{ id: '', type: ElasticsearchAssetType.transform, deferred: true },
],
});
it('Should return false for a package with no installationInfo', () => {
const noSoPkg = createPackage();
// @ts-ignore we know pkg has installationInfo but ts doesn't
delete noSoPkg.installationInfo;
expect(hasDeferredInstallations(noSoPkg)).toEqual(false);
});
expect(hasDeferredInstallations(pkgWithDeferredInstallations)).toEqual(true);
});
it('Should return true for a package with at least one asset deferred', () => {
const pkgWithDeferredInstallations = createPackage({
installedEs: [
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
{ id: '', type: ElasticsearchAssetType.transform, deferred: true },
],
});
expect(hasDeferredInstallations(pkgWithDeferredInstallations)).toEqual(true);
it('Should return false for a package that has no asset deferred', () => {
const pkgWithoutDeferredInstallations = createPackage({
installedEs: [
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
{ id: '', type: ElasticsearchAssetType.transform, deferred: false },
],
});
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
});
it('Should return false for a package that has no asset deferred', () => {
const pkgWithoutDeferredInstallations = createPackage({
installedEs: [
{ id: '', type: ElasticsearchAssetType.ingestPipeline },
{ id: '', type: ElasticsearchAssetType.transform, deferred: false },
],
});
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
});
it('Should return false for a package that has no asset', () => {
const pkgWithoutDeferredInstallations = createPackage({
installedEs: [],
});
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
it('Should return false for a package that has no asset', () => {
const pkgWithoutDeferredInstallations = createPackage({
installedEs: [],
});
expect(hasDeferredInstallations(pkgWithoutDeferredInstallations)).toEqual(false);
});
});

View file

@ -44,52 +44,9 @@ const createPackage = ({
});
describe('isPackageUnverified', () => {
describe('When experimental feature is disabled', () => {
beforeEach(() => {
// @ts-ignore don't want to define all experimental features here
mockGet.mockReturnValue({
packageVerification: false,
} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
});
it('Should return false for a package with no saved object', () => {
const noSoPkg = createPackage();
// @ts-ignore we know pkg has installationInfo but ts doesn't
delete noSoPkg.installationInfo;
expect(isPackageUnverified(noSoPkg)).toEqual(false);
});
it('Should return false for an unverified package', () => {
const unverifiedPkg = createPackage({ verificationStatus: 'unverified' });
expect(isPackageUnverified(unverifiedPkg)).toEqual(false);
});
it('Should return false for a verified package', () => {
const verifiedPkg = createPackage({ verificationStatus: 'verified' });
expect(isPackageUnverified(verifiedPkg)).toEqual(false);
});
it('Should return false for a verified package with correct key', () => {
const unverifiedPkg = createPackage({
verificationStatus: 'verified',
verificationKeyId: '1234',
});
expect(isPackageUnverified(unverifiedPkg, '1234')).toEqual(false);
});
it('Should return false for a verified package with out of date key', () => {
const unverifiedPkg = createPackage({
verificationStatus: 'verified',
verificationKeyId: '1234',
});
expect(isPackageUnverified(unverifiedPkg, 'not_1234')).toEqual(false);
});
it('Should return false for an unknown verification package', () => {
const unknownPkg = createPackage({ verificationStatus: 'unknown' });
expect(isPackageUnverified(unknownPkg)).toEqual(false);
});
});
describe('When experimental feature is enabled', () => {
beforeEach(() => {
mockGet.mockReturnValue({
packageVerification: true,
} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
mockGet.mockReturnValue({} as ReturnType<(typeof ExperimentalFeaturesService)['get']>);
});
it('Should return false for a package with no saved object', () => {
const noSoPkg = createPackage();

View file

@ -11,8 +11,6 @@ import type { PackageInfo, PackageListItem } from '../types';
import type { RequestError } from '../hooks';
import { ExperimentalFeaturesService } from '.';
export function isPackageUnverified(
pkg: PackageInfo | PackageListItem,
packageVerificationKeyId?: string
@ -22,11 +20,10 @@ export function isPackageUnverified(
const { verification_status: verificationStatus, verification_key_id: verificationKeyId } =
pkg?.installationInfo;
const { packageVerification: isPackageVerificationEnabled } = ExperimentalFeaturesService.get();
const isKeyOutdated = !!verificationKeyId && verificationKeyId !== packageVerificationKeyId;
const isUnverified =
verificationStatus === 'unverified' || (verificationStatus === 'verified' && isKeyOutdated);
return isPackageVerificationEnabled && isUnverified;
return isUnverified;
}
export const isVerificationError = (err?: FleetErrorResponse | RequestError) =>

View file

@ -150,7 +150,7 @@ describe('Config schema', () => {
it('should not add a depreciations when enabling an existing experimental feature', () => {
const res = applyConfigDeprecations({
enableExperimental: ['displayAgentMetrics'],
enableExperimental: ['useSpaceAwareness'],
});
expect(res.messages).toMatchInlineSnapshot(`Array []`);

View file

@ -112,11 +112,7 @@ export const createAppContextStartContractMock = (
securitySetup: securityMock.createSetup(),
securityStart: securityMock.createStart(),
logger: loggingSystemMock.create().get(),
experimentalFeatures: {
agentTamperProtectionEnabled: true,
diagnosticFileUploadEnabled: true,
enableReusableIntegrationPolicies: true,
} as ExperimentalFeatures,
experimentalFeatures: {} as ExperimentalFeatures,
isProductionMode: true,
configInitialValue: {
agents: { enabled: true, elasticsearch: {} },

View file

@ -368,115 +368,110 @@ export class FleetPlugin
},
],
},
subFeatures: experimentalFeatures.subfeaturePrivileges
? [
subFeatures: [
{
name: 'Agents',
requireAllSpaces,
privilegeGroups: [
{
name: 'Agents',
requireAllSpaces,
privilegeGroups: [
groupType: 'mutually_exclusive',
privileges: [
{
groupType: 'mutually_exclusive',
privileges: [
{
id: `agents_all`,
api: [`${PLUGIN_ID}-agents-read`, `${PLUGIN_ID}-agents-all`],
name: 'All',
ui: ['agents_read', 'agents_all'],
savedObject: {
all: allSavedObjectTypes,
read: allSavedObjectTypes,
},
includeIn: 'all',
},
{
id: `agents_read`,
api: [`${PLUGIN_ID}-agents-read`],
name: 'Read',
ui: ['agents_read'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
includeIn: 'read',
alerting: {},
},
],
id: `agents_all`,
api: [`${PLUGIN_ID}-agents-read`, `${PLUGIN_ID}-agents-all`],
name: 'All',
ui: ['agents_read', 'agents_all'],
savedObject: {
all: allSavedObjectTypes,
read: allSavedObjectTypes,
},
includeIn: 'all',
},
{
id: `agents_read`,
api: [`${PLUGIN_ID}-agents-read`],
name: 'Read',
ui: ['agents_read'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
includeIn: 'read',
alerting: {},
},
],
},
],
},
{
name: 'Agent policies',
requireAllSpaces,
privilegeGroups: [
{
name: 'Agent policies',
requireAllSpaces,
privilegeGroups: [
groupType: 'mutually_exclusive',
privileges: [
{
groupType: 'mutually_exclusive',
privileges: [
{
id: `agent_policies_all`,
api: [
`${PLUGIN_ID}-agent-policies-read`,
`${PLUGIN_ID}-agent-policies-all`,
],
name: 'All',
ui: ['agent_policies_read', 'agent_policies_all'],
savedObject: {
all: allSavedObjectTypes,
read: allSavedObjectTypes,
},
includeIn: 'all',
},
{
id: `agent_policies_read`,
api: [`${PLUGIN_ID}-agent-policies-read`],
name: 'Read',
ui: ['agent_policies_read'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
includeIn: 'read',
alerting: {},
},
],
id: `agent_policies_all`,
api: [`${PLUGIN_ID}-agent-policies-read`, `${PLUGIN_ID}-agent-policies-all`],
name: 'All',
ui: ['agent_policies_read', 'agent_policies_all'],
savedObject: {
all: allSavedObjectTypes,
read: allSavedObjectTypes,
},
includeIn: 'all',
},
{
id: `agent_policies_read`,
api: [`${PLUGIN_ID}-agent-policies-read`],
name: 'Read',
ui: ['agent_policies_read'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
includeIn: 'read',
alerting: {},
},
],
},
],
},
{
name: 'Settings',
requireAllSpaces,
privilegeGroups: [
{
name: 'Settings',
requireAllSpaces,
privilegeGroups: [
groupType: 'mutually_exclusive',
privileges: [
{
groupType: 'mutually_exclusive',
privileges: [
{
id: `settings_all`,
api: [`${PLUGIN_ID}-settings-read`, `${PLUGIN_ID}-settings-all`],
name: 'All',
ui: ['settings_read', 'settings_all'],
savedObject: {
all: allSavedObjectTypes,
read: allSavedObjectTypes,
},
includeIn: 'all',
},
{
id: `settings_read`,
api: [`${PLUGIN_ID}-settings-read`],
name: 'Read',
ui: ['settings_read'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
includeIn: 'read',
alerting: {},
},
],
id: `settings_all`,
api: [`${PLUGIN_ID}-settings-read`, `${PLUGIN_ID}-settings-all`],
name: 'All',
ui: ['settings_read', 'settings_all'],
savedObject: {
all: allSavedObjectTypes,
read: allSavedObjectTypes,
},
includeIn: 'all',
},
{
id: `settings_read`,
api: [`${PLUGIN_ID}-settings-read`],
name: 'Read',
ui: ['settings_read'],
savedObject: {
all: [],
read: allSavedObjectTypes,
},
includeIn: 'read',
alerting: {},
},
],
},
]
: [],
],
},
],
privileges: {
all: {
api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`],

View file

@ -8,7 +8,6 @@ import { schema } from '@kbn/config-schema';
import type { FleetAuthz } from '../../../common';
import { API_VERSIONS } from '../../../common/constants';
import { parseExperimentalConfigValue } from '../../../common/experimental_features';
import { getRouteRequiredAuthz, type FleetAuthzRouter } from '../../services/security';
import { AGENT_API_ROUTES } from '../../constants';
@ -854,36 +853,32 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT
getAvailableVersionsHandler
);
const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental);
// route used by export CSV feature on the UI to generate report
if (experimentalFeatures.enableExportCSV) {
router.versioned
.get({
path: '/internal/fleet/agents/status_runtime_field',
access: 'internal',
security: {
authz: {
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.READ],
},
router.versioned
.get({
path: '/internal/fleet/agents/status_runtime_field',
access: 'internal',
security: {
authz: {
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.READ],
},
})
.addVersion(
{
version: API_VERSIONS.internal.v1,
validate: {
request: {},
response: {
200: {
body: () => schema.string(),
},
400: {
body: genericErrorResponse,
},
},
})
.addVersion(
{
version: API_VERSIONS.internal.v1,
validate: {
request: {},
response: {
200: {
body: () => schema.string(),
},
400: {
body: genericErrorResponse,
},
},
},
getAgentStatusRuntimeFieldHandler
);
}
},
getAgentStatusRuntimeFieldHandler
);
};

View file

@ -22,7 +22,6 @@ jest.mock('../../services', () => ({
appContextService: {
getSecurityLicense: jest.fn().mockReturnValue({ isEnabled: jest.fn().mockReturnValue(false) }),
getCloud: jest.fn().mockReturnValue({ isServerlessEnabled: false } as any),
getExperimentalFeatures: jest.fn().mockReturnValue({ subfeaturePrivileges: false }),
getLogger: jest.fn().mockReturnValue({ debug: jest.fn(), error: jest.fn() } as any),
},
}));

View file

@ -33,12 +33,10 @@ export const getCheckPermissionsHandler: FleetRequestHandler<
};
const isServerless = appContextService.getCloud()?.isServerlessEnabled;
const isSubfeaturePrivilegesEnabled =
appContextService.getExperimentalFeatures().subfeaturePrivileges ?? false;
if (!appContextService.getSecurityLicense().isEnabled()) {
return response.ok({ body: missingSecurityBody });
} else if (isSubfeaturePrivilegesEnabled) {
} else {
const fleetContext = await context.fleet;
if (
!fleetContext.authz.fleet.all &&
@ -70,34 +68,6 @@ export const getCheckPermissionsHandler: FleetRequestHandler<
}
}
return response.ok({ body: { success: true } as CheckPermissionsResponse });
} else {
const fleetContext = await context.fleet;
if (!fleetContext.authz.fleet.all) {
return response.ok({
body: {
success: false,
error: 'MISSING_PRIVILEGES',
} as CheckPermissionsResponse,
});
}
// check the manage_service_account cluster privilege only on stateful
else if (request.query.fleetServerSetup && !isServerless) {
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const { has_all_requested: hasAllPrivileges } = await esClient.security.hasPrivileges({
body: { cluster: ['manage_service_account'] },
});
if (!hasAllPrivileges) {
return response.ok({
body: {
success: false,
error: 'MISSING_FLEET_SERVER_SETUP_PRIVILEGES',
} as CheckPermissionsResponse,
});
}
}
return response.ok({ body: { success: true } as CheckPermissionsResponse });
}
};

View file

@ -403,9 +403,6 @@ describe('When calling package policy', () => {
inputs: [],
});
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any);
await expect(() => routeHandler(context, request, response)).rejects.toThrow(

View file

@ -39,6 +39,9 @@ jest.mock('../../services/setup', () => {
});
jest.mock('../../services/fleet_server');
jest.mock('../../services/secrets', () => ({
isSecretStorageEnabled: jest.fn().mockResolvedValue(true),
}));
const mockSetupFleet = setupFleet as jest.MockedFunction<typeof setupFleet>;
@ -199,7 +202,7 @@ describe('FleetStatusHandler', () => {
const expectedBody = {
isReady: true,
is_secrets_storage_enabled: false,
is_secrets_storage_enabled: true,
is_space_awareness_enabled: false,
missing_optional_features: [],
missing_requirements: [],
@ -221,7 +224,7 @@ describe('FleetStatusHandler', () => {
const expectedBody = {
isReady: false,
is_secrets_storage_enabled: false,
is_secrets_storage_enabled: true,
is_space_awareness_enabled: false,
missing_optional_features: [],
missing_requirements: ['api_keys', 'fleet_server'],
@ -252,7 +255,7 @@ describe('FleetStatusHandler', () => {
const expectedBody = {
isReady: true,
is_secrets_storage_enabled: false,
is_secrets_storage_enabled: true,
is_space_awareness_enabled: false,
missing_optional_features: [],
missing_requirements: [],

View file

@ -6,15 +6,10 @@
*/
import type { TypeOf } from '@kbn/config-schema';
import type { KibanaRequest, VersionedRouter } from '@kbn/core-http-server';
import { httpServerMock, coreMock, loggingSystemMock } from '@kbn/core/server/mocks';
import type { RequestHandler } from '@kbn/core/server';
import type { KibanaRequest } from '@kbn/core-http-server';
import { httpServerMock, coreMock } from '@kbn/core/server/mocks';
import {
makeRouterWithFleetAuthz,
withDefaultErrorHandler,
} from '../../services/security/fleet_router';
import type { FleetAuthzRouter } from '../../services/security/types';
import { withDefaultErrorHandler } from '../../services/security/fleet_router';
import type {
UninstallToken,
@ -40,8 +35,6 @@ import {
import { createAgentPolicyMock } from '../../../common/mocks';
import { registerRoutes } from '.';
import { getUninstallTokenHandler, getUninstallTokensMetadataHandler } from './handlers';
const getUninstallTokenHandlerWithErrorHandler = withDefaultErrorHandler(getUninstallTokenHandler);
@ -197,48 +190,4 @@ describe('uninstall token handlers', () => {
});
});
});
// TODO: remove it when agentTamperProtectionEnabled FF is removed
describe.skip('Agent Tamper Protection feature flag', () => {
let config: { enableExperimental: string[] };
let fakeRouter: jest.Mocked<VersionedRouter<FleetRequestHandlerContext>>;
let fleetAuthzRouter: FleetAuthzRouter;
beforeEach(() => {
fakeRouter = {
versioned: {
get: jest.fn().mockImplementation(() => {
return {
addVersion: jest
.fn()
.mockImplementation((options: any, handler: RequestHandler) => Promise.resolve()),
};
}),
},
} as unknown as jest.Mocked<VersionedRouter<FleetRequestHandlerContext>>;
const mockLogger = loggingSystemMock.createLogger();
fleetAuthzRouter = makeRouterWithFleetAuthz(fakeRouter as any, mockLogger);
});
it('should register handlers if feature flag is enabled', () => {
config = { enableExperimental: ['agentTamperProtectionEnabled'] };
registerRoutes(fleetAuthzRouter, config);
const wrappedHandler =
// @ts-ignore
fakeRouter.versioned.get.mock.results[0].value.addVersion;
expect(wrappedHandler).toHaveBeenCalled();
});
it('should NOT register handlers if feature flag is disabled', async () => {
config = { enableExperimental: [] };
registerRoutes(fleetAuthzRouter, config);
// @ts-ignore
const mockGet = fakeRouter.versioned.get;
expect(mockGet).not.toHaveBeenCalled();
});
});
});

View file

@ -15,78 +15,73 @@ import {
GetUninstallTokensMetadataRequestSchema,
GetUninstallTokensMetadataResponseSchema,
} from '../../types/rest_spec/uninstall_token';
import { parseExperimentalConfigValue } from '../../../common/experimental_features';
import { genericErrorResponse } from '../schema/errors';
import { getUninstallTokenHandler, getUninstallTokensMetadataHandler } from './handlers';
export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType) => {
const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental);
if (experimentalFeatures.agentTamperProtectionEnabled) {
router.versioned
.get({
path: UNINSTALL_TOKEN_ROUTES.LIST_PATTERN,
security: {
authz: {
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
},
router.versioned
.get({
path: UNINSTALL_TOKEN_ROUTES.LIST_PATTERN,
security: {
authz: {
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
},
summary: 'Get metadata for latest uninstall tokens',
description: 'List the metadata for the latest uninstall tokens per agent policy.',
options: {
tags: ['oas-tag:Fleet uninstall tokens'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: GetUninstallTokensMetadataRequestSchema,
response: {
200: {
body: () => GetUninstallTokensMetadataResponseSchema,
},
400: {
body: genericErrorResponse,
},
},
summary: 'Get metadata for latest uninstall tokens',
description: 'List the metadata for the latest uninstall tokens per agent policy.',
options: {
tags: ['oas-tag:Fleet uninstall tokens'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: GetUninstallTokensMetadataRequestSchema,
response: {
200: {
body: () => GetUninstallTokensMetadataResponseSchema,
},
400: {
body: genericErrorResponse,
},
},
},
getUninstallTokensMetadataHandler
);
},
getUninstallTokensMetadataHandler
);
router.versioned
.get({
path: UNINSTALL_TOKEN_ROUTES.INFO_PATTERN,
security: {
authz: {
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
},
router.versioned
.get({
path: UNINSTALL_TOKEN_ROUTES.INFO_PATTERN,
security: {
authz: {
requiredPrivileges: [FLEET_API_PRIVILEGES.AGENTS.ALL],
},
summary: 'Get a decrypted uninstall token',
description: 'Get one decrypted uninstall token by its ID.',
options: {
tags: ['oas-tag:Fleet uninstall tokens'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: GetUninstallTokenRequestSchema,
response: {
200: {
body: () => GetUninstallTokenResponseSchema,
},
400: {
body: genericErrorResponse,
},
},
summary: 'Get a decrypted uninstall token',
description: 'Get one decrypted uninstall token by its ID.',
options: {
tags: ['oas-tag:Fleet uninstall tokens'],
},
})
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {
request: GetUninstallTokenRequestSchema,
response: {
200: {
body: () => GetUninstallTokenResponseSchema,
},
400: {
body: genericErrorResponse,
},
},
},
getUninstallTokenHandler
);
}
},
getUninstallTokenHandler
);
};

View file

@ -9,8 +9,6 @@ import { get } from 'lodash';
import * as esKuery from '@kbn/es-query';
import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal';
import { appContextService } from '../../services/app_context';
type KueryNode = any;
const astFunctionType = ['is', 'range', 'nested'];
@ -228,12 +226,7 @@ export const validateKuery = (
) => {
let isValid = true;
let error: string | undefined;
const { enableStrictKQLValidation } = appContextService.getExperimentalFeatures();
// Skip validation when enableStrictKQLValidation is disabled
if (!enableStrictKQLValidation) {
return { isValid, error };
}
if (!kuery) {
isValid = true;
}

View file

@ -19,12 +19,9 @@ import {
import { FLEET_ENROLLMENT_API_PREFIX } from '../../../common/constants';
import { appContextService } from '../../services/app_context';
import { validateFilterKueryNode, validateKuery } from './filter_utils';
jest.mock('../../services/app_context');
const mockedAppContextService = appContextService as jest.Mocked<typeof appContextService>;
describe('ValidateFilterKueryNode validates real kueries through KueryNode', () => {
describe('Agent policies', () => {
@ -512,14 +509,6 @@ describe('ValidateFilterKueryNode validates real kueries through KueryNode', ()
});
describe('validateKuery validates real kueries', () => {
beforeEach(() => {
mockedAppContextService.getExperimentalFeatures.mockReturnValue({
enableStrictKQLValidation: true,
} as any);
});
afterEach(() => {
mockedAppContextService.getExperimentalFeatures.mockReset();
});
describe('Agent policies', () => {
it('Search by data_output_id', async () => {
const validationObj = validateKuery(
@ -834,34 +823,4 @@ describe('validateKuery validates real kueries', () => {
expect(validationObj?.error).toEqual(undefined);
});
});
describe('Feature flag enableStrictKQLValidation', () => {
beforeEach(() => {
mockedAppContextService.getExperimentalFeatures.mockReturnValue({
enableStrictKQLValidation: false,
} as any);
});
it('Allows to skip validation for a free text query', async () => {
const validationObj = validateKuery(`test`, [AGENTS_PREFIX], AGENT_MAPPINGS, true);
expect(validationObj?.isValid).toEqual(true);
expect(validationObj?.error).toEqual(undefined);
});
it('Allows to skip validation for a catch all query', async () => {
const validationObj = validateKuery(`*`, [AGENTS_PREFIX], AGENT_MAPPINGS, true);
expect(validationObj?.isValid).toEqual(true);
expect(validationObj?.error).toEqual(undefined);
});
it('Allows to skip validation for a disallowed query too', async () => {
const validationObj = validateKuery(
`non_existent_parameter: 'test_id'`,
[AGENTS_PREFIX],
AGENT_MAPPINGS,
true
);
expect(validationObj?.isValid).toEqual(true);
expect(validationObj?.error).toEqual(undefined);
});
});
});

View file

@ -46,6 +46,7 @@ import { auditLoggingService } from './audit_logging';
import { licenseService } from './license';
import type { UninstallTokenServiceInterface } from './security/uninstall_token_service';
import { isSpaceAwarenessEnabled } from './spaces/helpers';
import { scheduleDeployAgentPoliciesTask } from './agent_policies/deploy_agent_policies_task';
jest.mock('./spaces/helpers');
@ -121,6 +122,7 @@ jest.mock('./app_context');
jest.mock('./audit_logging');
jest.mock('./agent_policies/full_agent_policy');
jest.mock('./agent_policies/outputs_helpers');
jest.mock('./agent_policies/deploy_agent_policies_task');
const mockedAppContextService = appContextService as jest.Mocked<typeof appContextService>;
mockedAppContextService.getSecuritySetup.mockImplementation(() => ({
@ -591,241 +593,130 @@ describe('Agent policy', () => {
let soClient: ReturnType<typeof savedObjectsClientMock.create>;
let esClient: ReturnType<typeof elasticsearchServiceMock.createClusterClient>['asInternalUser'];
describe('with enableReusableIntegrationPolicies disabled', () => {
beforeEach(() => {
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
mockedPackagePolicyService.create.mockReset();
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
beforeEach(() => {
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
},
] as any);
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
(getAgentsByKuery as jest.Mock).mockResolvedValue({
agents: [],
total: 0,
page: 1,
perPage: 10,
});
mockedPackagePolicyService.delete.mockResolvedValue([
{
id: 'package-1',
} as any,
]);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: false } as any);
(getAgentsByKuery as jest.Mock).mockResolvedValue({
agents: [],
total: 0,
page: 1,
perPage: 10,
});
mockedPackagePolicyService.create.mockReset();
});
it('should throw error for agent policy which has managed package policy', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
is_managed: true,
},
] as any);
try {
await agentPolicyService.delete(soClient, esClient, 'mocked');
} catch (e) {
expect(e.message).toEqual(
new PackagePolicyRestrictionRelatedError(
`Cannot delete agent policy mocked that contains managed package policies`
).message
);
}
});
it('should allow delete with force for agent policy which has managed package policy', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
is_managed: true,
},
] as any);
const response = await agentPolicyService.delete(soClient, esClient, 'mocked', {
force: true,
});
expect(response.id).toEqual('mocked');
});
it('should call audit logger', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
},
] as any);
it('should throw error for agent policy which has managed package policy', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
is_managed: true,
},
] as any);
try {
await agentPolicyService.delete(soClient, esClient, 'mocked');
expect(mockedAuditLoggingService.writeCustomSoAuditLog).toHaveBeenCalledWith({
action: 'delete',
id: 'mocked',
savedObjectType: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
});
});
it('should throw error if active agents are assigned to the policy', async () => {
(getAgentsByKuery as jest.Mock).mockResolvedValue({
agents: [],
total: 2,
page: 1,
perPage: 10,
});
await expect(agentPolicyService.delete(soClient, esClient, 'mocked')).rejects.toThrowError(
'Cannot delete an agent policy that is assigned to any active or inactive agents'
} catch (e) {
expect(e.message).toEqual(
new PackagePolicyRestrictionRelatedError(
`Cannot delete agent policy mocked that contains managed package policies`
).message
);
}
});
it('should allow delete with force for agent policy which has managed package policy', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
is_managed: true,
},
] as any);
const response = await agentPolicyService.delete(soClient, esClient, 'mocked', {
force: true,
});
expect(response.id).toEqual('mocked');
});
it('should delete .fleet-policies entries on agent policy delete', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
},
] as any);
esClient.deleteByQuery.mockResolvedValueOnce({
deleted: 2,
});
it('should call audit logger', async () => {
await agentPolicyService.delete(soClient, esClient, 'mocked');
await agentPolicyService.delete(soClient, esClient, 'mocked');
expect(esClient.deleteByQuery).toHaveBeenCalledWith(
expect.objectContaining({
index: AGENT_POLICY_INDEX,
query: {
term: {
policy_id: 'mocked',
},
},
})
);
expect(mockedAuditLoggingService.writeCustomSoAuditLog).toHaveBeenCalledWith({
action: 'delete',
id: 'mocked',
savedObjectType: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
});
});
describe('with enableReusableIntegrationPolicies enabled', () => {
beforeEach(() => {
soClient = getSavedObjectMock({ revision: 1, package_policies: ['package-1'] });
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
},
] as any);
esClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
it('should throw error if active agents are assigned to the policy', async () => {
(getAgentsByKuery as jest.Mock).mockResolvedValue({
agents: [],
total: 2,
page: 1,
perPage: 10,
});
await expect(agentPolicyService.delete(soClient, esClient, 'mocked')).rejects.toThrowError(
'Cannot delete an agent policy that is assigned to any active or inactive agents'
);
});
(getAgentsByKuery as jest.Mock).mockResolvedValue({
agents: [],
total: 0,
page: 1,
perPage: 10,
});
mockedPackagePolicyService.create.mockReset();
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
it('should delete .fleet-policies entries on agent policy delete', async () => {
esClient.deleteByQuery.mockResolvedValueOnce({
deleted: 2,
});
it('should throw error for agent policy which has managed package policy', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
is_managed: true,
},
] as any);
try {
await agentPolicyService.delete(soClient, esClient, 'mocked');
} catch (e) {
expect(e.message).toEqual(
new PackagePolicyRestrictionRelatedError(
`Cannot delete agent policy mocked that contains managed package policies`
).message
);
}
});
await agentPolicyService.delete(soClient, esClient, 'mocked');
it('should allow delete with force for agent policy which has managed package policy', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
is_managed: true,
},
] as any);
const response = await agentPolicyService.delete(soClient, esClient, 'mocked', {
force: true,
});
expect(response.id).toEqual('mocked');
});
it('should call audit logger', async () => {
await agentPolicyService.delete(soClient, esClient, 'mocked');
expect(mockedAuditLoggingService.writeCustomSoAuditLog).toHaveBeenCalledWith({
action: 'delete',
id: 'mocked',
savedObjectType: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
});
});
it('should throw error if active agents are assigned to the policy', async () => {
(getAgentsByKuery as jest.Mock).mockResolvedValue({
agents: [],
total: 2,
page: 1,
perPage: 10,
});
await expect(agentPolicyService.delete(soClient, esClient, 'mocked')).rejects.toThrowError(
'Cannot delete an agent policy that is assigned to any active or inactive agents'
);
});
it('should delete .fleet-policies entries on agent policy delete', async () => {
esClient.deleteByQuery.mockResolvedValueOnce({
deleted: 2,
});
await agentPolicyService.delete(soClient, esClient, 'mocked');
expect(esClient.deleteByQuery).toHaveBeenCalledWith(
expect.objectContaining({
index: AGENT_POLICY_INDEX,
query: {
term: {
policy_id: 'mocked',
},
expect(esClient.deleteByQuery).toHaveBeenCalledWith(
expect.objectContaining({
index: AGENT_POLICY_INDEX,
query: {
term: {
policy_id: 'mocked',
},
})
);
});
},
})
);
});
it('should only delete package polices that are not shared with other agent policies', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
it('should only delete package polices that are not shared with other agent policies', async () => {
mockedPackagePolicyService.findAllForAgentPolicy.mockReturnValue([
{
id: 'package-1',
policy_id: ['policy_1'],
policy_ids: ['policy_1', 'int_policy_2'],
},
{
id: 'package-2',
policy_id: ['policy_1'],
policy_ids: ['policy_1'],
},
{
id: 'package-3',
},
] as any);
await agentPolicyService.delete(soClient, esClient, 'policy_1');
expect(mockedPackagePolicyService.delete).toBeCalledWith(
expect.anything(),
expect.anything(),
['package-2', 'package-3'],
expect.anything()
);
expect(mockedPackagePolicyService.bulkUpdate).toBeCalledWith(
expect.anything(),
expect.anything(),
[
{
id: 'package-1',
policy_id: ['policy_1'],
policy_ids: ['policy_1', 'int_policy_2'],
policy_id: 'int_policy_2',
policy_ids: ['int_policy_2'],
},
{
id: 'package-2',
policy_id: ['policy_1'],
policy_ids: ['policy_1'],
},
{
id: 'package-3',
},
] as any);
await agentPolicyService.delete(soClient, esClient, 'policy_1');
expect(mockedPackagePolicyService.delete).toBeCalledWith(
expect.anything(),
expect.anything(),
['package-2', 'package-3'],
expect.anything()
);
expect(mockedPackagePolicyService.bulkUpdate).toBeCalledWith(
expect.anything(),
expect.anything(),
[
{
id: 'package-1',
policy_id: 'int_policy_2',
policy_ids: ['int_policy_2'],
},
]
);
});
]
);
});
});
@ -844,7 +735,7 @@ describe('Agent policy', () => {
});
describe('bumpAllAgentPolicies', () => {
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
it('should call scheduleDeployAgentPoliciesTask with updated event once', async () => {
const soClient = getSavedObjectMock({
revision: 1,
monitoring_enabled: ['metrics'],
@ -872,12 +763,12 @@ describe('Agent policy', () => {
})
);
expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1);
expect(scheduleDeployAgentPoliciesTask).toHaveBeenCalledTimes(1);
});
});
describe('bumpAllAgentPoliciesForOutput', () => {
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
it('should call scheduleDeployAgentPoliciesTask with updated event once', async () => {
const soClient = getSavedObjectMock({
revision: 1,
monitoring_enabled: ['metrics'],
@ -888,7 +779,7 @@ describe('Agent policy', () => {
);
await agentPolicyService.bumpAllAgentPoliciesForOutput(esClient, 'output-id-123');
expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1);
expect(scheduleDeployAgentPoliciesTask).toHaveBeenCalledTimes(1);
});
});
@ -1030,7 +921,7 @@ describe('Agent policy', () => {
});
describe('bumpAllAgentPoliciesForDownloadSource', () => {
it('should call agentPolicyUpdateEventHandler with updated event once', async () => {
it('should call scheduleDeployAgentPoliciesTask with updated event once', async () => {
const soClient = getSavedObjectMock({
revision: 1,
monitoring_enabled: ['metrics'],
@ -1041,7 +932,7 @@ describe('Agent policy', () => {
);
await agentPolicyService.bumpAllAgentPoliciesForDownloadSource(esClient, 'test-id-1');
expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1);
expect(scheduleDeployAgentPoliciesTask).toHaveBeenCalledTimes(1);
});
});

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { chunk, groupBy, isEqual, keyBy, omit, pick, uniq } from 'lodash';
import { groupBy, isEqual, keyBy, omit, pick, uniq } from 'lodash';
import { v5 as uuidv5 } from 'uuid';
import { dump } from 'js-yaml';
import pMap from 'p-map';
@ -26,8 +26,6 @@ import type { BulkResponseItem } from '@elastic/elasticsearch/lib/api/types';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants';
import { asyncForEach } from '@kbn/std';
import type { SavedObjectError } from '@kbn/core-saved-objects-common';
import { withSpan } from '@kbn/apm-utils';
@ -1073,24 +1071,13 @@ class AgentPolicyService {
}
);
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
await scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
savedObjectsResults.map((policy) => ({
id: policy.id,
spaceId: policy.namespaces?.[0],
}))
);
} else {
await pMap(
savedObjectsResults,
(policy) =>
this.triggerAgentPolicyUpdatedEvent(esClient, 'updated', policy.id, {
spaceId: policy.namespaces?.[0],
}),
{ concurrency: MAX_CONCURRENT_AGENT_POLICIES_OPERATIONS }
);
}
await scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
savedObjectsResults.map((policy) => ({
id: policy.id,
spaceId: policy.namespaces?.[0],
}))
);
return res;
}
@ -1750,24 +1737,13 @@ class AgentPolicyService {
const updatedPoliciesSuccess = updatedAgentPolicies.filter((policy) => !policy.error);
const config = appContextService.getConfig();
const batchSize = config?.setup?.agentPolicySchemaUpgradeBatchSize ?? 100;
const policyIds = updatedPoliciesSuccess.map((policy) => policy.id);
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
await scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
updatedPoliciesSuccess.map((policy) => ({
id: policy.id,
spaceId: policy.namespaces?.[0],
}))
);
} else {
await asyncForEach(
chunk(policyIds, batchSize),
async (policyIdsBatch) => await this.deployPolicies(soClient, policyIdsBatch)
);
}
await scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
updatedPoliciesSuccess.map((policy) => ({
id: policy.id,
spaceId: policy.namespaces?.[0],
}))
);
return { updatedPolicies: updatedPoliciesSuccess, failedPolicies };
}

View file

@ -364,11 +364,11 @@ export async function getPackage(
verificationResult?: PackageVerificationResult;
}> {
const logger = appContextService.getLogger();
const verifyPackage = appContextService.getExperimentalFeatures().packageVerification;
let packageInfo: ArchivePackage | undefined = getPackageInfo({ name, version });
let verificationResult: PackageVerificationResult | undefined = verifyPackage
? getVerificationResult({ name, version })
: undefined;
let verificationResult: PackageVerificationResult | undefined = getVerificationResult({
name,
version,
});
try {
const {
archiveBuffer,
@ -378,7 +378,7 @@ export async function getPackage(
fetchArchiveBuffer({
pkgName: name,
pkgVersion: version,
shouldVerify: verifyPackage,
shouldVerify: true,
ignoreUnverified: options?.ignoreUnverified,
})
);

View file

@ -26,6 +26,7 @@ jest.mock('./app_context');
jest.mock('./agent_policy');
jest.mock('./package_policy');
jest.mock('./audit_logging');
jest.mock('./secrets');
const mockedAuditLoggingService = auditLoggingService as jest.Mocked<typeof auditLoggingService>;
const mockedAppContextService = appContextService as jest.Mocked<typeof appContextService>;

View file

@ -75,9 +75,6 @@ describe('Package Policy Utils', () => {
it('should throw if no enterprise license and multiple policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
await expect(
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: ['1', '2'] })
@ -88,9 +85,6 @@ describe('Package Policy Utils', () => {
it('should throw if no enterprise license and no policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
await expect(
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: [] })
@ -99,22 +93,8 @@ describe('Package Policy Utils', () => {
);
});
it('should throw if enterprise license and multiple policy_ids is provided but no feature flag', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: false } as any);
await expect(
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: ['1', '2'] })
).rejects.toThrowError('Reusable integration policies are not supported');
});
it('should not throw if enterprise license and multiple policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
await expect(
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: ['1', '2'] })
).resolves.not.toThrow();
@ -122,9 +102,6 @@ describe('Package Policy Utils', () => {
it('should not throw if enterprise license and no policy_ids is provided', async () => {
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
jest
.spyOn(appContextService, 'getExperimentalFeatures')
.mockReturnValue({ enableReusableIntegrationPolicies: true } as any);
await expect(
preflightCheckPackagePolicy(soClient, { ...testPolicy, policy_ids: [] })
).resolves.not.toThrow();

View file

@ -26,7 +26,6 @@ import {
} from '../../errors';
import { licenseService } from '../license';
import { outputService } from '../output';
import { appContextService } from '../app_context';
export const mapPackagePolicySavedObjectToPackagePolicy = (
{ id, version, attributes }: SavedObject<PackagePolicySOAttributes>,
@ -75,10 +74,9 @@ export async function preflightCheckPackagePolicy(
export function canUseMultipleAgentPolicies() {
const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES);
const { enableReusableIntegrationPolicies } = appContextService.getExperimentalFeatures();
return {
canUseReusablePolicies: hasEnterpriseLicence && enableReusableIntegrationPolicies,
canUseReusablePolicies: hasEnterpriseLicence,
errorMessage: !hasEnterpriseLicence
? 'Reusable integration policies are only available with an Enterprise license'
: 'Reusable integration policies are not supported',

View file

@ -208,6 +208,10 @@ jest.mock('./upgrade_sender', () => {
jest.mock('./audit_logging');
const mockedAuditLoggingService = auditLoggingService as jest.Mocked<typeof auditLoggingService>;
jest.mock('./secrets', () => ({
isSecretStorageEnabled: jest.fn(),
}));
type CombinedExternalCallback = PutPackagePolicyUpdateCallback | PostPackagePolicyCreateCallback;
const mockAgentPolicyGet = (spaceIds: string[] = ['default']) => {

View file

@ -44,6 +44,7 @@ jest.mock('../app_context', () => ({
},
}
),
getTaskManagerStart: jest.fn(),
},
}));

View file

@ -604,13 +604,6 @@ export async function isSecretStorageEnabled(
): Promise<boolean> {
const logger = appContextService.getLogger();
// first check if the feature flag is enabled, if not secrets are disabled
const { secretsStorage: secretsStorageEnabled } = appContextService.getExperimentalFeatures();
if (!secretsStorageEnabled) {
logger.debug('Secrets storage is disabled by feature flag');
return false;
}
// if serverless then secrets will always be supported
const isFleetServerStandalone =
appContextService.getConfig()?.internal?.fleetServerStandalone ?? false;
@ -660,14 +653,6 @@ export async function isOutputSecretStorageEnabled(
): Promise<boolean> {
const logger = appContextService.getLogger();
// first check if the feature flag is enabled, if not output secrets are disabled
const { outputSecretsStorage: outputSecretsStorageEnabled } =
appContextService.getExperimentalFeatures();
if (!outputSecretsStorageEnabled) {
logger.debug('Output secrets storage is disabled by feature flag');
return false;
}
// if serverless then output secrets will always be supported
const isFleetServerStandalone =
appContextService.getConfig()?.internal?.fleetServerStandalone ?? false;

View file

@ -584,9 +584,6 @@ describe('getAuthzFromRequest', () => {
jest.mocked(appContextService.getSecurityLicense).mockReturnValue({
isEnabled: () => true,
} as any);
jest.mocked(appContextService.getExperimentalFeatures).mockReturnValue({
subfeaturePrivileges: true,
} as any);
});
it('should not authorize access if RBAC is not enabled', async () => {
mockSecurity.authz.mode.useRbacForRequest.mockReturnValue(false);

View file

@ -78,8 +78,6 @@ const FLEET_SUBFEATURES = ['agents', 'agent-policies', 'settings'];
export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuthz> {
const security = appContextService.getSecurity();
const isSubfeatureEnabled =
appContextService.getExperimentalFeatures()?.subfeaturePrivileges ?? false;
if (security.authz.mode.useRbacForRequest(req)) {
const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(req);
@ -180,7 +178,7 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuth
all: intAllAuth,
read: intReadAuth,
},
subfeatureEnabled: isSubfeatureEnabled,
subfeatureEnabled: true,
}),
packagePrivileges: calculatePackagePrivilegesFromKibanaPrivileges(privileges.kibana),
endpointExceptionsPrivileges: calculateEndpointExceptionsPrivilegesFromKibanaPrivileges(
@ -195,7 +193,7 @@ export async function getAuthzFromRequest(req: KibanaRequest): Promise<FleetAuth
all: false,
read: false,
},
subfeatureEnabled: isSubfeatureEnabled,
subfeatureEnabled: true,
});
}

View file

@ -769,31 +769,6 @@ describe('UninstallTokenService', () => {
]);
});
});
describe('agentTamperProtectionEnabled false', () => {
beforeAll(() => {
// @ts-ignore
mockContext.experimentalFeatures.agentTamperProtectionEnabled = false;
});
it('generateTokensForPolicyIds should not generate token if agentTamperProtectionEnabled: false', async () => {
const so = getDefaultSO();
await uninstallTokenService.generateTokensForPolicyIds([so.attributes.policy_id]);
expect(soClientMock.bulkCreate).not.toBeCalled();
});
it('generateTokensForAllPolicies should not generate token if agentTamperProtectionEnabled: false', async () => {
mockAgentPolicyFetchAllAgentPolicyIds(canEncrypt);
await uninstallTokenService.generateTokensForAllPolicies();
expect(soClientMock.bulkCreate).not.toBeCalled();
});
it('generateTokenForPolicyId should not generate token if agentTamperProtectionEnabled: false', async () => {
const so = getDefaultSO();
await uninstallTokenService.generateTokenForPolicyId(so.attributes.policy_id);
expect(soClientMock.bulkCreate).not.toBeCalled();
});
});
});
describe('check validity of tokens', () => {

View file

@ -520,9 +520,7 @@ export class UninstallTokenService implements UninstallTokenServiceInterface {
policyIds: string[],
force: boolean = false
): Promise<void> {
const { agentTamperProtectionEnabled } = appContextService.getExperimentalFeatures();
if (!agentTamperProtectionEnabled || !policyIds.length) {
if (!policyIds.length) {
return;
}

View file

@ -12,12 +12,15 @@ import { appContextService } from '../app_context';
import { agentPolicyService } from '../agent_policy';
import { ensureDefaultEnrollmentAPIKeyForAgentPolicy } from '../api_keys';
import { scheduleDeployAgentPoliciesTask } from '../agent_policies/deploy_agent_policies_task';
import { ensureAgentPoliciesFleetServerKeysAndPolicies } from './fleet_server_policies_enrollment_keys';
jest.mock('../app_context');
jest.mock('../agent_policy');
jest.mock('../api_keys');
jest.mock('../agent_policies/bump_agent_policies_task');
jest.mock('../agent_policies/deploy_agent_policies_task');
const mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy = jest.mocked(
ensureDefaultEnrollmentAPIKeyForAgentPolicy
@ -30,9 +33,6 @@ describe('ensureAgentPoliciesFleetServerKeysAndPolicies', () => {
jest.mocked(appContextService).getSecurity.mockReturnValue({
authc: { apiKeys: { areAPIKeysEnabled: async () => true } },
} as any);
jest.mocked(appContextService).getExperimentalFeatures.mockReturnValue({
asyncDeployPolicies: false,
} as any);
mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy.mockReset();
mockedAgentPolicyService.getLatestFleetPolicy.mockReset();
@ -108,7 +108,9 @@ describe('ensureAgentPoliciesFleetServerKeysAndPolicies', () => {
});
expect(mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy).toBeCalledTimes(2);
expect(mockedAgentPolicyService.deployPolicies).toBeCalledWith(expect.anything(), ['policy2']);
expect(scheduleDeployAgentPoliciesTask).toBeCalledWith(undefined, [
{ id: 'policy2', spaceId: undefined },
]);
});
it('should do deploy policies never deployed', async () => {
@ -132,36 +134,8 @@ describe('ensureAgentPoliciesFleetServerKeysAndPolicies', () => {
});
expect(mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy).toBeCalledTimes(2);
expect(mockedAgentPolicyService.deployPolicies).toBeCalledWith(expect.anything(), ['policy2']);
});
it('handle errors when deploying policies', async () => {
const logger = loggingSystemMock.createLogger();
const esClient = elasticsearchServiceMock.createInternalClient();
const soClient = savedObjectsClientMock.create();
mockedAgentPolicyService.getLatestFleetPolicy.mockImplementation(async (_, agentPolicyId) => {
if (agentPolicyId === 'policy1') {
return {
revision_idx: 1,
} as any;
}
return null;
});
mockedAgentPolicyService.deployPolicies.mockRejectedValue(new Error('test rejection'));
await ensureAgentPoliciesFleetServerKeysAndPolicies({
logger,
esClient,
soClient,
});
expect(mockedEnsureDefaultEnrollmentAPIKeyForAgentPolicy).toBeCalledTimes(2);
expect(mockedAgentPolicyService.deployPolicies).toBeCalledWith(expect.anything(), ['policy2']);
expect(logger.warn).toBeCalledWith(
'Error deploying policies: test rejection',
expect.anything()
);
expect(scheduleDeployAgentPoliciesTask).toBeCalledWith(undefined, [
{ id: 'policy2', spaceId: undefined },
]);
});
});

View file

@ -58,20 +58,9 @@ export async function ensureAgentPoliciesFleetServerKeysAndPolicies({
await scheduleBumpAgentPoliciesTask(appContextService.getTaskManagerStart()!);
if (outdatedAgentPolicyIds.length) {
if (appContextService.getExperimentalFeatures().asyncDeployPolicies) {
return scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
outdatedAgentPolicyIds
);
} else {
return agentPolicyService
.deployPolicies(
soClient,
outdatedAgentPolicyIds.map(({ id }) => id)
)
.catch((error) => {
logger.warn(`Error deploying policies: ${error.message}`, { error });
});
}
return scheduleDeployAgentPoliciesTask(
appContextService.getTaskManagerStart()!,
outdatedAgentPolicyIds
);
}
}