mirror of https://github.com/apache/nifi.git
NIFI-2884: - Adding support to selecting multiple users before updating a policy.
NIFI-2533: - Only including a user/group in the search results if they are not currently selected. NIFI-2286: - Providing a tooltip for the add user and remove policy button. This closes #1155. Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
parent
8c09bef4f8
commit
8dc60c72d4
|
@ -38,7 +38,6 @@ import org.apache.nifi.web.api.dto.RevisionDTO;
|
|||
import org.apache.nifi.web.api.dto.TenantDTO;
|
||||
import org.apache.nifi.web.api.dto.UserDTO;
|
||||
import org.apache.nifi.web.api.dto.UserGroupDTO;
|
||||
import org.apache.nifi.web.api.entity.ClusterSearchResultsEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantsEntity;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
|
@ -166,6 +165,10 @@ public class TenantsResource extends ApplicationResource {
|
|||
throw new IllegalArgumentException("User ID cannot be specified.");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(requestUserEntity.getComponent().getIdentity())) {
|
||||
throw new IllegalArgumentException("User identity must be specified.");
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
return replicate(HttpMethod.POST, requestUserEntity);
|
||||
}
|
||||
|
@ -551,6 +554,10 @@ public class TenantsResource extends ApplicationResource {
|
|||
throw new IllegalArgumentException("User group ID cannot be specified.");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(requestUserGroupEntity.getComponent().getIdentity())) {
|
||||
throw new IllegalArgumentException("User group identity must be specified.");
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
return replicate(HttpMethod.POST, requestUserGroupEntity);
|
||||
}
|
||||
|
@ -864,19 +871,19 @@ public class TenantsResource extends ApplicationResource {
|
|||
// ------------
|
||||
|
||||
/**
|
||||
* Searches the cluster for a node with a given address.
|
||||
* Searches for a tenant with a given identity.
|
||||
*
|
||||
* @param value Search value that will be matched against a node's address
|
||||
* @return Nodes that match the specified criteria
|
||||
* @param value Search value that will be matched against a user/group identity
|
||||
* @return Tenants match the specified criteria
|
||||
*/
|
||||
@GET
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("search-results")
|
||||
@ApiOperation(
|
||||
value = "Searches the cluster for a node with the specified address",
|
||||
value = "Searches for a tenant with the specified identity",
|
||||
notes = NON_GUARANTEED_ENDPOINT,
|
||||
response = ClusterSearchResultsEntity.class,
|
||||
response = TenantsEntity.class,
|
||||
authorizations = {
|
||||
@Authorization(value = "Read - /tenants", type = "")
|
||||
}
|
||||
|
@ -892,7 +899,7 @@ public class TenantsResource extends ApplicationResource {
|
|||
)
|
||||
public Response searchCluster(
|
||||
@ApiParam(
|
||||
value = "Node address to search for.",
|
||||
value = "Identity to search for.",
|
||||
required = true
|
||||
)
|
||||
@QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) {
|
||||
|
@ -925,6 +932,7 @@ public class TenantsResource extends ApplicationResource {
|
|||
|
||||
final TenantEntity entity = new TenantEntity();
|
||||
entity.setPermissions(userEntity.getPermissions());
|
||||
entity.setRevision(userEntity.getRevision());
|
||||
entity.setId(userEntity.getId());
|
||||
entity.setComponent(tenant);
|
||||
|
||||
|
@ -942,6 +950,7 @@ public class TenantsResource extends ApplicationResource {
|
|||
|
||||
final TenantEntity entity = new TenantEntity();
|
||||
entity.setPermissions(userGroupEntity.getPermissions());
|
||||
entity.setRevision(userGroupEntity.getRevision());
|
||||
entity.setId(userGroupEntity.getId());
|
||||
entity.setComponent(tenant);
|
||||
|
||||
|
|
|
@ -78,8 +78,8 @@
|
|||
<div id="component-policy-target"></div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<button id="delete-policy-button" class="fa fa-trash policy-button"></button>
|
||||
<button id="new-policy-user-button" class="fa fa-user-plus policy-button"></button>
|
||||
<button id="delete-policy-button" class="fa fa-trash policy-button" title="Delete this policy"></button>
|
||||
<button id="new-policy-user-button" class="fa fa-user-plus policy-button" title="Add users/groups to this policy"></button>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<div id="policy-table"></div>
|
||||
|
|
|
@ -17,7 +17,21 @@
|
|||
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
|
||||
<div id="search-users-dialog" class="hidden">
|
||||
<div class="dialog-content">
|
||||
<input id="search-users-field" type="text" placeholder="User Identity"/>
|
||||
<div class="secure-port-setting">
|
||||
<input id="search-users-field" type="text" placeholder="User Identity"/>
|
||||
</div>
|
||||
<div class="secure-port-setting">
|
||||
<div class="setting-name">Allowed Users</div>
|
||||
<div class="setting-field allowed-container">
|
||||
<ul id="allowed-users" class="allowed"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="secure-port-setting">
|
||||
<div class="setting-name">Allowed Groups</div>
|
||||
<div class="setting-field allowed-container">
|
||||
<ul id="allowed-groups" class="allowed"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="search-users-results"></div>
|
||||
|
|
|
@ -536,6 +536,15 @@ div.context-menu-item-text {
|
|||
color: #262626;
|
||||
}
|
||||
|
||||
/* search */
|
||||
|
||||
li.search-no-matches {
|
||||
padding: 4px;
|
||||
font-weight: bold;
|
||||
color: #aaa;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* progress bars */
|
||||
|
||||
md-progress-linear > div {
|
||||
|
|
|
@ -170,13 +170,6 @@ input.search-flow {
|
|||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#search-flow-results li.search-no-matches {
|
||||
padding: 4px;
|
||||
font-weight: bold;
|
||||
color: #aaa;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.search-glass-pane {
|
||||
position: absolute;
|
||||
background-image: url(../images/spacer.png);
|
||||
|
|
|
@ -194,7 +194,7 @@ div.policy-selected-component-type {
|
|||
|
||||
#search-users-dialog {
|
||||
width: 450px;
|
||||
height: 250px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
#search-users-results .ui-autocomplete {
|
||||
|
@ -211,6 +211,69 @@ div.policy-selected-component-type {
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
div.secure-port-setting {
|
||||
margin-bottom: 15px;
|
||||
width: 410px;
|
||||
}
|
||||
|
||||
li.search-users-header {
|
||||
font-weight: bold;
|
||||
padding-top: 4px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
div.search-users-match-header {
|
||||
font-weight: normal;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
li.search-users-no-matches {
|
||||
padding: 4px;
|
||||
font-weight: bold;
|
||||
color: #aaa;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.allowed-container {
|
||||
width: 408px;
|
||||
height: 100px;
|
||||
border: 1px solid #aaa;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
ul.allowed {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul.allowed li {
|
||||
height: 16px;
|
||||
width: 385px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: #262626;
|
||||
overflow: hidden;
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
line-height: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
span.allowed-entity {
|
||||
float: left;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
div.remove-allowed-entity {
|
||||
float: right;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
/*
|
||||
admin policy message
|
||||
*/
|
||||
|
|
|
@ -50,85 +50,31 @@ nf.PolicyManagement = (function () {
|
|||
},
|
||||
handler: {
|
||||
click: function () {
|
||||
var tenantSearchTerm = $('#search-users-field').val();
|
||||
// add to table and update policy
|
||||
var policyGrid = $('#policy-table').data('gridInstance');
|
||||
var policyData = policyGrid.getData();
|
||||
|
||||
// create the search request
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
data: {
|
||||
q: tenantSearchTerm
|
||||
},
|
||||
dataType: 'json',
|
||||
url: config.urls.searchTenants
|
||||
}).done(function (response) {
|
||||
var selectedUsers = $.map(response.users, function (user) {
|
||||
return $.extend({
|
||||
type: 'user'
|
||||
}, user);
|
||||
});
|
||||
var selectedGroups = $.map(response.userGroups, function (userGroup) {
|
||||
return $.extend({
|
||||
type: 'group'
|
||||
}, userGroup);
|
||||
});
|
||||
// begin the update
|
||||
policyData.beginUpdate();
|
||||
|
||||
var selectedUser = [];
|
||||
selectedUser = selectedUser.concat(selectedUsers, selectedGroups);
|
||||
|
||||
// add the user to the policy table
|
||||
var addUser = function (user) {
|
||||
// add to table and update policy
|
||||
var policyGrid = $('#policy-table').data('gridInstance');
|
||||
var policyData = policyGrid.getData();
|
||||
|
||||
// begin the update
|
||||
policyData.beginUpdate();
|
||||
|
||||
// remove the user
|
||||
policyData.addItem(user);
|
||||
|
||||
// end the update
|
||||
policyData.endUpdate();
|
||||
|
||||
// update the policy
|
||||
updatePolicy();
|
||||
};
|
||||
|
||||
// ensure the search found some results
|
||||
if (!$.isArray(selectedUser) || selectedUser.length === 0) {
|
||||
nf.Dialog.showOkDialog({
|
||||
headerText: 'User Search',
|
||||
dialogContent: 'No users match \'' + nf.Common.escapeHtml(tenantSearchTerm) + '\'.'
|
||||
});
|
||||
} else if (selectedUser.length > 1) {
|
||||
var exactMatch = false;
|
||||
|
||||
// look for an exact match
|
||||
$.each(selectedUser, function (_, userEntity) {
|
||||
if (userEntity.component.identity === tenantSearchTerm) {
|
||||
addUser(userEntity);
|
||||
exactMatch = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// if there is an exact match, use it
|
||||
if (exactMatch) {
|
||||
// close the dialog
|
||||
$('#search-users-dialog').modal('hide');
|
||||
} else {
|
||||
nf.Dialog.showOkDialog({
|
||||
headerText: 'User Search',
|
||||
dialogContent: 'More than one user matches \'' + nf.Common.escapeHtml(tenantSearchTerm) + '\'.'
|
||||
});
|
||||
}
|
||||
} else if (selectedUser.length === 1) {
|
||||
addUser(selectedUser[0]);
|
||||
|
||||
// close the dialog
|
||||
$('#search-users-dialog').modal('hide');
|
||||
}
|
||||
// add all users/groups
|
||||
$.each(getTenantsToAdd($('#allowed-users')), function (_, user) {
|
||||
// remove the user
|
||||
policyData.addItem(user);
|
||||
});
|
||||
$.each(getTenantsToAdd($('#allowed-groups')), function (_, group) {
|
||||
// remove the user
|
||||
policyData.addItem(group);
|
||||
});
|
||||
|
||||
// end the update
|
||||
policyData.endUpdate();
|
||||
|
||||
// update the policy
|
||||
updatePolicy();
|
||||
|
||||
// close the dialog
|
||||
$('#search-users-dialog').modal('hide');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -150,11 +96,18 @@ nf.PolicyManagement = (function () {
|
|||
close: function () {
|
||||
// reset the search fields
|
||||
$('#search-users-field').userSearchAutocomplete('reset').val('');
|
||||
$('#selected-user-id').text('');
|
||||
|
||||
// clear the selected users/groups
|
||||
$('#allowed-users, #allowed-groups').empty();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// listen for removal requests
|
||||
$(document).on('click', 'div.remove-allowed-entity', function () {
|
||||
$(this).closest('li').remove();
|
||||
});
|
||||
|
||||
// configure the user auto complete
|
||||
$.widget('nf.userSearchAutocomplete', $.ui.autocomplete, {
|
||||
reset: function () {
|
||||
|
@ -169,18 +122,25 @@ nf.PolicyManagement = (function () {
|
|||
// results are normalized into a single element array
|
||||
var searchResults = items[0];
|
||||
|
||||
var allowedGroups = getAllAllowedGroups();
|
||||
var allowedUsers = getAllAllowedUsers();
|
||||
|
||||
var self = this;
|
||||
$.each(searchResults.userGroups, function (_, tenant) {
|
||||
self._renderGroup(ul, {
|
||||
label: tenant.component.identity,
|
||||
value: tenant.component.identity
|
||||
});
|
||||
// see if this match is not already selected
|
||||
if ($.inArray(tenant.id, allowedGroups) === -1) {
|
||||
self._renderGroup(ul, $.extend({
|
||||
type: 'group'
|
||||
}, tenant));
|
||||
}
|
||||
});
|
||||
$.each(searchResults.users, function (_, tenant) {
|
||||
self._renderUser(ul, {
|
||||
label: tenant.component.identity,
|
||||
value: tenant.component.identity
|
||||
});
|
||||
// see if this match is not already selected
|
||||
if ($.inArray(tenant.id, allowedUsers) === -1) {
|
||||
self._renderUser(ul, $.extend({
|
||||
type: 'user'
|
||||
}, tenant));
|
||||
}
|
||||
});
|
||||
|
||||
// ensure there were some results
|
||||
|
@ -190,14 +150,14 @@ nf.PolicyManagement = (function () {
|
|||
},
|
||||
_resizeMenu: function () {
|
||||
var ul = this.menu.element;
|
||||
ul.width($('#search-users-field').width() + 6);
|
||||
ul.width($('#search-users-field').outerWidth() - 6);
|
||||
},
|
||||
_renderUser: function (ul, match) {
|
||||
var userContent = $('<a></a>').text(match.label);
|
||||
var userContent = $('<a></a>').text(match.component.identity);
|
||||
return $('<li></li>').data('ui-autocomplete-item', match).append(userContent).appendTo(ul);
|
||||
},
|
||||
_renderGroup: function (ul, match) {
|
||||
var groupLabel = $('<span></span>').text(match.label);
|
||||
var groupLabel = $('<span></span>').text(match.component.identity);
|
||||
var groupContent = $('<a></a>').append('<div class="fa fa-users" style="margin-right: 5px;"></div>').append(groupLabel);
|
||||
return $('<li></li>').data('ui-autocomplete-item', match).append(groupContent).appendTo(ul);
|
||||
}
|
||||
|
@ -224,10 +184,110 @@ nf.PolicyManagement = (function () {
|
|||
}).done(function (searchResponse) {
|
||||
response(searchResponse);
|
||||
});
|
||||
},
|
||||
select: function (event, ui) {
|
||||
addAllowedTenant(ui.item);
|
||||
|
||||
// reset the search field
|
||||
$(this).val('');
|
||||
|
||||
// stop event propagation
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets all allowed groups including those already in the policy and those selected while searching (not yet saved).
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
var getAllAllowedGroups = function () {
|
||||
var policyGrid = $('#policy-table').data('gridInstance');
|
||||
var policyData = policyGrid.getData();
|
||||
|
||||
var userGroups = [];
|
||||
|
||||
// consider existing groups in the policy table
|
||||
var items = policyData.getItems();
|
||||
$.each(items, function (_, item) {
|
||||
if (item.type === 'group') {
|
||||
userGroups.push(item.id);
|
||||
}
|
||||
});
|
||||
|
||||
// also consider groups already selected in the search users dialog
|
||||
$.each(getTenantsToAdd($('#allowed-groups')), function (_, group) {
|
||||
userGroups.push(group.id);
|
||||
});
|
||||
|
||||
return userGroups;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the user groups that will be added upon applying the changes.
|
||||
*
|
||||
* @param {jQuery} container
|
||||
* @returns {Array}
|
||||
*/
|
||||
var getTenantsToAdd = function (container) {
|
||||
var tenants = [];
|
||||
|
||||
// also consider groups already selected in the search users dialog
|
||||
container.children('li').each(function (_, allowedTenant) {
|
||||
var tenant = $(allowedTenant).data('tenant');
|
||||
if (nf.Common.isDefinedAndNotNull(tenant)) {
|
||||
tenants.push(tenant);
|
||||
}
|
||||
});
|
||||
|
||||
return tenants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets all allowed users including those already in the policy and those selected while searching (not yet saved).
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
var getAllAllowedUsers = function () {
|
||||
var policyGrid = $('#policy-table').data('gridInstance');
|
||||
var policyData = policyGrid.getData();
|
||||
|
||||
var users = [];
|
||||
|
||||
// consider existing users in the policy table
|
||||
var items = policyData.getItems();
|
||||
$.each(items, function (_, item) {
|
||||
if (item.type === 'user') {
|
||||
users.push(item.id);
|
||||
}
|
||||
});
|
||||
|
||||
// also consider users already selected in the search users dialog
|
||||
$.each(getTenantsToAdd($('#allowed-users')), function (_, user) {
|
||||
users.push(user.id);
|
||||
});
|
||||
|
||||
return users;
|
||||
};
|
||||
|
||||
/**
|
||||
* Added the specified tenant to the listing of users/groups which will be added when applied.
|
||||
*
|
||||
* @param allowedTenant user/group to add
|
||||
*/
|
||||
var addAllowedTenant = function (allowedTenant) {
|
||||
var allowedTenants = allowedTenant.type === 'user' ? $('#allowed-users') : $('#allowed-groups');
|
||||
|
||||
// append the user
|
||||
var tenant = $('<span></span>').addClass('allowed-entity ellipsis').text(allowedTenant.component.identity).ellipsis();
|
||||
var tenantAction = $('<div></div>').addClass('remove-allowed-entity fa fa-trash');
|
||||
$('<li></li>').data('tenant', allowedTenant).append(tenant).append(tenantAction).appendTo(allowedTenants);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the policy table.
|
||||
*/
|
||||
var initPolicyTable = function () {
|
||||
$('#override-policy-dialog').modal({
|
||||
headerText: 'Override Policy',
|
||||
|
|
|
@ -824,7 +824,12 @@ nf.UsersTable = (function () {
|
|||
$('#new-user-button').on('click', function () {
|
||||
buildUsersList();
|
||||
buildGroupsList();
|
||||
|
||||
// show the dialog
|
||||
$('#user-dialog').modal('show');
|
||||
|
||||
// set the focus automatically, only when adding a new user
|
||||
$('#user-identity-edit-dialog').focus();
|
||||
});
|
||||
|
||||
$('#new-user-button').prop('disabled', false);
|
||||
|
|
Loading…
Reference in New Issue