Repository: nifi-registry
Updated Branches:
  refs/heads/master afa41cfc3 -> a94b81745


NIFIREG-75 Fix User Group Data Integrity Checks

Removes user existence check from FileUserGroupProvider when
group is created or updated. Replaces it with check in the
Authorizer Decorator class created by Authorizer Factory, so
that all providers are used. Also fixes bug when searching
for group membership by user that returns results across all
providers.

Also updates a package in the authorizers.xml template.

UI fixes for action icon enabled/disabled states in Administration.

This closes #64.

Signed-off-by: Bryan Bende <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/nifi-registry/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-registry/commit/a94b8174
Tree: http://git-wip-us.apache.org/repos/asf/nifi-registry/tree/a94b8174
Diff: http://git-wip-us.apache.org/repos/asf/nifi-registry/diff/a94b8174

Branch: refs/heads/master
Commit: a94b81745d106a1025b60b37365d60dce60f14ac
Parents: afa41cf
Author: Kevin Doran <[email protected]>
Authored: Wed Dec 20 20:57:20 2017 -0500
Committer: Bryan Bende <[email protected]>
Committed: Fri Dec 22 13:47:09 2017 -0500

----------------------------------------------------------------------
 .../nifi/registry/authorization/UserGroup.java  |  9 +++
 .../authorization/AuthorizerFactory.java        | 31 +++++++-
 .../CompositeConfigurableUserGroupProvider.java | 34 ++++++++-
 .../authorization/CompositeUserAndGroups.java   | 79 ++++++++++++++++++++
 .../CompositeUserGroupProvider.java             | 60 ++++++++++-----
 .../file/FileUserGroupProvider.java             | 29 -------
 .../registry/service/AuthorizationService.java  |  6 +-
 .../src/main/resources/conf/authorizers.xml     |  2 +-
 .../security/authorization/UserAndGroups.java   | 15 ++++
 .../IdentityAuthenticationProvider.java         |  2 +-
 .../resources/conf/secure-file/authorizers.xml  |  4 +-
 .../resources/conf/secure-ldap/authorizers.xml  |  4 +-
 .../manage-group/nf-registry-manage-group.html  |  4 +-
 .../manage-user/nf-registry-manage-user.html    |  2 +-
 .../manage-user/nf-registry-manage-user.js      | 44 ++++++-----
 .../nf-registry-manage-bucket.html              |  4 +-
 .../src/main/webapp/services/nf-registry.api.js |  1 +
 .../main/webapp/services/nf-registry.service.js |  5 +-
 18 files changed, 247 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
index cc38c21..1467929 100644
--- 
a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
+++ 
b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/UserGroup.java
@@ -59,4 +59,13 @@ public class UserGroup extends Tenant {
         }
     }
 
