[ML] Hide ML embeddables from the "Add panel" flyout when ML feature isn't available (#187639)

## Summary

Fixes #187007 

Hides ML embeddables from the "Add panel" flyout when
1. ML feature isn't available for the user role 
2. ML is hidden in a current space 


### How to test 
1. Create a custom role with disabled ML privilege and assign it to a
user

![image](07fe2865-2ebe-448f-8e31-c36581b57b28)

2. Remove ML feature visibility in a current space 

![image](dc3f19fa-cb29-424a-a04d-677518bb45fa)

### Checklist

- [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:
Dima Arnautov 2024-07-09 15:21:20 +03:00 committed by GitHub
parent 43fca860ae
commit 7997d6fe33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 73 additions and 5 deletions

View file

@ -69,9 +69,14 @@ export class DashboardAddPanelService extends FtrService {
await this.testSubjects.click(`visType-${visType}`);
}
async verifyEmbeddableFactoryGroupExists(groupId: string) {
async verifyEmbeddableFactoryGroupExists(groupId: string, expectExist: boolean = true) {
this.log.debug('DashboardAddPanel.verifyEmbeddableFactoryGroupExists');
await this.testSubjects.existOrFail(`dashboardEditorMenu-${groupId}Group`);
const testSubject = `dashboardEditorMenu-${groupId}Group`;
if (expectExist) {
await this.testSubjects.existOrFail(testSubject);
} else {
await this.testSubjects.missingOrFail(testSubject);
}
}
async clickAddNewEmbeddableLink(type: string) {

View file

@ -41,7 +41,9 @@ export class AiopsPlugin
{ registerChangePointChartsAttachment },
[coreStart, pluginStart],
]) => {
if (license.hasAtLeast('platinum')) {
const { canUseAiops } = coreStart.application.capabilities.ml;
if (license.hasAtLeast('platinum') && canUseAiops) {
if (embeddable) {
registerEmbeddables(embeddable, core);
}

View file

@ -269,7 +269,7 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
);
}
if (fullLicense) {
if (fullLicense && mlCapabilities.canGetMlInfo) {
registerMlUiActions(pluginsSetup.uiActions, core);
if (this.enabledFeatures.ad) {

View file

@ -10,8 +10,9 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../services/ml/security_common';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'error']);
const PageObjects = getPageObjects(['common', 'error', 'dashboard']);
const ml = getService('ml');
const esArchiver = getService('esArchiver');
const testUsers = [{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true }];
@ -56,5 +57,28 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
});
}
describe('for user with no ML access and Kibana features access', function () {
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp');
await ml.securityUI.loginAs(USER.ML_DISABLED);
await ml.api.cleanMlIndices();
});
after(async () => {
// NOTE: Logout needs to happen before anything else to avoid flaky behavior
await ml.securityUI.logout();
});
it('should not register ML embeddables in the dashboard', async () => {
await ml.testExecution.logTestStep(
'should not contain ML embeddable in the Add panel list'
);
await PageObjects.dashboard.navigateToApp();
await PageObjects.dashboard.clickCreateDashboardPrompt();
await ml.dashboardEmbeddables.assertMlSectionExists(false);
});
});
});
}

View file

@ -114,6 +114,13 @@ export function MachineLearningDashboardEmbeddablesProvider(
});
},
async assertMlSectionExists(expectExist = true) {
await retry.tryForTime(60 * 1000, async () => {
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.verifyEmbeddableFactoryGroupExists('ml', expectExist);
});
},
async openAnomalyJobSelectionFlyout(
mlEmbeddableType: 'ml_anomaly_swimlane' | 'ml_anomaly_charts' | 'ml_single_metric_viewer'
) {

View file

@ -21,6 +21,7 @@ export enum USER {
ML_VIEWER_SPACE1 = 'ft_ml_viewer_space1',
ML_VIEWER_ALL_SPACES = 'ft_ml_viewer_all_spaces',
ML_UNAUTHORIZED = 'ft_ml_unauthorized',
ML_DISABLED = 'ft_ml_disabled',
}
export function MachineLearningSecurityCommonProvider({ getService }: FtrProviderContext) {
@ -133,6 +134,29 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [{ base: [], feature: { discover: ['read'] }, spaces: ['default'] }],
},
{
name: 'ft_ml_disabled',
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [
{
base: [],
feature: {
// FIXME: We need permission to save search in Discover to test the data viz embeddable
// change permission back to read once tests are moved out of ML
discover: ['all'],
visualize: ['add'],
dashboard: ['all'],
actions: ['all'],
savedObjectsManagement: ['all'],
advancedSettings: ['all'],
indexPatterns: ['all'],
generalCases: ['all'],
ml: ['none'],
},
spaces: ['*'],
},
],
},
{
name: 'ft_all_space_ml_none',
elasticsearch: { cluster: [], indices: [], run_as: [] },
@ -230,6 +254,12 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
password: 'mlu001',
roles: ['ft_default_space_ml_none', 'ft_ml_source_readonly'],
},
{
name: 'ft_ml_disabled',
full_name: 'ML Disabled',
password: 'mlud001',
roles: ['ft_ml_disabled'],
},
];
return {