[Security Solution] Adds RBAC for Assistant (#163031)

## Summary

Adds `All`/`None` RBAC for the Elastic AI Assistant within the Security
app via Kibana feature privileges, and also via serverless PLI App
Features for the Security `complete` product (see
https://github.com/elastic/security-team/issues/7023).

Added as high-level category to enable future support of sub-features
(included sample `Create Conversation` sub-feature plumbed as example).

<p align="center">
<img width="500"
src="b42ab3fe-65e1-49b9-a379-262f2438c0eb"
/>
</p> 

Note: Since [`minimumLicense:
'enterprise'`](https://github.com/elastic/kibana/pull/163031/files#diff-56de8b6234daf4e0e69efe680e5a4afc4f88d152243b773d90c3991fa9dabc19R28)
is configured on this privilege, when the license check isn't satisfied,
the privilege will be hidden (not disabled).

## Testing

Feature is available for `enterprise` licenses and when `All` privilege
is set, otherwise Assistant is hidden in Timeline, all `Chat` UI
elements are hidden, and the `cmd/ctrl + ;` shortcut is unavailable.

---

### On Prem Testing:
Create two roles, one for each `all`/`none` Security Elastic AI
Assistant privilege (via Dev Tools):

<details><summary>PUT /_security/role/assistant_all</summary>
<p>

``` ts
PUT /_security/role/assistant_all
{
  "cluster": [
    "all"
  ],
  "indices": [
    {
      "names": [
        "*"
      ],
      "privileges": [
        "all"
      ],
      "field_security": {
        "grant": [
          "*"
        ],
        "except": []
      },
      "allow_restricted_indices": false
    }
  ],
  "applications": [
    {
      "application": "kibana-.kibana",
      "privileges": [
        "feature_securitySolutionAssistant.minimal_all",
        "feature_siem.all",
        "feature_securitySolutionCases.all",
        "feature_actions.all"
      ],
      "resources": [
        "*"
      ]
    }
  ],
  "run_as": [],
  "metadata": {},
  "transient_metadata": {
    "enabled": true
  }
}
```
</p>
</details>


<details><summary>PUT /_security/role/assistant_none</summary>
<p>

``` ts
PUT /_security/role/assistant_none
{
  "cluster": [
    "all"
  ],
  "indices": [
    {
      "names": [
        "*"
      ],
      "privileges": [
        "all"
      ],
      "field_security": {
        "grant": [
          "*"
        ],
        "except": []
      },
      "allow_restricted_indices": false
    }
  ],
  "applications": [
    {
      "application": "kibana-.kibana",
      "privileges": [
        "feature_siem.all",
        "feature_securitySolutionCases.all",
        "feature_actions.all"
      ],
      "resources": [
        "*"
      ]
    }
  ],
  "run_as": [],
  "metadata": {},
  "transient_metadata": {
    "enabled": true
  }
}
```
</p>
</details>

Create a new `assistant_user` (assigned both roles above), log in and
test assistant availability, then remove one role at a time testing
each:

<details><summary>POST /_security/user/assistant_user (ALL)</summary>
<p>

``` ts
POST /_security/user/assistant_user
{
    "username": "assistant_user",
    "password": "changeme",
    "roles": [
      "assistant_all",
      "assistant_none"
    ],
    "full_name": "Assistant User",
    "email": "assistant-user@elastic.co",
    "metadata": {},
    "enabled": true
}
```
</p>
</details>

Test that assistant is available in UI via `Chat` buttons and shortcut
keys.

<details><summary>PUT /_security/user/assistant_user (NONE)</summary>
<p>

``` ts
PUT /_security/user/assistant_user
{
    "username": "assistant_user",
    "roles": [
      "assistant_none"
    ],
    "full_name": "Assistant User",
    "email": "assistant-user@elastic.co",
    "metadata": {},
    "enabled": true
}
```
</p>
</details>

Test that assistant is **NOT** available in UI via `Chat` buttons or
shortcut keys.

---

### Serverless Testing:

To test with the Assistant available, set `productTypes` to `complete`
in `config/serverless.security.yml`

```
xpack.securitySolutionServerless.productTypes:
  [
    { product_line: 'security', product_tier: 'complete' },
    { product_line: 'endpoint', product_tier: 'complete' },
  ]
  ```

otherwise to test without the Assistant, pick a different product type like `essentials`:

```
xpack.securitySolutionServerless.productTypes:
  [
    { product_line: 'security', product_tier: 'essentials' },
    { product_line: 'endpoint', product_tier: 'essentials' },
  ]
```

Then start Serverless Kibana: `yarn serverless-security`

---

### Checklist

Delete any items that are not applicable to this PR.

- [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials
- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
Garrett Spong 2023-08-11 15:48:59 -06:00 committed by GitHub
parent 0a74fa03a0
commit 6acf72f25c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 281 additions and 10 deletions

View file

@ -20,6 +20,7 @@ export { SecurityPageName } from '@kbn/security-solution-navigation';
*/
export const APP_ID = 'securitySolution' as const;
export const APP_UI_ID = 'securitySolutionUI' as const;
export const ASSISTANT_FEATURE_ID = 'securitySolutionAssistant' as const;
export const CASES_FEATURE_ID = 'securitySolutionCases' as const;
export const SERVER_APP_ID = 'siem' as const;
export const APP_NAME = 'Security' as const;

View file

@ -55,6 +55,13 @@ export enum AppFeatureSecurityKey {
osqueryAutomatedResponseActions = 'osquery_automated_response_actions',
}
export enum AppFeatureAssistantKey {
/**
* Enables Elastic AI Assistant
*/
assistant = 'assistant',
}
export enum AppFeatureCasesKey {
/**
* Enables Cases Connectors
@ -63,9 +70,13 @@ export enum AppFeatureCasesKey {
}
// Merges the two enums.
export type AppFeatureKey = AppFeatureSecurityKey | AppFeatureCasesKey;
export type AppFeatureKey = AppFeatureSecurityKey | AppFeatureCasesKey | AppFeatureAssistantKey;
export type AppFeatureKeys = AppFeatureKey[];
// We need to merge the value and the type and export both to replicate how enum works.
export const AppFeatureKey = { ...AppFeatureSecurityKey, ...AppFeatureCasesKey };
export const AppFeatureKey = {
...AppFeatureSecurityKey,
...AppFeatureCasesKey,
...AppFeatureAssistantKey,
};
export const ALL_APP_FEATURE_KEYS = Object.freeze(Object.values(AppFeatureKey));

View file

@ -63,6 +63,7 @@ export const secAll: Role = {
{
feature: {
siem: ['all'],
securitySolutionAssistant: ['all'],
securitySolutionCases: ['all'],
actions: ['all'],
actionsSimulators: ['all'],
@ -94,6 +95,7 @@ export const secReadCasesAll: Role = {
{
feature: {
siem: ['read'],
securitySolutionAssistant: ['all'],
securitySolutionCases: ['all'],
actions: ['all'],
actionsSimulators: ['all'],
@ -125,6 +127,7 @@ export const secAllCasesOnlyReadDelete: Role = {
{
feature: {
siem: ['all'],
securitySolutionAssistant: ['all'],
securitySolutionCases: ['cases_read', 'cases_delete'],
actions: ['all'],
actionsSimulators: ['all'],
@ -156,6 +159,7 @@ export const secAllCasesNoDelete: Role = {
{
feature: {
siem: ['all'],
securitySolutionAssistant: ['all'],
securitySolutionCases: ['minimal_all'],
actions: ['all'],
actionsSimulators: ['all'],

View file

@ -6,6 +6,8 @@
*/
import { useLicense } from '../../common/hooks/use_license';
import { useKibana } from '../../common/lib/kibana';
import { ASSISTANT_FEATURE_ID } from '../../../common/constants';
export interface UseAssistantAvailability {
// True when user is Enterprise. When false, the Assistant is disabled and unavailable
@ -16,11 +18,11 @@ export interface UseAssistantAvailability {
export const useAssistantAvailability = (): UseAssistantAvailability => {
const isEnterprise = useLicense().isEnterprise();
const capabilities = useKibana().services.application.capabilities;
const isAssistantEnabled = capabilities[ASSISTANT_FEATURE_ID]?.['ai-assistant'] === true;
return {
isAssistantEnabled: isEnterprise,
// TODO: RBAC check (https://github.com/elastic/security-team/issues/6932)
// Leaving as a placeholder for RBAC as the same behavior will be required
// When false, the Assistant is hidden and unavailable
hasAssistantPrivilege: true,
hasAssistantPrivilege: isAssistantEnabled,
};
};

View file

@ -35,7 +35,7 @@ import {
import type { FieldHook } from '../../shared_imports';
import { SUB_PLUGINS_REDUCER } from './utils';
import { createSecuritySolutionStorageMock, localStorageMock } from './mock_local_storage';
import { CASES_FEATURE_ID } from '../../../common/constants';
import { ASSISTANT_FEATURE_ID, CASES_FEATURE_ID } from '../../../common/constants';
import { UserPrivilegesProvider } from '../components/user_privileges/user_privileges_context';
const state: State = mockGlobalState;
@ -125,6 +125,7 @@ const TestProvidersWithPrivilegesComponent: React.FC<Props> = ({
{
siem: { show: true, crud: true },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
[ASSISTANT_FEATURE_ID]: { 'ai-assistant': true },
} as unknown as Capabilities
}
>

View file

@ -113,6 +113,9 @@ jest.mock('../../../../common/lib/kibana', () => {
save: true,
show: true,
},
siem: {
'ai-assistant': true,
},
},
},
data: {

View file

@ -21,7 +21,11 @@ import { coreMock } from '@kbn/core/public/mocks';
import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context';
import { useTimelineEventsDetails } from '../../../containers/details';
import { allCasesPermissions } from '../../../../cases_test_utils';
import { DEFAULT_ALERTS_INDEX, DEFAULT_PREVIEW_INDEX } from '../../../../../common/constants';
import {
DEFAULT_ALERTS_INDEX,
DEFAULT_PREVIEW_INDEX,
ASSISTANT_FEATURE_ID,
} from '../../../../../common/constants';
const ecsData: Ecs = {
_id: '1',
@ -138,6 +142,13 @@ describe('event details panel component', () => {
(KibanaServices.get as jest.Mock).mockReturnValue(coreStartMock);
(useKibana as jest.Mock).mockReturnValue({
services: {
application: {
capabilities: {
[ASSISTANT_FEATURE_ID]: {
'ai-assistant': true,
},
},
},
uiSettings: {
get: jest.fn().mockReturnValue([]),
},

View file

@ -48,6 +48,25 @@ const CASES_APP_FEATURE_CONFIG = {
},
};
const ASSISTANT_BASE_CONFIG = {
bar: 'bar',
};
const ASSISTANT_APP_FEATURE_CONFIG = {
'test-assistant-feature': {
privileges: {
all: {
ui: ['test-assistant-capability'],
api: ['test-assistant-capability'],
},
read: {
ui: ['test-assistant-capability'],
api: ['test-assistant-capability'],
},
},
},
};
jest.mock('./security_kibana_features', () => {
return {
getSecurityBaseKibanaFeature: jest.fn(() => SECURITY_BASE_CONFIG),
@ -75,6 +94,20 @@ jest.mock('./security_cases_kibana_sub_features', () => {
};
});
jest.mock('./security_assistant_kibana_features', () => {
return {
getAssistantBaseKibanaFeature: jest.fn(() => ASSISTANT_BASE_CONFIG),
getAssistantBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
getAssistantAppFeaturesConfig: jest.fn(() => ASSISTANT_APP_FEATURE_CONFIG),
};
});
jest.mock('./security_assistant_kibana_sub_features', () => {
return {
assistantSubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
};
});
describe('AppFeatures', () => {
it('should register enabled kibana features', () => {
const featuresSetup = {
@ -118,4 +151,25 @@ describe('AppFeatures', () => {
subFeatures: [{ baz: 'baz' }],
});
});
it('should register enabled assistant features', () => {
const featuresSetup = {
registerKibanaFeature: jest.fn(),
} as unknown as PluginSetupContract;
const appFeatureKeys = ['test-assistant-feature'] as unknown as AppFeatureKeys;
const appFeatures = new AppFeatures(
loggingSystemMock.create().get('mock'),
[] as unknown as ExperimentalFeatures
);
appFeatures.init(featuresSetup);
appFeatures.set(appFeatureKeys);
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
...ASSISTANT_BASE_CONFIG,
...ASSISTANT_APP_FEATURE_CONFIG['test-assistant-feature'],
subFeatures: [{ baz: 'baz' }],
});
});
});

View file

@ -22,9 +22,16 @@ import {
import { AppFeaturesConfigMerger } from './app_features_config_merger';
import { casesSubFeaturesMap } from './security_cases_kibana_sub_features';
import { securitySubFeaturesMap } from './security_kibana_sub_features';
import { assistantSubFeaturesMap } from './security_assistant_kibana_sub_features';
import {
getAssistantAppFeaturesConfig,
getAssistantBaseKibanaFeature,
getAssistantBaseKibanaSubFeatureIds,
} from './security_assistant_kibana_features';
export class AppFeatures {
private securityFeatureConfigMerger: AppFeaturesConfigMerger;
private assistantFeatureConfigMerger: AppFeaturesConfigMerger;
private casesFeatureConfigMerger: AppFeaturesConfigMerger;
private appFeatures?: Set<AppFeatureKey>;
private featuresSetup?: FeaturesPluginSetup;
@ -38,6 +45,10 @@ export class AppFeatures {
securitySubFeaturesMap
);
this.casesFeatureConfigMerger = new AppFeaturesConfigMerger(this.logger, casesSubFeaturesMap);
this.assistantFeatureConfigMerger = new AppFeaturesConfigMerger(
this.logger,
assistantSubFeaturesMap
);
}
public init(featuresSetup: FeaturesPluginSetup) {
@ -98,6 +109,23 @@ export class AppFeatures {
this.logger.info(JSON.stringify(completeCasesAppFeatureConfig));
this.featuresSetup.registerKibanaFeature(completeCasesAppFeatureConfig);
// register security assistant Kibana features
const securityAssistantBaseKibanaFeature = getAssistantBaseKibanaFeature();
const securityAssistantBaseKibanaSubFeatureIds = getAssistantBaseKibanaSubFeatureIds();
const enabledAssistantAppFeaturesConfigs = this.getEnabledAppFeaturesConfigs(
getAssistantAppFeaturesConfig()
);
const completeAssistantAppFeatureConfig =
this.assistantFeatureConfigMerger.mergeAppFeatureConfigs(
securityAssistantBaseKibanaFeature,
securityAssistantBaseKibanaSubFeatureIds,
enabledAssistantAppFeaturesConfigs
);
this.logger.info(JSON.stringify(completeAssistantAppFeatureConfig));
this.featuresSetup.registerKibanaFeature(completeAssistantAppFeatureConfig);
}
private getEnabledAppFeaturesConfigs(

View file

@ -0,0 +1,74 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
import type { AppFeaturesAssistantConfig, BaseKibanaFeatureConfig } from './types';
import { APP_ID, ASSISTANT_FEATURE_ID } from '../../../common/constants';
import { AppFeatureAssistantKey } from '../../../common/types/app_features';
import type { AssistantSubFeatureId } from './security_assistant_kibana_sub_features';
export const getAssistantBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({
id: ASSISTANT_FEATURE_ID,
name: i18n.translate(
'xpack.securitySolution.featureRegistry.linkSecuritySolutionAssistantTitle',
{
defaultMessage: 'Elastic AI Assistant',
}
),
order: 1100,
category: DEFAULT_APP_CATEGORIES.security,
app: [ASSISTANT_FEATURE_ID, 'kibana'],
catalogue: [APP_ID],
minimumLicense: 'enterprise',
privileges: {
all: {
api: [],
app: [ASSISTANT_FEATURE_ID, 'kibana'],
catalogue: [APP_ID],
savedObject: {
all: [],
read: [],
},
ui: [],
},
read: {
// No read-only mode currently supported
disabled: true,
savedObject: {
all: [],
read: [],
},
ui: [],
},
},
});
export const getAssistantBaseKibanaSubFeatureIds = (): AssistantSubFeatureId[] => [
// This is a sample sub-feature that can be used for future implementations
// AssistantSubFeatureId.createConversation,
];
/**
* Maps the AppFeatures keys to Kibana privileges that will be merged
* into the base privileges config for the Security app.
*
* Privileges can be added in different ways:
* - `privileges`: the privileges that will be added directly into the main Security Assistant feature.
* - `subFeatureIds`: the ids of the sub-features that will be added into the Assistant subFeatures entry.
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Assistant subFeature with the privilege `id` specified.
*/
export const getAssistantAppFeaturesConfig = (): AppFeaturesAssistantConfig => ({
[AppFeatureAssistantKey.assistant]: {
privileges: {
all: {
ui: ['ai-assistant'],
},
},
},
});

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
// This is a sample sub-feature that can be used for future implementations
// @ts-expect-error unused variable
const createConversationSubFeature: SubFeatureConfig = {
name: i18n.translate(
'xpack.securitySolution.featureRegistry.assistant.createConversationSubFeatureName',
{
defaultMessage: 'Create Conversations',
}
),
description: i18n.translate(
'xpack.securitySolution.featureRegistry.subFeatures.assistant.description',
{ defaultMessage: 'Create custom conversations.' }
),
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
api: [],
id: 'create_conversation',
name: i18n.translate(
'xpack.securitySolution.featureRegistry.assistant.createConversationSubFeatureDetails',
{
defaultMessage: 'Create conversations',
}
),
includeIn: 'all',
savedObject: {
all: [],
read: [],
},
ui: ['createConversation'],
},
],
},
],
};
export enum AssistantSubFeatureId {
createConversation = 'createConversationSubFeature',
}
// Defines all the ordered Security Assistant subFeatures available
export const assistantSubFeaturesMap = Object.freeze(
new Map<AssistantSubFeatureId, SubFeatureConfig>([
// This is a sample sub-feature that can be used for future implementations
// [AssistantSubFeatureId.createConversation, createConversationSubFeature],
])
);

View file

@ -7,7 +7,11 @@
import type { KibanaFeatureConfig, SubFeaturePrivilegeConfig } from '@kbn/features-plugin/common';
import type { AppFeatureKey } from '../../../common';
import type { AppFeatureSecurityKey, AppFeatureCasesKey } from '../../../common/types/app_features';
import type {
AppFeatureSecurityKey,
AppFeatureCasesKey,
AppFeatureAssistantKey,
} from '../../../common/types/app_features';
import type { RecursivePartial } from '../../../common/utility_types';
export type BaseKibanaFeatureConfig = Omit<KibanaFeatureConfig, 'subFeatures'>;
@ -29,3 +33,7 @@ export type AppFeaturesCasesConfig<T extends string = string> = Record<
AppFeatureCasesKey,
AppFeatureKibanaConfig<T>
>;
export type AppFeaturesAssistantConfig<T extends string = string> = Record<
AppFeatureAssistantKey,
AppFeatureKibanaConfig<T>
>;

View file

@ -32,6 +32,7 @@
"feature": {
"ml": ["all"],
"siem": ["all", "read_alerts", "crud_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["all"],
"actions": ["read"],
"builtInAlerts": ["all"],

View file

@ -34,6 +34,7 @@
"feature": {
"ml": ["read"],
"siem": ["all", "read_alerts", "crud_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["all"],
"actions": ["read"],
"builtInAlerts": ["all"]

View file

@ -34,6 +34,7 @@
"feature": {
"ml": ["read"],
"siem": ["all", "read_alerts", "crud_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["all"],
"builtInAlerts": ["all"]
},

View file

@ -38,6 +38,7 @@
"feature": {
"ml": ["all"],
"siem": ["all", "read_alerts", "crud_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["all"],
"actions": ["all"],
"builtInAlerts": ["all"]

View file

@ -27,6 +27,7 @@
"feature": {
"ml": ["read"],
"siem": ["read", "read_alerts"],
"securitySolutionAssistant": ["none"],
"securitySolutionCases": ["read"],
"actions": ["read"],
"builtInAlerts": ["read"]

View file

@ -37,6 +37,7 @@
"feature": {
"ml": ["read"],
"siem": ["all", "read_alerts", "crud_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["all"],
"actions": ["read"],
"builtInAlerts": ["all"]

View file

@ -37,6 +37,7 @@
"feature": {
"ml": ["read"],
"siem": ["all", "read_alerts", "crud_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["all"],
"actions": ["all"],
"builtInAlerts": ["all"]

View file

@ -26,6 +26,7 @@
"feature": {
"ml": ["read"],
"siem": ["read", "read_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["read"],
"actions": ["read"],
"builtInAlerts": ["read"]

View file

@ -31,6 +31,7 @@
"feature": {
"ml": ["read"],
"siem": ["read", "read_alerts"],
"securitySolutionAssistant": ["all"],
"securitySolutionCases": ["read"],
"actions": ["read"],
"builtInAlerts": ["read"]

View file

@ -17,6 +17,7 @@ export const PLI_APP_FEATURES: PliAppFeatures = {
essentials: [AppFeatureKey.endpointHostManagement, AppFeatureKey.endpointPolicyManagement],
complete: [
AppFeatureKey.advancedInsights,
AppFeatureKey.assistant,
AppFeatureKey.investigationGuide,
AppFeatureKey.threatIntelligence,
AppFeatureKey.casesConnectors,

View file

@ -125,6 +125,7 @@ export default function ({ getService }: FtrProviderContext) {
'uptime',
'siem',
'slo',
'securitySolutionAssistant',
'securitySolutionCases',
'fleet',
'fleetv2',

View file

@ -56,6 +56,7 @@ export default function ({ getService }: FtrProviderContext) {
'execute_operations_all',
],
uptime: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionAssistant: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'],
infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'],
logs: ['all', 'read', 'minimal_all', 'minimal_read'],

View file

@ -42,6 +42,7 @@ export default function ({ getService }: FtrProviderContext) {
osquery: ['all', 'read', 'minimal_all', 'minimal_read'],
ml: ['all', 'read', 'minimal_all', 'minimal_read'],
siem: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionAssistant: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read'],
fleetv2: ['all', 'read', 'minimal_all', 'minimal_read'],
fleet: ['all', 'read', 'minimal_all', 'minimal_read'],
@ -130,6 +131,7 @@ export default function ({ getService }: FtrProviderContext) {
'execute_operations_all',
],
uptime: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionAssistant: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read', 'cases_delete'],
infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'],
logs: ['all', 'read', 'minimal_all', 'minimal_read'],

View file

@ -77,7 +77,8 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
'enterpriseSearchVectorSearch',
'appSearch',
'workplaceSearch',
'guidedOnboardingFeature'
'guidedOnboardingFeature',
'securitySolutionAssistant'
)
);
break;