+    public void addUser(Tenant user) {
+        if (user != null) {
+            if (this.users == null) {
+                this.users = new HashSet<>();
+            }
+            this.users.add(user);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
index f94889f..49b5688 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
@@ -570,6 +570,9 @@ public class AuthorizerFactory implements 
UserGroupProviderLookup, AccessPolicyP
                                             if 
(tenantExists(baseConfigurableUserGroupProvider, group.getIdentifier(), 
group.getName())) {
                                                 throw new 
IllegalStateException(String.format("User/user group already exists with the 
identity '%s'.", group.getName()));
                                             }
+                                            if 
(!allGroupUsersExist(baseUserGroupProvider, group)) {
+                                                throw new 
IllegalStateException(String.format("Cannot create group '%s' with users that 
don't exist.", group.getName()));
+                                            }
                                             return 
baseConfigurableUserGroupProvider.addGroup(group);
                                         }
 
@@ -586,6 +589,9 @@ public class AuthorizerFactory implements 
UserGroupProviderLookup, AccessPolicyP
                                             if 
(!baseConfigurableUserGroupProvider.isConfigurable(group)) {
                                                 throw new 
IllegalArgumentException("The specified group does not support modification.");
                                             }
+                                            if 
(!allGroupUsersExist(baseUserGroupProvider, group)) {
+                                                throw new 
IllegalStateException(String.format("Cannot update group '%s' to add users that 
don't exist.", group.getName()));
+                                            }
                                             return 
baseConfigurableUserGroupProvider.updateGroup(group);
                                         }
 
@@ -782,10 +788,11 @@ public class AuthorizerFactory implements 
UserGroupProviderLookup, AccessPolicyP
     }
 
     /**
-     * Checks if another user exists with the same identity.
+     * Checks if another user or group exists with the same identity.
      *
-     * @param identifier identity of the user
-     * @param identity identity of the user
+     * @param userGroupProvider the userGroupProvider to use to lookup the 
tenant
+     * @param identifier identity of the tenant
+     * @param identity identity of the tenant
      * @return true if another user exists with the same identity, false 
otherwise
      */
     private static boolean tenantExists(final UserGroupProvider 
userGroupProvider, final String identifier, final String identity) {
@@ -806,4 +813,22 @@ public class AuthorizerFactory implements 
UserGroupProviderLookup, AccessPolicyP
         return false;
     }
 
+    /**
+     * Check that all users in the group exist.
+     *
+     * @param userGroupProvider the userGroupProvider to use to lookup the 
users
+     * @param group the group whose users will be checked for existence.
+     * @return true if another user exists with the same identity, false 
otherwise
+     */
+    private static boolean allGroupUsersExist(final UserGroupProvider 
userGroupProvider, final Group group) {
+        for (String userIdentifier : group.getUsers()) {
+            User user = userGroupProvider.getUser(userIdentifier);
+            if (user == null) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
index dfa671e..b65a549 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
@@ -181,17 +181,43 @@ public class CompositeConfigurableUserGroupProvider 
extends CompositeUserGroupPr
 
     @Override
     public UserAndGroups getUserAndGroups(String identity) throws 
AuthorizationAccessException {
-        UserAndGroups userAndGroups = 
configurableUserGroupProvider.getUserAndGroups(identity);
 
-        if (userAndGroups.getUser() == null) {
-            userAndGroups = super.getUserAndGroups(identity);
+        final CompositeUserAndGroups combinedResult;
+
+        // First, lookup user and groups by identity and combine data from all 
providers
+        UserAndGroups configurableProviderResult = 
configurableUserGroupProvider.getUserAndGroups(identity);
+        UserAndGroups compositeProvidersResult = 
super.getUserAndGroups(identity);
+
+        if (configurableProviderResult.getUser() != null && 
compositeProvidersResult.getUser() != null) {
+            throw new IllegalStateException("Multiple UserGroupProviders claim 
to provide user " + identity);
+
+        } else if (configurableProviderResult.getUser() != null) {
+            combinedResult = new 
CompositeUserAndGroups(configurableProviderResult.getUser(), 
configurableProviderResult.getGroups());
+            combinedResult.addAllGroups(compositeProvidersResult.getGroups());
+
+        } else if (compositeProvidersResult.getUser() != null) {
+            combinedResult = new 
CompositeUserAndGroups(compositeProvidersResult.getUser(), 
compositeProvidersResult.getGroups());
+            
combinedResult.addAllGroups(configurableProviderResult.getGroups());
+
+        } else {
+            return UserAndGroups.EMPTY;
+        }
+
+        // Second, lookup groups containing the user identifier
+        // Don't have to check super.getGroups() because this step is done as 
part of super.getUserAndGroups(...)
+        String userIdentifier = combinedResult.getUser().getIdentifier();
+        for (final Group group : configurableUserGroupProvider.getGroups()) {
+            if (group.getUsers() != null && 
group.getUsers().contains(userIdentifier)) {
+                combinedResult.addGroup(group);
+            }
         }
 
-        return userAndGroups;
+        return combinedResult;
     }
 
     @Override
     public void preDestruction() throws SecurityProviderDestructionException {
         super.preDestruction();
     }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
new file mode 100644
index 0000000..5665122
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class CompositeUserAndGroups implements UserAndGroups {
+
+    private User user;
+    private Set<Group> groups;
+
+    public CompositeUserAndGroups() {
+        this.user = null;
+        this.groups = null;
+    }
+
+    public CompositeUserAndGroups(User user, Set<Group> groups) {
+        this.user = user;
+        setGroups(groups);
+    }
+
+    @Override
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    @Override
+    public Set<Group> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(Set<Group> groups) {
+        // copy the collection so that if we add to this collection it does 
not modify other references
+        if (groups != null) {
+            this.groups = new HashSet<>(groups);
+        } else {
+            this.groups = null;
+        }
+    }
+
+    public void addAllGroups(Set<Group> groups) {
+        if (groups != null) {
+            if (this.groups == null) {
+                this.groups = new HashSet<>();
+            }
+            this.groups.addAll(groups);
+        }
+    }
+
+    public void addGroup(Group group) {
+        if (group != null) {
+            if (this.groups == null) {
+                this.groups = new HashSet<>();
+            }
+            this.groups.add(group);
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
index 40ced18..76e154a 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
@@ -20,6 +20,8 @@ import org.apache.commons.lang3.StringUtils;
 import 
org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
 import 
org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
 import 
org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -31,6 +33,8 @@ import java.util.regex.Pattern;
 
 public class CompositeUserGroupProvider implements UserGroupProvider {
 
+    private static final Logger logger = 
LoggerFactory.getLogger(CompositeUserGroupProvider.class);
+
     static final String PROP_USER_GROUP_PROVIDER_PREFIX = "User Group Provider 
";
     static final Pattern USER_GROUP_PROVIDER_PATTERN = 
Pattern.compile(PROP_USER_GROUP_PROVIDER_PREFIX + "\\S+");
 
@@ -142,33 +146,55 @@ public class CompositeUserGroupProvider implements 
UserGroupProvider {
 
     @Override
     public UserAndGroups getUserAndGroups(String identity) throws 
AuthorizationAccessException {
-        UserAndGroups userAndGroups = null;
 
+        // This method builds a UserAndGroups response by combining the data 
from all providers using a two-pass approach
+
+        CompositeUserAndGroups compositeUserAndGroups = new 
CompositeUserAndGroups();
+
+        // First pass - call getUserAndGroups(identity) on all providers, 
aggregate the responses, check for multiple
+        // user identity matches, which should not happen as identities should 
by globally unique.
+        String providerClassForUser = "";
         for (final UserGroupProvider userGroupProvider : userGroupProviders) {
-            userAndGroups = userGroupProvider.getUserAndGroups(identity);
+            UserAndGroups userAndGroups = 
userGroupProvider.getUserAndGroups(identity);
 
             if (userAndGroups.getUser() != null) {
-                break;
+                // is this the first match on the user?
+                if(compositeUserAndGroups.getUser() == null) {
+                    compositeUserAndGroups.setUser(userAndGroups.getUser());
+                    providerClassForUser = 
userGroupProvider.getClass().getName();
+                } else {
+                    logger.warn("Multiple UserGroupProviders are claiming to 
provide user '{}': [{} and {}] ",
+                            identity,
+                            userAndGroups.getUser(),
+                            providerClassForUser, 
userGroupProvider.getClass().getName());
+                    throw new IllegalStateException("Multiple 
UserGroupProviders are claiming to provide user " + identity);
+                }
+            }
+
+            if (userAndGroups.getGroups() != null) {
+                compositeUserAndGroups.addAllGroups(userAndGroups.getGroups());
             }
         }
 
-        if (userAndGroups == null) {
-            // per API, returning non null with null user/groups
-            return new UserAndGroups() {
-                @Override
-                public User getUser() {
-                    return null;
-                }
+        if (compositeUserAndGroups.getUser() == null) {
+            logger.debug("No user found for identity {}", identity);
+            return UserAndGroups.EMPTY;
+        }
 
-                @Override
-                public Set<Group> getGroups() {
-                    return null;
+        // Second pass - Now that we've matched a user, call getGroups() on 
all providers, and
+        // check all groups to see if they contain the user identifier 
corresponding to the identity.
+        // This is necessary because a provider might only know about a 
group<->userIdentifier mapping
+        // without knowing the user identifier.
+        String userIdentifier = 
compositeUserAndGroups.getUser().getIdentifier();
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            for (final Group group : userGroupProvider.getGroups()) {
+                if (group.getUsers() != null && 
group.getUsers().contains(userIdentifier)) {
+                    compositeUserAndGroups.addGroup(group);
                 }
-            };
-        } else {
-            // a delegated provider contained a matching user
-            return userAndGroups;
+            }
         }
+
+        return compositeUserAndGroups;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
index e12816a..4736fe2 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java
@@ -310,9 +310,6 @@ public class FileUserGroupProvider implements 
ConfigurableUserGroupProvider {
         final UserGroupHolder holder = userGroupHolder.get();
         final Tenants tenants = holder.getTenants();
 
-        // determine that all users in the group exist before doing anything, 
throw an exception if they don't
-        checkGroupUsers(group, tenants.getUsers().getUser());
-
         // create a new JAXB Group based on the incoming Group
         final 
org.apache.nifi.registry.security.authorization.file.tenants.generated.Group 
jaxbGroup =
                 new 
org.apache.nifi.registry.security.authorization.file.tenants.generated.Group();
@@ -593,27 +590,6 @@ public class FileUserGroupProvider implements 
ConfigurableUserGroupProvider {
         return jaxbUser;
     }
 
-    private 
Set<org.apache.nifi.registry.security.authorization.file.tenants.generated.User>
 checkGroupUsers(
-            final Group group,
-            final 
List<org.apache.nifi.registry.security.authorization.file.tenants.generated.User>
 users) {
-        final 
Set<org.apache.nifi.registry.security.authorization.file.tenants.generated.User>
 jaxbUsers = new HashSet<>();
-        for (String groupUser : group.getUsers()) {
-            boolean found = false;
-            for 
(org.apache.nifi.registry.security.authorization.file.tenants.generated.User 
jaxbUser : users) {
-                if (jaxbUser.getIdentifier().equals(groupUser)) {
-                    jaxbUsers.add(jaxbUser);
-                    found = true;
-                    break;
-                }
-            }
-
-            if (!found) {
-                throw new IllegalStateException("Unable to add group because 
user " + groupUser + " does not exist");
-            }
-        }
-        return jaxbUsers;
-    }
-
     /**
      * Loads the authorizations file and populates the AuthorizationsHolder, 
only called during start-up.
      *
@@ -632,13 +608,8 @@ public class FileUserGroupProvider implements 
ConfigurableUserGroupProvider {
 
         final UserGroupHolder userGroupHolder = new UserGroupHolder(tenants);
         final boolean emptyTenants = userGroupHolder.getAllUsers().isEmpty() 
&& userGroupHolder.getAllGroups().isEmpty();
-//        final boolean hasLegacyAuthorizedUsers = (legacyAuthorizedUsersFile 
!= null && !StringUtils.isBlank(legacyAuthorizedUsersFile));
 
         if (emptyTenants) {
-//            if (hasLegacyAuthorizedUsers) {
-//                logger.info("Loading users from legacy model " + 
legacyAuthorizedUsersFile + " into new users file.");
-//                convertLegacyAuthorizedUsers(tenants);
-//            }
 
             populateInitialUsers(tenants);
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index 5543c7e..219885c 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -16,16 +16,16 @@
  */
 package org.apache.nifi.registry.service;
 
-import org.apache.nifi.registry.bucket.Bucket;
 import org.apache.nifi.registry.authorization.AccessPolicy;
 import org.apache.nifi.registry.authorization.AccessPolicySummary;
 import org.apache.nifi.registry.authorization.CurrentUser;
 import org.apache.nifi.registry.authorization.Permissions;
 import org.apache.nifi.registry.authorization.Resource;
-import org.apache.nifi.registry.authorization.Tenant;
 import org.apache.nifi.registry.authorization.ResourcePermissions;
+import org.apache.nifi.registry.authorization.Tenant;
 import org.apache.nifi.registry.authorization.User;
 import org.apache.nifi.registry.authorization.UserGroup;
+import org.apache.nifi.registry.bucket.Bucket;
 import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
 import 
org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext;
 import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
@@ -622,6 +622,7 @@ public class AuthorizationService {
             return null;
         }
         Tenant tenantDTO = new Tenant(user.getIdentifier(), 
user.getIdentity());
+        
tenantDTO.setConfigurable(AuthorizerCapabilityDetection.isUserConfigurable(authorizer,
 user));
         return tenantDTO;
     }
 
@@ -630,6 +631,7 @@ public class AuthorizationService {
             return null;
         }
         Tenant tenantDTO = new Tenant(group.getIdentifier(), group.getName());
+        
tenantDTO.setConfigurable(AuthorizerCapabilityDetection.isGroupConfigurable(authorizer,
 group));
         return tenantDTO;
     }
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-resources/src/main/resources/conf/authorizers.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-resources/src/main/resources/conf/authorizers.xml 
b/nifi-registry-resources/src/main/resources/conf/authorizers.xml
index f447714..af0c531 100644
--- a/nifi-registry-resources/src/main/resources/conf/authorizers.xml
+++ b/nifi-registry-resources/src/main/resources/conf/authorizers.xml
@@ -182,7 +182,7 @@
     <!-- To enable the composite-configurable-user-group-provider remove 2 
lines. This is 1 of 2.
     <userGroupProvider>
         <identifier>composite-configurable-user-group-provider</identifier>
-        
<class>org.apache.nifi.authorization.CompositeConfigurableUserGroupProvider</class>
+        
<class>org.apache.nifi.registry.security.authorization.CompositeConfigurableUserGroupProvider</class>
         <property name="Configurable User Group 
Provider">file-user-group-provider</property>
         <property name="User Group Provider 1"></property>
     </userGroupProvider>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java
 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java
index c9cd0aa..6776592 100644
--- 
a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java
+++ 
b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java
@@ -24,6 +24,21 @@ import java.util.Set;
 public interface UserAndGroups {
 
     /**
+     * A static, immutable, empty implementation.
+     */
+    UserAndGroups EMPTY = new UserAndGroups() {
+        @Override
+        public User getUser() {
+            return null;
+        }
+
+        @Override
+        public Set<Group> getGroups() {
+            return null;
+        }
+    };
+
+    /**
      * Retrieves the user, or null if the user is unknown
      *
      * @return the user with the given identity

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
index 67c45d8..ff6a218 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
@@ -129,7 +129,7 @@ public class IdentityAuthenticationProvider implements 
AuthenticationProvider {
             final Set<Group> userGroups = userAndGroups.getGroups();
 
             if (userGroups == null || userGroups.isEmpty()) {
-                return Collections.EMPTY_SET;
+                return Collections.emptySet();
             } else {
                 return 
userAndGroups.getGroups().stream().map(Group::getName).collect(Collectors.toSet());
             }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml 
b/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml
index e20a22d..97a0bb2 100644
--- a/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml
+++ b/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml
@@ -62,7 +62,7 @@
     <!-- To enable the composite-user-group-provider remove 2 lines. This is 1 
of 2.
     <userGroupProvider>
         <identifier>composite-user-group-provider</identifier>
-        <class>org.apache.nifi.authorization.CompositeUserGroupProvider</class>
+        
<class>org.apache.nifi.registry.security.authorization.CompositeUserGroupProvider</class>
         <property name="User Group Provider 1"></property>
     </userGroupProvider>
     To enable the composite-user-group-provider remove 2 lines. This is 2 of 
2. -->
@@ -84,7 +84,7 @@
     <!-- To enable the composite-configurable-user-group-provider remove 2 
lines. This is 1 of 2.
     <userGroupProvider>
         <identifier>composite-configurable-user-group-provider</identifier>
-        
<class>org.apache.nifi.authorization.CompositeConfigurableUserGroupProvider</class>
+        
<class>org.apache.nifi.registry.security.authorization.CompositeConfigurableUserGroupProvider</class>
         <property name="Configurable User Group 
Provider">file-user-group-provider</property>
         <property name="User Group Provider 1"></property>
     </userGroupProvider>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml 
b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml
index 0a7fd4d..c55e5a2 100644
--- a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml
+++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml
@@ -164,7 +164,7 @@
     <!-- To enable the composite-user-group-provider remove 2 lines. This is 1 
of 2.
     <userGroupProvider>
         <identifier>composite-user-group-provider</identifier>
-        <class>org.apache.nifi.authorization.CompositeUserGroupProvider</class>
+        
<class>org.apache.nifi.registry.security.authorization.CompositeUserGroupProvider</class>
         <property name="User Group Provider 1"></property>
     </userGroupProvider>
     To enable the composite-user-group-provider remove 2 lines. This is 2 of 
2. -->
@@ -186,7 +186,7 @@
     <!-- To enable the composite-configurable-user-group-provider remove 2 
lines. This is 1 of 2.
     <userGroupProvider>
         <identifier>composite-configurable-user-group-provider</identifier>
-        
<class>org.apache.nifi.authorization.CompositeConfigurableUserGroupProvider</class>
+        
<class>org.apache.nifi.registry.security.authorization.CompositeConfigurableUserGroupProvider</class>
         <property name="Configurable User Group 
Provider">file-user-group-provider</property>
         <property name="User Group Provider 1"></property>
     </userGroupProvider>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.html
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.html
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.html
index 9003b85..a7d6280 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.html
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.html
@@ -104,7 +104,7 @@ limitations under the License.
                           
[checked]="nfRegistryService.group.resourcePermissions.policies.canRead && 
nfRegistryService.group.resourcePermissions.policies.canWrite && 
nfRegistryService.group.resourcePermissions.policies.canDelete"
                           
(change)="toggleGroupManagePoliciesPrivileges($event)">
             <span class="description">Can manage policies<i
-                    matTooltip="Additional permissions that allow a user to 
manage or access certain aspects of the registry."
+                    matTooltip="Allow a user to grant all registry users read, 
write, and delete permission to a bucket."
                     class="pad-left-sm fa fa-question-circle-o 
help-icon"></i></span>
             </mat-checkbox>
             <div flex fxLayout="row" fxLayoutAlign="space-around center">
@@ -182,7 +182,7 @@ limitations under the License.
                         <div class="td-data-table-cell">
                             <div>
                                 <button 
(click)="removeUserFromGroup(row);row.checked = !row.checked;"
-                                        
[disabled]="!nfRegistryService.group.configurable"
+                                        
[disabled]="!nfRegistryService.currentUser.resourcePermissions.tenants.canWrite 
|| !nfRegistryService.group.configurable"
                                         matTooltip="'Remove user from group'" 
mat-icon-button color="accent">
                                     <i class="fa fa-trash" 
aria-hidden="true"></i>
                                 </button>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.html
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.html
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.html
index dcefc76..62fd591 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.html
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.html
@@ -188,7 +188,7 @@ limitations under the License.
                         <div class="td-data-table-cell">
                             <div>
                                 <button 
(click)="removeUserFromGroup(row);row.checked = !row.checked;"
-                                        [disabled]="!row.configurable"
+                                        
[disabled]="!nfRegistryService.currentUser.resourcePermissions.tenants.canWrite 
|| row.configurable === false"
                                         matTooltip="'Remove user from group'" 
mat-icon-button color="accent">
                                     <i class="fa fa-trash" 
aria-hidden="true"></i>
                                 </button>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
index 2444254..9abc043 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
@@ -499,27 +499,31 @@ NfRegistryManageUser.prototype = {
      */
     removeUserFromGroup: function (group) {
         var self = this;
-        var userGroups = 
this.nfRegistryService.user.userGroups.filter(function (userGroup) {
-            if (userGroup.identifier !== group.identifier) {
-                return userGroup;
-            }
-        });
-
-        this.nfRegistryApi.updateUserGroup(group.identifier, group.identity, 
userGroups).subscribe(function (response) {
-            self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier)
-                .subscribe(function (response) {
-                    self.nfRegistryService.user = response;
-                    self.filterGroups();
+        this.nfRegistryApi.getUserGroup(group.identifier).subscribe(function 
(response) {
+            if (!response.error) {
+                var fullGroup = response;
+                var users = fullGroup.users.filter(function (user) {
+                    if (self.nfRegistryService.user.identifier !== 
user.identifier) {
+                        return user;
+                    }
+                })
+                self.nfRegistryApi.updateUserGroup(fullGroup.identifier, 
fullGroup.identity, users).subscribe(function (response) {
+                    
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier)
+                        .subscribe(function (response) {
+                            self.nfRegistryService.user = response;
+                            self.filterGroups();
+                        });
+                    var snackBarRef = self.snackBarService.openCoaster({
+                        title: 'Success',
+                        message: 'This user has been removed from the ' + 
group.identity + ' group.',
+                        verticalPosition: 'bottom',
+                        horizontalPosition: 'right',
+                        icon: 'fa fa-check-circle-o',
+                        color: '#1EB475',
+                        duration: 3000
+                    });
                 });
-            var snackBarRef = self.snackBarService.openCoaster({
-                title: 'Success',
-                message: 'This user has been removed from the ' + 
group.identity + ' group.',
-                verticalPosition: 'bottom',
-                horizontalPosition: 'right',
-                icon: 'fa fa-check-circle-o',
-                color: '#1EB475',
-                duration: 3000
-            });
+            }
         });
     },
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.html
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.html
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.html
index d4da24e..f23b4da 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.html
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.html
@@ -83,7 +83,7 @@ limitations under the License.
                             <i class="fa fa-pencil" aria-hidden="true"></i>
                         </button>
                         <button 
(click)="removePolicyFromBucket(row);row.checked = !row.checked;"
-                                
[disabled]="!nfRegistryService.currentUser.resourcePermissions.policies.canDelete"
+                                
[disabled]="!nfRegistryService.currentUser.resourcePermissions.policies.canWrite"
                                 matTooltip="'Remove group policies from 
bucket'" mat-icon-button color="accent">
                             <i class="fa fa-trash" aria-hidden="true"></i>
                         </button>
@@ -107,7 +107,7 @@ limitations under the License.
                         </button>
                         <button 
(click)="removePolicyFromBucket(row);row.checked = !row.checked;"
                                 matTooltip="'Remove user policies from this 
bucket'" mat-icon-button color="accent"
-                                
[disabled]="!nfRegistryService.currentUser.resourcePermissions.policies.canDelete">
+                                
[disabled]="!nfRegistryService.currentUser.resourcePermissions.policies.canWrite">
                             <i class="fa fa-trash" aria-hidden="true"></i>
                         </button>
                     </div>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
----------------------------------------------------------------------
diff --git a/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js 
b/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
index 9714a44..04ec818 100644
--- a/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
+++ b/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
@@ -746,6 +746,7 @@ NfRegistryApi.prototype = {
             .catch(function (error) {
                 // there is no anonymous access and we don't know this user - 
open the login page which handles login/registration/etc
                 if (error.status === 401) {
+                    self.nfStorage.removeItem('jwt');
                     self.router.navigateByUrl('/nifi-registry/login');
                 }
                 return rxjs.Observable.of({

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/a94b8174/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js 
b/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
index 3de0ffb..77597fb 100644
--- a/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
+++ b/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
@@ -35,6 +35,7 @@ var rxjs = require('rxjs/Observable');
  * @constructor
  */
 function NfRegistryService(nfRegistryApi, nfStorage, tdDataTableService, 
router, fdsDialogService, fdsSnackBarService) {
+    var self = this;
     this.registry = {
         name: "NiFi Registry"
     };
@@ -163,7 +164,7 @@ function NfRegistryService(nfRegistryApi, nfStorage, 
tdDataTableService, router,
             icon: 'fa fa-trash',
             tooltip: 'Delete User',
             disabled: function(row) {
-                return (!row.configurable);
+                return (!self.currentUser.resourcePermissions.tenants.canWrite 
|| !row.configurable);
             }
         }
     ];
@@ -181,7 +182,7 @@ function NfRegistryService(nfRegistryApi, nfStorage, 
tdDataTableService, router,
             icon: 'fa fa-trash',
             tooltip: 'Delete User Group',
             disabled: function(row) {
-                return (!row.configurable);
+                return (!self.currentUser.resourcePermissions.tenants.canWrite 
|| !row.configurable);
             }
         }
     ];

Reply via email to