[SecuritySolution] Register AI Assistant management settings according to productFeatureKeys (#213105)

## Summary

Fixes: https://github.com/elastic/kibana/issues/212667

AI Assistant management is registered according to `productFeatureKeys`
set by security_solution_ess or security_solution_serverless plugin.

To verify:

- Security project

```
yarn es serverless --projectType security --kill --clean -E  —ssl xpack.security.authc.api_key.enabled=true
yarn serverless-security --no-base-path
```

Update xpack.securitySolutionServerless.productTypes in
config/serverless.security.yml to switch between `essentials` and
`complete` tier

Project essentials:

<img width="2140" alt="Screenshot 2025-03-04 at 17 05 59"
src="https://github.com/user-attachments/assets/c2df5d20-a0f8-4ee4-82d3-b04a47a47f53"
/>

Project complete:

<img width="2199" alt="Screenshot 2025-03-04 at 17 07 16"
src="https://github.com/user-attachments/assets/0e6f0c5d-f318-4db8-925b-01e46ed76a7b"
/>


- ESS 

License trial and enterprise:

```
yarn es snapshot --license trial -E xpack.security.authc.api_key.enabled=true -E discovery.type=single-node -E network.host=0.0.0.0

```



https://github.com/user-attachments/assets/1771f51b-7b48-46e5-8bf6-3918c6e913c5


License basic and others:

```
yarn es snapshot --license basic -E xpack.security.authc.api_key.enabled=true -E discovery.type=single-node -E network.host=0.0.0.0

```



https://github.com/user-attachments/assets/fe00454c-10f6-4862-8eca-03179431ea6c

---------

Co-authored-by: Sergi Massaneda <sergi.massaneda@gmail.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Angela Chuang 2025-03-06 15:46:09 +00:00 committed by GitHub
parent d2412a5f98
commit 56ea5cced3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 44 additions and 4 deletions

View file

@ -24,7 +24,8 @@ export const contractStartServicesMock: ContractStartServices = {
const setupMock = (): PluginSetup => ({
resolver: jest.fn(),
experimentalFeatures: allowedExperimentalValues, // default values
experimentalFeatures: allowedExperimentalValues, // default values,
setProductFeatureKeys: jest.fn(),
});
const startMock = (): PluginStart => ({

View file

@ -24,6 +24,7 @@ import type {
SecuritySolutionAppWrapperFeature,
SecuritySolutionCellRendererFeature,
} from '@kbn/discover-shared-plugin/public/services/discover_features';
import { ProductFeatureAssistantKey } from '@kbn/security-solution-features/src/product_features_keys';
import { getLazyCloudSecurityPosturePliAuthBlockExtension } from './cloud_security_posture/lazy_cloud_security_posture_pli_auth_block_extension';
import { getLazyEndpointAgentTamperProtectionExtension } from './management/pages/policy/view/ingest_manager_integration/lazy_endpoint_agent_tamper_protection_extension';
import type {
@ -99,6 +100,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
this.services.setup(core, plugins);
const { home, usageCollection, management, cases } = plugins;
const { productFeatureKeys$ } = this.contract;
// Lazily instantiate subPlugins and initialize services
const mountDependencies = async (params?: AppMountParameters) => {
@ -202,6 +204,25 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
},
});
productFeatureKeys$
.pipe(combineLatestWith(plugins.licensing.license$))
.subscribe(([productFeatureKeys, license]) => {
if (!productFeatureKeys || !license) {
return;
}
const isAssistantAvailable =
productFeatureKeys?.has(ProductFeatureAssistantKey.assistant) &&
license?.hasAtLeast('enterprise');
const assistantManagementApp = management?.sections.section.kibana.getApp(
'securityAiAssistantManagement'
);
if (!isAssistantAvailable) {
assistantManagementApp?.disable();
}
});
cases?.attachmentFramework.registerExternalReference(
getExternalReferenceAttachmentEndpointRegular()
);

View file

@ -4,10 +4,13 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { BehaviorSubject } from 'rxjs';
import { UpsellingService } from '@kbn/security-solution-upselling/service';
import type { CoreStart } from '@kbn/core/public';
import type {
ProductFeatureKeyType,
ProductFeatureKeys,
} from '@kbn/security-solution-features/src/types';
import type { ContractStartServices, PluginSetup, PluginStart } from './types';
import type { ExperimentalFeatures } from '../common/experimental_features';
import { navLinks$, updateNavLinks } from './common/links/nav_links';
@ -20,18 +23,23 @@ export class PluginContract {
public upsellingService: UpsellingService;
public onboardingService: OnboardingService;
public isSolutionNavigationEnabled$: BehaviorSubject<boolean>;
public productFeatureKeys$: BehaviorSubject<Set<ProductFeatureKeyType> | null>;
constructor(private readonly experimentalFeatures: ExperimentalFeatures) {
this.onboardingService = new OnboardingService();
this.componentsService = new ContractComponentsService();
this.upsellingService = new UpsellingService();
this.isSolutionNavigationEnabled$ = new BehaviorSubject<boolean>(false); // defaults to classic navigation
this.productFeatureKeys$ = new BehaviorSubject<Set<ProductFeatureKeyType> | null>(null);
}
public getSetupContract(): PluginSetup {
return {
resolver: lazyResolver,
experimentalFeatures: { ...this.experimentalFeatures },
setProductFeatureKeys: (productFeatureKeys: ProductFeatureKeys) => {
this.productFeatureKeys$.next(new Set(productFeatureKeys));
},
};
}

View file

@ -62,6 +62,7 @@ import type { MapsStartApi } from '@kbn/maps-plugin/public';
import type { ServerlessPluginStart } from '@kbn/serverless/public';
import type { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public';
import type { AutomaticImportPluginStart } from '@kbn/automatic-import-plugin/public';
import type { ProductFeatureKeys } from '@kbn/security-solution-features';
import type { ResolverPluginSetup } from './resolver/types';
import type { Inspect } from '../common/search_strategy';
import type { Detections } from './detections';
@ -213,6 +214,7 @@ export type StartRenderServices = Pick<
export interface PluginSetup {
resolver: () => Promise<ResolverPluginSetup>;
experimentalFeatures: ExperimentalFeatures;
setProductFeatureKeys: (productFeatureKeys: ProductFeatureKeys) => void;
}
export interface PluginStart {

View file

@ -16,6 +16,7 @@ import type {
SecuritySolutionEssPluginStartDeps,
} from './types';
import { setOnboardingSettings } from './onboarding';
import { DEFAULT_PRODUCT_FEATURES } from '../common/constants';
export class SecuritySolutionEssPlugin
implements
@ -28,8 +29,12 @@ export class SecuritySolutionEssPlugin
{
public setup(
_core: CoreSetup,
_setupDeps: SecuritySolutionEssPluginSetupDeps
setupDeps: SecuritySolutionEssPluginSetupDeps
): SecuritySolutionEssPluginSetup {
const { securitySolution } = setupDeps;
securitySolution.setProductFeatureKeys(DEFAULT_PRODUCT_FEATURES);
return {};
}

View file

@ -7,7 +7,7 @@
import type { Plugin, CoreSetup } from '@kbn/core/server';
import { getProductProductFeaturesConfigurator } from './product_features';
import { DEFAULT_PRODUCT_FEATURES } from './constants';
import { DEFAULT_PRODUCT_FEATURES } from '../common/constants';
import type {
SecuritySolutionEssPluginSetup,

View file

@ -24,6 +24,7 @@ import {
} from '../common/experimental_features';
import { setOnboardingSettings } from './onboarding';
import { getAdditionalChargesMessage } from './components/additional_charges_message';
import { getProductProductFeatures } from '../common/pli/pli_features';
export class SecuritySolutionServerlessPlugin
implements
@ -47,12 +48,14 @@ export class SecuritySolutionServerlessPlugin
setupDeps: SecuritySolutionServerlessPluginSetupDeps
): SecuritySolutionServerlessPluginSetup {
const { securitySolution } = setupDeps;
const { productTypes } = this.config;
this.experimentalFeatures = parseExperimentalConfigValue(
this.config.enableExperimental,
securitySolution.experimentalFeatures
).features;
securitySolution.setProductFeatureKeys(getProductProductFeatures(productTypes));
return {};
}