Grant access to machine learning features when base privileges are used (#115444)

This commit is contained in:
Xavier Mouligneau 2021-10-26 18:15:44 -04:00 committed by GitHub
parent 1827cf2479
commit 852a728229
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 33 deletions

View file

@ -96,7 +96,7 @@ export function getPluginPrivileges() {
];
const privilege = {
app: [PLUGIN_ID, 'kibana'],
excludeFromBasePrivileges: true,
excludeFromBasePrivileges: false,
management: {
insightsAndAlerting: ['jobsListLink'],
},

View file

@ -64,7 +64,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
expect(sections).to.have.length(2);
expect(sections[0]).to.eql({
sectionId: 'insightsAndAlerting',
sectionLinks: ['triggersActions'],
sectionLinks: ['triggersActions', 'jobsListLink'],
});
expect(sections[1]).to.eql({
sectionId: 'kibana',

View file

@ -77,9 +77,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await security.user.delete('global_all');
});
it(`doesn't show ml navlink`, async () => {
it(`shows ml navlink`, async () => {
const navLinks = (await appsMenu.readLinks()).map((link) => link.text);
expect(navLinks).not.to.contain('Machine Learning');
expect(navLinks).to.contain('Machine Learning');
});
});
@ -103,5 +103,75 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
expect(navLinks).to.contain('Machine Learning');
});
});
describe('ml read', () => {
before(async () => {
await security.role.create('ml_role_read', {
elasticsearch: {
indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }],
},
kibana: [
{
base: [],
feature: { ml: ['read'], savedObjectsManagement: ['read'] },
spaces: ['*'],
},
],
});
await security.user.create('ml_read_user', {
password: 'ml_read-password',
roles: ['ml_role_read'],
full_name: 'ml read',
});
await PageObjects.security.login('ml_read_user', 'ml_read-password');
});
after(async () => {
await security.role.delete('ml_role_read');
await security.user.delete('ml_read_user');
});
it('shows ML navlink', async () => {
const navLinks = (await appsMenu.readLinks()).map((link) => link.text);
expect(navLinks).to.contain('Machine Learning');
});
});
describe('ml none', () => {
before(async () => {
await security.role.create('ml_role_none', {
elasticsearch: {
indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }],
},
kibana: [
{
base: [],
feature: { discover: ['read'] },
spaces: ['*'],
},
],
});
await security.user.create('ml_none_user', {
password: 'ml_none-password',
roles: ['ml_role_none'],
full_name: 'ml none',
});
await PageObjects.security.login('ml_none_user', 'ml_none-password');
});
after(async () => {
await security.role.delete('ml_role_none');
await security.user.delete('ml_none_user');
});
it('does NOT show ML navlink', async () => {
const navLinks = (await appsMenu.readLinks()).map((link) => link.text);
expect(navLinks).to.not.contain('Machine Learning');
});
});
});
}

View file

