Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 19 commits into from
Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/common/types/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function getPluginPrivileges() {
];
const privilege = {
app: [PLUGIN_ID, 'kibana'],
excludeFromBasePrivileges: true,
excludeFromBasePrivileges: false,
management: {
insightsAndAlerting: ['jobsListLink'],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
74 changes: 72 additions & 2 deletions x-pack/test/functional/apps/ml/feature_controls/ml_security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});

Expand All @@ -103,5 +103,75 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
expect(navLinks).to.contain('Machine Learning');
});
});

describe('ml read', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these tests that you added for explicit read / explicit none

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');
});
});
});
}
5 changes: 1 addition & 4 deletions x-pack/test/functional/apps/ml/permissions/no_ml_access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
42 changes: 29 additions & 13 deletions x-pack/test/functional/services/ml/security_common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -90,8 +89,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [
{
base: [],
feature: { ml: ['all'], savedObjectsManagement: ['all'] },
base: ['all'],
spaces: ['*'],
},
],
Expand Down Expand Up @@ -123,8 +121,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [
{
base: [],
feature: { ml: ['read'], savedObjectsManagement: ['read'] },
base: ['read'],
spaces: ['*'],
},
],
Expand All @@ -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 = [
Expand All @@ -142,7 +164,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
full_name: 'ML Poweruser',
password: 'mlp001',
roles: [
'kibana_admin',
'ft_all_space_ml_none',
pheyos marked this conversation as resolved.
Show resolved Hide resolved
'machine_learning_admin',
'ft_ml_source',
'ft_ml_dest',
Expand Down Expand Up @@ -172,7 +194,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide
full_name: 'ML Viewer',
password: 'mlv001',
roles: [
'kibana_admin',
'ft_all_space_ml_none',
XavierM marked this conversation as resolved.
Show resolved Hide resolved
'machine_learning_user',
'ft_ml_source_readonly',
'ft_ml_dest_readonly',
Expand Down Expand Up @@ -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'],
XavierM marked this conversation as resolved.
Show resolved Hide resolved
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
29 changes: 23 additions & 6 deletions x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,46 @@ 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)
);
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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand All @@ -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',
Expand Down