-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Public Role APIs #20732
Public Role APIs #20732
Changes from 22 commits
c2474b2
50e4fc1
366df0f
a03ac1b
55f59b3
25ddb7b
35a7def
c9a64c6
70862b2
da8d5db
0abe3b6
7c22748
e9f8a73
3b1ddc3
cff2b89
5ee5d23
2072348
3e9e8bd
fa03390
2566043
77ab30b
694ed25
4096ae0
941cf2b
7bcaac7
f00cb3e
b599fda
8b7cb3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,6 @@ | |
*/ | ||
|
||
import _ from 'lodash'; | ||
import chrome from 'ui/chrome'; | ||
import routes from 'ui/routes'; | ||
import { fatalError, toastNotifications } from 'ui/notify'; | ||
import { toggle } from 'plugins/security/lib/util'; | ||
|
@@ -22,60 +21,41 @@ import { IndexPatternsProvider } from 'ui/index_patterns/index_patterns'; | |
import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info'; | ||
import { checkLicenseError } from 'plugins/security/lib/check_license_error'; | ||
import { EDIT_ROLES_PATH, ROLES_PATH } from './management_urls'; | ||
import { ALL_RESOURCE } from '../../../common/constants'; | ||
|
||
const getKibanaPrivileges = (applicationPrivileges, roleApplications, application) => { | ||
const kibanaPrivileges = applicationPrivileges.reduce((acc, p) => { | ||
const getKibanaPrivilegesViewModel = (applicationPrivileges, roleKibanaPrivileges) => { | ||
const viewModel = applicationPrivileges.reduce((acc, p) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: avoid one letter variables |
||
acc[p.name] = false; | ||
return acc; | ||
}, {}); | ||
|
||
if (!roleApplications || roleApplications.length === 0) { | ||
return kibanaPrivileges; | ||
if (!roleKibanaPrivileges || roleKibanaPrivileges.length === 0) { | ||
return viewModel; | ||
} | ||
|
||
// we're filtering out privileges for non-all resources incase the roles were created in a future version | ||
const applications = roleApplications | ||
.filter(roleApplication => roleApplication.application === application) | ||
.filter(roleApplication => !roleApplication.resources.some(resource => resource !== ALL_RESOURCE)); | ||
|
||
const assigned = _.uniq(_.flatten(_.pluck(applications, 'privileges'))); | ||
const assigned = _.uniq(_.flatten(_.pluck(roleKibanaPrivileges, 'privileges'))); | ||
assigned.forEach(a => { | ||
// we don't want to display privileges that aren't in our expected list of privileges | ||
if (a in kibanaPrivileges) { | ||
kibanaPrivileges[a] = true; | ||
if (a in viewModel) { | ||
viewModel[a] = true; | ||
} | ||
}); | ||
|
||
return kibanaPrivileges; | ||
return viewModel; | ||
}; | ||
|
||
const getRoleApplications = (kibanaPrivileges, currentRoleApplications = [], application) => { | ||
// we keep any other applications | ||
const newRoleApplications = currentRoleApplications.filter(roleApplication => { | ||
return roleApplication.application !== application; | ||
}); | ||
|
||
const selectedPrivileges = Object.keys(kibanaPrivileges).filter(key => kibanaPrivileges[key]); | ||
const getKibanaPrivileges = (kibanaPrivilegesViewModel) => { | ||
const selectedPrivileges = Object.keys(kibanaPrivilegesViewModel).filter(key => kibanaPrivilegesViewModel[key]); | ||
|
||
// if we have any selected privileges, add a single application entry | ||
if (selectedPrivileges.length > 0) { | ||
newRoleApplications.push({ | ||
application, | ||
privileges: selectedPrivileges, | ||
resources: [ALL_RESOURCE] | ||
}); | ||
} | ||
|
||
return newRoleApplications; | ||
}; | ||
|
||
const getOtherApplications = (roleApplications, application) => { | ||
if (!roleApplications || roleApplications.length === 0) { | ||
return []; | ||
return [ | ||
{ | ||
privileges: selectedPrivileges | ||
} | ||
]; | ||
} | ||
|
||
return roleApplications.map(roleApplication => roleApplication.application).filter(app =>app !== application); | ||
return []; | ||
}; | ||
|
||
routes.when(`${EDIT_ROLES_PATH}/:name?`, { | ||
|
@@ -98,10 +78,13 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { | |
}); | ||
} | ||
return new ShieldRole({ | ||
cluster: [], | ||
indices: [], | ||
run_as: [], | ||
applications: [] | ||
elasticsearch: { | ||
cluster: [], | ||
indices: [], | ||
run_as: [], | ||
}, | ||
kibana: [], | ||
_unrecognized_applications: [] | ||
}); | ||
}, | ||
applicationPrivileges(ApplicationPrivileges, kbnUrl, Promise, Private) { | ||
|
@@ -128,7 +111,6 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { | |
const Private = $injector.get('Private'); | ||
const confirmModal = $injector.get('confirmModal'); | ||
const shieldIndices = $injector.get('shieldIndices'); | ||
const rbacApplication = chrome.getInjected('rbacApplication'); | ||
|
||
$scope.role = $route.current.locals.role; | ||
$scope.users = $route.current.locals.users; | ||
|
@@ -137,8 +119,8 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { | |
|
||
const applicationPrivileges = $route.current.locals.applicationPrivileges; | ||
const role = $route.current.locals.role; | ||
$scope.kibanaPrivileges = getKibanaPrivileges(applicationPrivileges, role.applications, rbacApplication); | ||
$scope.otherApplications = getOtherApplications(role.applications, rbacApplication); | ||
$scope.kibanaPrivilegesViewModel = getKibanaPrivilegesViewModel(applicationPrivileges, role.kibana); | ||
$scope.otherApplications = role._unrecognized_applications; | ||
|
||
$scope.rolesHref = `#${ROLES_PATH}`; | ||
|
||
|
@@ -162,10 +144,10 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { | |
}; | ||
|
||
$scope.saveRole = (role) => { | ||
role.indices = role.indices.filter((index) => index.names.length); | ||
role.indices.forEach((index) => index.query || delete index.query); | ||
role.elasticsearch.indices = role.elasticsearch.indices.filter((index) => index.names.length); | ||
role.elasticsearch.indices.forEach((index) => index.query || delete index.query); | ||
|
||
role.applications = getRoleApplications($scope.kibanaPrivileges, role.applications, rbacApplication); | ||
role.kibana = getKibanaPrivileges($scope.kibanaPrivilegesViewModel); | ||
|
||
return role.$save() | ||
.then(() => toastNotifications.addSuccess('Updated role')) | ||
|
@@ -203,7 +185,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { | |
$scope.allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); | ||
$scope.allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity'); | ||
|
||
$scope.$watch('role.indices', (indices) => { | ||
$scope.$watch('role.elasticsearch.indices', (indices) => { | ||
if (!indices.length) $scope.addIndex(indices); | ||
else indices.forEach($scope.fetchFieldOptions); | ||
}, true); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import _ from 'lodash'; | ||
import Joi from 'joi'; | ||
import { wrapError } from '../../../../lib/errors'; | ||
|
||
export function initDeleteRolesApi(server, callWithRequest, routePreCheckLicenseFn) { | ||
server.route({ | ||
method: 'DELETE', | ||
path: '/api/security/role/{name}', | ||
handler(request, reply) { | ||
const name = request.params.name; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edit - I'm withdrawing this comment, but leaving it here for posterity.
{"statusCode":404,"error":"Not Found","message":"Not Found"}
|
||
return callWithRequest(request, 'shield.deleteRole', { name }).then( | ||
() => reply().code(204), | ||
_.flow(wrapError, reply)); | ||
}, | ||
config: { | ||
validate: { | ||
params: Joi.object() | ||
.keys({ | ||
name: Joi.string() | ||
.required(), | ||
}) | ||
.required(), | ||
}, | ||
pre: [routePreCheckLicenseFn] | ||
} | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a precedent for how to version our public APIs yet? I'm concerned that not including a version number will prevent us from iterating in minor versions. Or at the other side of the spectrum, these public APIs could inadvertently become fluid like our plugin API, where users have to tie to a specific minor/patch release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want our public APIs to be versioned with our "product version" so that when we do a major release, we are able to make breaking changes to the APIs. If we had to do a breaking change in a minor, we'd have to put it in the release notes and have a very good reason for doing so. We don't want "multiple versions" for stuff like this.