@ -13,10 +13,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'error']);
const ml = getService('ml');
const testUsers = [
{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true },
{ user: USER.ML_UNAUTHORIZED_SPACES, discoverAvailable: true },
];
const testUsers = [{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true }];
describe('for user with no ML access', function () {
this.tags(['skipFirefox', 'mlqa']);

View file

@ -21,7 +21,6 @@ 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_UNAUTHORIZED_SPACES = 'ft_ml_unauthorized_spaces',
}
export function MachineLearningSecurityCommonProvider({ getService }: FtrProviderContext) {
@ -90,8 +89,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [
{
base: [],
feature: { ml: ['all'], savedObjectsManagement: ['all'] },
base: ['all'],
spaces: ['*'],
},
],
@ -123,8 +121,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [
{
base: [],
feature: { ml: ['read'], savedObjectsManagement: ['read'] },
base: ['read'],
spaces: ['*'],
},
],
@ -134,6 +131,31 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [{ base: [], feature: { discover: ['read'] }, spaces: ['default'] }],
},
{
name: 'ft_all_space_ml_none',
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [
{
base: [],
// This role is intended to be used by the "ft_ml_poweruser" and "ft_ml_viewer" users; they should have access to ML by virtue of
// the "machine_learning_admin" and "machine_learning_user" roles. However, a user needs _at least_ one Kibana privilege to log
// into Kibana. This role allows these users to log in, but explicitly omits ML from the feature privileges.
// In addition: several functional tests that use these users also rely on UI elements that are enabled by other Kibana features,
// such as "View in Lens", "Add to Dashboard", and creating anomaly detection rules. These feature privileges are the minimal ones
// necessary to satisfy all of those functional tests.
feature: {
discover: ['read'],
visualize: ['read'],
dashboard: ['all'],
actions: ['all'],
savedObjectsManagement: ['all'],
advancedSettings: ['all'],
indexPatterns: ['all'],
},
spaces: ['*'],
},
],
},
];
const users = [
@ -142,7 +164,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
full_name: 'ML Poweruser',
password: 'mlp001',
roles: [
'kibana_admin',
'ft_all_space_ml_none',
'machine_learning_admin',
'ft_ml_source',
'ft_ml_dest',
@ -172,7 +194,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
full_name: 'ML Viewer',
password: 'mlv001',
roles: [
'kibana_admin',
'ft_all_space_ml_none',
'machine_learning_user',
'ft_ml_source_readonly',
'ft_ml_dest_readonly',
@ -200,12 +222,6 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
name: 'ft_ml_unauthorized',
full_name: 'ML Unauthorized',
password: 'mlu001',
roles: ['kibana_admin', 'ft_ml_source_readonly'],
},
{
name: 'ft_ml_unauthorized_spaces',
full_name: 'ML Unauthorized',
password: 'mlus001',
roles: ['ft_default_space_ml_none', 'ft_ml_source_readonly'],
},
];

View file

@ -13,10 +13,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'error']);
const ml = getService('ml');
const testUsers = [
{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true },
{ user: USER.ML_UNAUTHORIZED_SPACES, discoverAvailable: true },
];
const testUsers = [{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true }];
describe('for user with no ML access', function () {
for (const testUser of testUsers) {

View file

@ -46,12 +46,10 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
case 'dual_privileges_all at everything_space': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything except ml, monitoring, and ES features are enabled
// everything except monitoring, and ES features are enabled
const expected = mapValues(
uiCapabilities.value!.catalogue,
(enabled, catalogueId) =>
catalogueId !== 'ml' &&
catalogueId !== 'ml_file_data_visualizer' &&
catalogueId !== 'monitoring' &&
catalogueId !== 'osquery' &&
!esFeatureExceptions.includes(catalogueId)
@ -59,16 +57,35 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
expect(uiCapabilities.value!.catalogue).to.eql(expected);
break;
}
case 'everything_space_all at everything_space':
case 'everything_space_all at everything_space': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything except spaces, monitoring, the enterprise search suite, and ES features are enabled
// (easier to say: all "proper" Kibana features are enabled)
const exceptions = [
'monitoring',
'enterpriseSearch',
'appSearch',
'workplaceSearch',
'spaces',
'osquery',
...esFeatureExceptions,
];
const expected = mapValues(
uiCapabilities.value!.catalogue,
(enabled, catalogueId) => !exceptions.includes(catalogueId)
);
expect(uiCapabilities.value!.catalogue).to.eql(expected);
break;
}
case 'global_read at everything_space':
case 'dual_privileges_read at everything_space':
case 'everything_space_read at everything_space': {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('catalogue');
// everything except spaces, ml, monitoring, the enterprise search suite, and ES features are enabled
// everything except spaces, ml_file_data_visualizer, monitoring, the enterprise search suite, and ES features are enabled
// (easier to say: all "proper" Kibana features are enabled)
const exceptions = [
'ml',
'ml_file_data_visualizer',
'monitoring',
'enterpriseSearch',

View file

@ -42,7 +42,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
expect(uiCapabilities.success).to.be(true);
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(
navLinksBuilder.except('ml', 'monitoring', 'osquery')
navLinksBuilder.except('monitoring', 'osquery')
);
break;
case 'everything_space_all at everything_space':
@ -53,7 +53,6 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
expect(uiCapabilities.value).to.have.property('navLinks');
expect(uiCapabilities.value!.navLinks).to.eql(
navLinksBuilder.except(
'ml',
'monitoring',
'enterpriseSearch',
'appSearch',