diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java
index 8073e8a111..ab82aceb7a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TenantsResource.java
@@ -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);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp
index b833c7c476..7cd08e0825 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp
@@ -78,8 +78,8 @@
-
-
+
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp
index 03c62ca4a3..43be5c02ff 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/search-users-dialog.jsp
@@ -17,7 +17,21 @@
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/common-ui.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/common-ui.css
index cf6a7a6558..0466df5b7a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/common-ui.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/common-ui.css
@@ -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 {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-status.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-status.css
index c13344db4a..186d9841a8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-status.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/flow-status.css
@@ -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);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css
index 21cdc1ff48..a1a5f89c3b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/policy-management.css
@@ -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
*/
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js
index c8e87f3049..d738b58813 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js
@@ -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 = $(' ').text(match.label);
+ var userContent = $(' ').text(match.component.identity);
return $(' ').data('ui-autocomplete-item', match).append(userContent).appendTo(ul);
},
_renderGroup: function (ul, match) {
- var groupLabel = $(' ').text(match.label);
+ var groupLabel = $(' ').text(match.component.identity);
var groupContent = $(' ').append('
').append(groupLabel);
return $(' ').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 = $(' ').addClass('allowed-entity ellipsis').text(allowedTenant.component.identity).ellipsis();
+ var tenantAction = $('
').addClass('remove-allowed-entity fa fa-trash');
+ $(' ').data('tenant', allowedTenant).append(tenant).append(tenantAction).appendTo(allowedTenants);
+ };
+
+ /**
+ * Initializes the policy table.
+ */
var initPolicyTable = function () {
$('#override-policy-dialog').modal({
headerText: 'Override Policy',
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
index 79267e059c..1656050aa0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
@@ -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);