nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bbe...@apache.org
Subject nifi-registry git commit: NIFIREG-75 Fix User Group Data Integrity Checks
Date Fri, 22 Dec 2017 18:47:30 GMT
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 <bbende@apache.org>


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 <kdoran.apache@gmail.com>
Authored: Wed Dec 20 20:57:20 2017 -0500
Committer: Bryan Bende <bbende@apache.org>
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);
             }
         }
     ];


Mime
View raw message