You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2017/06/09 17:55:10 UTC

[03/11] nifi git commit: NIFI-3653: - Introducing UserGroup and Policy provider interfaces. - Introducing FileUserGroupProvider and FileAccessPolicyProvider. - Refactoring FileAuthorizer to utilize the file based implementations. - Introducing the Standa

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMerger.java
new file mode 100644
index 0000000..4b2406a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMerger.java
@@ -0,0 +1,61 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.AccessPolicyEntityMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.AccessPolicyEntity;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class AccessPolicyEndpointMerger extends AbstractSingleEntityEndpoint<AccessPolicyEntity> implements EndpointResponseMerger {
+    public static final Pattern ACCESS_POLICIES_URI_PATTERN = Pattern.compile("/nifi-api/policies");
+    public static final Pattern ACCESS_POLICY_URI_PATTERN = Pattern.compile("/nifi-api/policies/[a-f0-9\\-]{36}");
+    public static final Pattern ACCESS_POLICY_LOOKUP_URI_PATTERN = Pattern.compile("/nifi-api/policies/(?:read|write)/(?:[\\w-]+?/?)+");
+    private final AccessPolicyEntityMerger accessPolicyEntityMerger = new AccessPolicyEntityMerger();
+
+    @Override
+    public boolean canHandle(final URI uri, final String method) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (ACCESS_POLICY_URI_PATTERN.matcher(uri.getPath()).matches())) {
+            return true;
+        } else if ("GET".equalsIgnoreCase(method) && ACCESS_POLICY_LOOKUP_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        } else if ("POST".equalsIgnoreCase(method) && ACCESS_POLICIES_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected Class<AccessPolicyEntity> getEntityClass() {
+        return AccessPolicyEntity.class;
+    }
+
+    @Override
+    protected void mergeResponses(final AccessPolicyEntity clientEntity, final Map<NodeIdentifier, AccessPolicyEntity> entityMap,
+                                  final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
+
+        accessPolicyEntityMerger.merge(clientEntity, entityMap);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/SearchUsersEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/SearchUsersEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/SearchUsersEndpointMerger.java
new file mode 100644
index 0000000..31996cb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/SearchUsersEndpointMerger.java
@@ -0,0 +1,59 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.web.api.entity.TenantEntity;
+import org.apache.nifi.web.api.entity.TenantsEntity;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class SearchUsersEndpointMerger implements EndpointResponseMerger {
+    public static final Pattern SEARCH_TENANTS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/search-results");
+
+    @Override
+    public boolean canHandle(final URI uri, final String method) {
+        return "GET".equalsIgnoreCase(method) && SEARCH_TENANTS_URI_PATTERN.matcher(uri.getPath()).matches();
+    }
+
+    @Override
+    public final NodeResponse merge(final URI uri, final String method, final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses, final NodeResponse clientResponse) {
+        if (!canHandle(uri, method)) {
+            throw new IllegalArgumentException("Cannot use Endpoint Mapper of type " + getClass().getSimpleName() + " to map responses for URI " + uri + ", HTTP Method " + method);
+        }
+
+        final TenantsEntity responseEntity = clientResponse.getClientResponse().getEntity(TenantsEntity.class);
+        final Collection<TenantEntity> userEntities = responseEntity.getUsers();
+        final Collection<TenantEntity> userGroupEntities = responseEntity.getUserGroups();
+
+        for (final NodeResponse nodeResponse : successfulResponses) {
+            final TenantsEntity nodeResponseEntity = nodeResponse == clientResponse ? responseEntity : nodeResponse.getClientResponse().getEntity(TenantsEntity.class);
+
+            // only retain users/groups that all nodes agree on
+            userEntities.retainAll(nodeResponseEntity.getUsers());
+            userGroupEntities.retainAll(nodeResponseEntity.getUserGroups());
+        }
+
+        // create a new client response
+        return new NodeResponse(clientResponse, responseEntity);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserEndpointMerger.java
new file mode 100644
index 0000000..afac159
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserEndpointMerger.java
@@ -0,0 +1,58 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.UserEntityMerger;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.UserEntity;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class UserEndpointMerger extends AbstractSingleEntityEndpoint<UserEntity> implements EndpointResponseMerger {
+    public static final Pattern USERS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/users");
+    public static final Pattern USER_URI_PATTERN = Pattern.compile("/nifi-api/tenants/users/[a-f0-9\\-]{36}");
+    private final UserEntityMerger userEntityMerger = new UserEntityMerger();
+
+    @Override
+    public boolean canHandle(final URI uri, final String method) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (USER_URI_PATTERN.matcher(uri.getPath()).matches())) {
+            return true;
+        } else if ("POST".equalsIgnoreCase(method) && USERS_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected Class<UserEntity> getEntityClass() {
+        return UserEntity.class;
+    }
+
+    @Override
+    protected void mergeResponses(final UserEntity clientEntity, final Map<NodeIdentifier, UserEntity> entityMap,
+                                  final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
+
+        userEntityMerger.merge(clientEntity, entityMap);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupEndpointMerger.java
new file mode 100644
index 0000000..ca2d6e1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupEndpointMerger.java
@@ -0,0 +1,58 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.UserGroupEntityMerger;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.UserGroupEntity;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class UserGroupEndpointMerger extends AbstractSingleEntityEndpoint<UserGroupEntity> implements EndpointResponseMerger {
+    public static final Pattern USER_GROUPS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/user-groups");
+    public static final Pattern USER_GROUP_URI_PATTERN = Pattern.compile("/nifi-api/tenants/user-groups/[a-f0-9\\-]{36}");
+    private final UserGroupEntityMerger userGroupEntityMerger = new UserGroupEntityMerger();
+
+    @Override
+    public boolean canHandle(final URI uri, final String method) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (USER_GROUP_URI_PATTERN.matcher(uri.getPath()).matches())) {
+            return true;
+        } else if ("POST".equalsIgnoreCase(method) && USER_GROUPS_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected Class<UserGroupEntity> getEntityClass() {
+        return UserGroupEntity.class;
+    }
+
+    @Override
+    protected void mergeResponses(final UserGroupEntity clientEntity, final Map<NodeIdentifier, UserGroupEntity> entityMap,
+                                  final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
+
+        userGroupEntityMerger.merge(clientEntity, entityMap);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupsEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupsEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupsEndpointMerger.java
new file mode 100644
index 0000000..3b18814
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UserGroupsEndpointMerger.java
@@ -0,0 +1,76 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.UserGroupsEntityMerger;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.UserGroupEntity;
+import org.apache.nifi.web.api.entity.UserGroupsEntity;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class UserGroupsEndpointMerger implements EndpointResponseMerger {
+    public static final Pattern USER_GROUPS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/user-groups");
+
+    @Override
+    public boolean canHandle(final URI uri, final String method) {
+        return "GET".equalsIgnoreCase(method) && USER_GROUPS_URI_PATTERN.matcher(uri.getPath()).matches();
+    }
+
+    @Override
+    public final NodeResponse merge(final URI uri, final String method, final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses, final NodeResponse clientResponse) {
+        if (!canHandle(uri, method)) {
+            throw new IllegalArgumentException("Cannot use Endpoint Mapper of type " + getClass().getSimpleName() + " to map responses for URI " + uri + ", HTTP Method " + method);
+        }
+
+        final UserGroupsEntity responseEntity = clientResponse.getClientResponse().getEntity(UserGroupsEntity.class);
+        final Collection<UserGroupEntity> userGroupEntities = responseEntity.getUserGroups();
+
+        final Map<String, Map<NodeIdentifier, UserGroupEntity>> entityMap = new HashMap<>();
+        for (final NodeResponse nodeResponse : successfulResponses) {
+            final UserGroupsEntity nodeResponseEntity = nodeResponse == clientResponse ? responseEntity : nodeResponse.getClientResponse().getEntity(UserGroupsEntity.class);
+            final Collection<UserGroupEntity> nodeUserGroupEntities = nodeResponseEntity.getUserGroups();
+
+            // only retain user groups that all nodes agree on
+            userGroupEntities.retainAll(nodeUserGroupEntities);
+
+            for (final UserGroupEntity nodeUserGroupEntity : nodeUserGroupEntities) {
+                final NodeIdentifier nodeId = nodeResponse.getNodeId();
+                Map<NodeIdentifier, UserGroupEntity> innerMap = entityMap.get(nodeId);
+                if (innerMap == null) {
+                    innerMap = new HashMap<>();
+                    entityMap.put(nodeUserGroupEntity.getId(), innerMap);
+                }
+
+                innerMap.put(nodeResponse.getNodeId(), nodeUserGroupEntity);
+            }
+        }
+
+        UserGroupsEntityMerger.mergeUserGroups(userGroupEntities, entityMap);
+
+        // create a new client response
+        return new NodeResponse(clientResponse, responseEntity);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UsersEndpointMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UsersEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UsersEndpointMerger.java
new file mode 100644
index 0000000..581b359
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/UsersEndpointMerger.java
@@ -0,0 +1,76 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.UsersEntityMerger;
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.UserEntity;
+import org.apache.nifi.web.api.entity.UsersEntity;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class UsersEndpointMerger implements EndpointResponseMerger {
+    public static final Pattern TENANTS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/users");
+
+    @Override
+    public boolean canHandle(final URI uri, final String method) {
+        return "GET".equalsIgnoreCase(method) && TENANTS_URI_PATTERN.matcher(uri.getPath()).matches();
+    }
+
+    @Override
+    public final NodeResponse merge(final URI uri, final String method, final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses, final NodeResponse clientResponse) {
+        if (!canHandle(uri, method)) {
+            throw new IllegalArgumentException("Cannot use Endpoint Mapper of type " + getClass().getSimpleName() + " to map responses for URI " + uri + ", HTTP Method " + method);
+        }
+
+        final UsersEntity responseEntity = clientResponse.getClientResponse().getEntity(UsersEntity.class);
+        final Collection<UserEntity> userEntities = responseEntity.getUsers();
+
+        final Map<String, Map<NodeIdentifier, UserEntity>> entityMap = new HashMap<>();
+        for (final NodeResponse nodeResponse : successfulResponses) {
+            final UsersEntity nodeResponseEntity = nodeResponse == clientResponse ? responseEntity : nodeResponse.getClientResponse().getEntity(UsersEntity.class);
+            final Collection<UserEntity> nodeUserEntities = nodeResponseEntity.getUsers();
+
+            // only retain users that all nodes agree on
+            userEntities.retainAll(nodeUserEntities);
+
+            for (final UserEntity nodeUserEntity : nodeUserEntities) {
+                final NodeIdentifier nodeId = nodeResponse.getNodeId();
+                Map<NodeIdentifier, UserEntity> innerMap = entityMap.get(nodeId);
+                if (innerMap == null) {
+                    innerMap = new HashMap<>();
+                    entityMap.put(nodeUserEntity.getId(), innerMap);
+                }
+
+                innerMap.put(nodeResponse.getNodeId(), nodeUserEntity);
+            }
+        }
+
+        UsersEntityMerger.mergeUsers(userEntities, entityMap);
+
+        // create a new client response
+        return new NodeResponse(clientResponse, responseEntity);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMerger.java
new file mode 100644
index 0000000..88a0faf
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMerger.java
@@ -0,0 +1,74 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.AccessPolicyDTO;
+import org.apache.nifi.web.api.entity.AccessPolicyEntity;
+import org.apache.nifi.web.api.entity.TenantEntity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class AccessPolicyEntityMerger implements ComponentEntityMerger<AccessPolicyEntity> {
+    @Override
+    public void merge(AccessPolicyEntity clientEntity, Map<NodeIdentifier, AccessPolicyEntity> entityMap) {
+        ComponentEntityMerger.super.merge(clientEntity, entityMap);
+    }
+
+    /**
+     * Merges the AccessPolicyEntity responses.
+     *
+     * @param clientEntity the entity being returned to the client
+     * @param entityMap all node responses
+     */
+    public void mergeComponents(final AccessPolicyEntity clientEntity, final Map<NodeIdentifier, AccessPolicyEntity> entityMap) {
+        final AccessPolicyDTO clientDto = clientEntity.getComponent();
+        final Map<NodeIdentifier, AccessPolicyDTO> dtoMap = new HashMap<>();
+        for (final Map.Entry<NodeIdentifier, AccessPolicyEntity> entry : entityMap.entrySet()) {
+            final AccessPolicyEntity nodeAccessPolicyEntity = entry.getValue();
+            final AccessPolicyDTO nodeAccessPolicyDto = nodeAccessPolicyEntity.getComponent();
+            dtoMap.put(entry.getKey(), nodeAccessPolicyDto);
+        }
+
+        mergeDtos(clientDto, dtoMap);
+    }
+
+    private static void mergeDtos(final AccessPolicyDTO clientDto, final Map<NodeIdentifier, AccessPolicyDTO> dtoMap) {
+        // if unauthorized for the client dto, simple return
+        if (clientDto == null) {
+            return;
+        }
+
+        final Set<TenantEntity> users = new HashSet<>(clientDto.getUsers());
+        final Set<TenantEntity> userGroups = new HashSet<>(clientDto.getUserGroups());
+
+        for (final Map.Entry<NodeIdentifier, AccessPolicyDTO> nodeEntry : dtoMap.entrySet()) {
+            final AccessPolicyDTO nodeAccessPolicy = nodeEntry.getValue();
+
+            if (nodeAccessPolicy != null) {
+                users.retainAll(nodeAccessPolicy.getUsers());
+                userGroups.retainAll(nodeAccessPolicy.getUserGroups());
+            }
+        }
+
+        clientDto.setUsers(users);
+        clientDto.setUserGroups(userGroups);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserEntityMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserEntityMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserEntityMerger.java
new file mode 100644
index 0000000..e1e3fcc
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserEntityMerger.java
@@ -0,0 +1,76 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.UserDTO;
+import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
+import org.apache.nifi.web.api.entity.TenantEntity;
+import org.apache.nifi.web.api.entity.UserEntity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class UserEntityMerger implements ComponentEntityMerger<UserEntity> {
+
+    @Override
+    public void merge(UserEntity clientEntity, Map<NodeIdentifier, UserEntity> entityMap) {
+        ComponentEntityMerger.super.merge(clientEntity, entityMap);
+    }
+
+    /**
+     * Merges the UserEntity responses.
+     *
+     * @param clientEntity the entity being returned to the client
+     * @param entityMap all node responses
+     */
+    public void mergeComponents(final UserEntity clientEntity, final Map<NodeIdentifier, UserEntity> entityMap) {
+        final UserDTO clientDto = clientEntity.getComponent();
+        final Map<NodeIdentifier, UserDTO> dtoMap = new HashMap<>();
+        for (final Map.Entry<NodeIdentifier, UserEntity> entry : entityMap.entrySet()) {
+            final UserEntity nodeUserEntity = entry.getValue();
+            final UserDTO nodeUserDto = nodeUserEntity.getComponent();
+            dtoMap.put(entry.getKey(), nodeUserDto);
+        }
+
+        mergeDtos(clientDto, dtoMap);
+    }
+
+    private static void mergeDtos(final UserDTO clientDto, final Map<NodeIdentifier, UserDTO> dtoMap) {
+        // if unauthorized for the client dto, simple return
+        if (clientDto == null) {
+            return;
+        }
+
+        final Set<AccessPolicySummaryEntity> accessPolicyEntities = new HashSet<>(clientDto.getAccessPolicies());
+        final Set<TenantEntity> tenantEntities = new HashSet<>(clientDto.getUserGroups());
+
+        for (final Map.Entry<NodeIdentifier, UserDTO> nodeEntry : dtoMap.entrySet()) {
+            final UserDTO nodeUser = nodeEntry.getValue();
+
+            if (nodeUser != null) {
+                accessPolicyEntities.retainAll(nodeUser.getAccessPolicies());
+                tenantEntities.retainAll(nodeUser.getUserGroups());
+            }
+        }
+
+        clientDto.setAccessPolicies(accessPolicyEntities);
+        clientDto.setUserGroups(tenantEntities);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupEntityMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupEntityMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupEntityMerger.java
new file mode 100644
index 0000000..8142b45
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupEntityMerger.java
@@ -0,0 +1,75 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.UserGroupDTO;
+import org.apache.nifi.web.api.entity.AccessPolicyEntity;
+import org.apache.nifi.web.api.entity.TenantEntity;
+import org.apache.nifi.web.api.entity.UserGroupEntity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class UserGroupEntityMerger implements ComponentEntityMerger<UserGroupEntity> {
+    @Override
+    public void merge(UserGroupEntity clientEntity, Map<NodeIdentifier, UserGroupEntity> entityMap) {
+        ComponentEntityMerger.super.merge(clientEntity, entityMap);
+    }
+
+    /**
+     * Merges the UserGroupEntity responses.
+     *
+     * @param clientEntity the entity being returned to the client
+     * @param entityMap all node responses
+     */
+    public void mergeComponents(final UserGroupEntity clientEntity, final Map<NodeIdentifier, UserGroupEntity> entityMap) {
+        final UserGroupDTO clientDto = clientEntity.getComponent();
+        final Map<NodeIdentifier, UserGroupDTO> dtoMap = new HashMap<>();
+        for (final Map.Entry<NodeIdentifier, UserGroupEntity> entry : entityMap.entrySet()) {
+            final UserGroupEntity nodeUserGroupEntity = entry.getValue();
+            final UserGroupDTO nodeUserGroupDto = nodeUserGroupEntity.getComponent();
+            dtoMap.put(entry.getKey(), nodeUserGroupDto);
+        }
+
+        mergeDtos(clientDto, dtoMap);
+    }
+
+    private static void mergeDtos(final UserGroupDTO clientDto, final Map<NodeIdentifier, UserGroupDTO> dtoMap) {
+        // if unauthorized for the client dto, simple return
+        if (clientDto == null) {
+            return;
+        }
+
+        final Set<AccessPolicyEntity> accessPolicyEntities = new HashSet<>(clientDto.getAccessPolicies());
+        final Set<TenantEntity> userEntities = new HashSet<>(clientDto.getUsers());
+
+        for (final Map.Entry<NodeIdentifier, UserGroupDTO> nodeEntry : dtoMap.entrySet()) {
+            final UserGroupDTO nodeUserGroup = nodeEntry.getValue();
+
+            if (nodeUserGroup != null) {
+                accessPolicyEntities.retainAll(nodeUserGroup.getAccessPolicies());
+                userEntities.retainAll(nodeUserGroup.getUsers());
+            }
+        }
+
+        clientDto.setAccessPolicies(accessPolicyEntities);
+        clientDto.setUsers(userEntities);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupsEntityMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupsEntityMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupsEntityMerger.java
new file mode 100644
index 0000000..6dbb0c6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UserGroupsEntityMerger.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.UserGroupEntity;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class UserGroupsEntityMerger implements ComponentEntityMerger<UserGroupEntity> {
+    private static final UserGroupEntityMerger userGroupEntityMerger = new UserGroupEntityMerger();
+
+    /**
+     * Merges multiple UserGroupEntity responses.
+     *
+     * @param userGroupEntities entities being returned to the client
+     * @param entityMap all node responses
+     */
+    public static void mergeUserGroups(final Collection<UserGroupEntity> userGroupEntities, final Map<String, Map<NodeIdentifier, UserGroupEntity>> entityMap) {
+        for (final UserGroupEntity entity : userGroupEntities) {
+            userGroupEntityMerger.merge(entity, entityMap.get(entity.getId()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UsersEntityMerger.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UsersEntityMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UsersEntityMerger.java
new file mode 100644
index 0000000..4874336
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/UsersEntityMerger.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.entity.UserEntity;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class UsersEntityMerger implements ComponentEntityMerger<UserEntity> {
+    private static final UserEntityMerger userEntityMerger = new UserEntityMerger();
+
+    /**
+     * Merges multiple UserEntity responses.
+     *
+     * @param userEntities entities being returned to the client
+     * @param entityMap all node responses
+     */
+    public static void mergeUsers(final Collection<UserEntity> userEntities, final Map<String, Map<NodeIdentifier, UserEntity>> entityMap) {
+        for (final UserEntity entity : userEntities) {
+            userEntityMerger.merge(entity, entityMap.get(entity.getId()));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMergerTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMergerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMergerTest.java
new file mode 100644
index 0000000..c8a06ed
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/endpoints/AccessPolicyEndpointMergerTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.cluster.coordination.http.endpoints;
+
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.UUID;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class AccessPolicyEndpointMergerTest {
+
+    @Test
+    public void testCanHandle() throws Exception {
+        final AccessPolicyEndpointMerger merger = new AccessPolicyEndpointMerger();
+        assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies"), "POST"));
+        assertFalse(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies"), "GET"));
+        assertFalse(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies"), "PUT"));
+        assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/" + UUID.randomUUID().toString()), "PUT"));
+        assertFalse(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/Read/flow"), "GET"));
+        assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/read/flow"), "GET"));
+        assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/read/processors/" + UUID.randomUUID().toString()), "GET"));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/replication/TestThreadPoolRequestReplicator.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/replication/TestThreadPoolRequestReplicator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/replication/TestThreadPoolRequestReplicator.java
index d90c49b..b5eff63 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/replication/TestThreadPoolRequestReplicator.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/coordination/http/replication/TestThreadPoolRequestReplicator.java
@@ -27,6 +27,7 @@ import org.apache.commons.collections4.map.MultiValueMap;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.authorization.user.NiFiUserDetails;
 import org.apache.nifi.authorization.user.StandardNiFiUser;
+import org.apache.nifi.authorization.user.StandardNiFiUser.Builder;
 import org.apache.nifi.cluster.coordination.ClusterCoordinator;
 import org.apache.nifi.cluster.coordination.node.NodeConnectionState;
 import org.apache.nifi.cluster.coordination.node.NodeConnectionStatus;
@@ -157,9 +158,9 @@ public class TestThreadPoolRequestReplicator {
             final Entity entity = new ProcessorEntity();
 
             // set the user
-            final NiFiUser proxy2 = new StandardNiFiUser(proxyIdentity2);
-            final NiFiUser proxy1 = new StandardNiFiUser(proxyIdentity1, proxy2);
-            final NiFiUser user = new StandardNiFiUser(userIdentity, proxy1);
+            final NiFiUser proxy2 = new Builder().identity(proxyIdentity2).build();
+            final NiFiUser proxy1 = new Builder().identity(proxyIdentity1).chain(proxy2).build();
+            final NiFiUser user = new Builder().identity(userIdentity).chain(proxy1).build();
             final Authentication authentication = new NiFiAuthenticationToken(new NiFiUserDetails(user));
             SecurityContextHolder.getContext().setAuthentication(authentication);
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMergerTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMergerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMergerTest.java
new file mode 100644
index 0000000..70a941a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/AccessPolicyEntityMergerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.AccessPolicyDTO;
+import org.apache.nifi.web.api.dto.PermissionsDTO;
+import org.apache.nifi.web.api.dto.TenantDTO;
+import org.apache.nifi.web.api.entity.AccessPolicyEntity;
+import org.apache.nifi.web.api.entity.TenantEntity;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class AccessPolicyEntityMergerTest {
+
+    @Test
+    public void testMergeAccessPolicy() throws Exception {
+        final NodeIdentifier node1 = new NodeIdentifier("node-1", "host-1", 8080, "host-1", 19998, null, null, null, false);
+        final NodeIdentifier node2 = new NodeIdentifier("node-2", "host-2", 8081, "host-2", 19999, null, null, null, false);
+
+        final PermissionsDTO permissed = new PermissionsDTO();
+        permissed.setCanRead(true);
+        permissed.setCanWrite(true);
+
+        final TenantDTO user1DTO = new TenantDTO();
+        user1DTO.setId("user-1");
+
+        final TenantEntity user1Entity = new TenantEntity();
+        user1Entity.setPermissions(permissed);
+        user1Entity.setId(user1DTO.getId());
+        user1Entity.setComponent(user1DTO);
+
+        final TenantDTO user2DTO = new TenantDTO();
+        user1DTO.setId("user-2");
+
+        final TenantEntity user2Entity = new TenantEntity();
+        user2Entity.setPermissions(permissed);
+        user2Entity.setId(user2DTO.getId());
+        user2Entity.setComponent(user2DTO);
+
+        final AccessPolicyDTO accessPolicy1DTO = new AccessPolicyDTO();
+        accessPolicy1DTO.setId("policy-1");
+        accessPolicy1DTO.setUsers(Stream.of(user1Entity, user2Entity).collect(Collectors.toSet()));
+        accessPolicy1DTO.setUserGroups(Stream.of(user2Entity).collect(Collectors.toSet()));
+
+        final AccessPolicyEntity accessPolicy1Entity = new AccessPolicyEntity();
+        accessPolicy1Entity.setPermissions(permissed);
+        accessPolicy1Entity.setId(accessPolicy1DTO.getId());
+        accessPolicy1Entity.setComponent(accessPolicy1DTO);
+
+        final AccessPolicyDTO accessPolicy2DTO = new AccessPolicyDTO();
+        accessPolicy2DTO.setId("policy-2");
+        accessPolicy2DTO.setUsers(Stream.of(user1Entity).collect(Collectors.toSet()));
+        accessPolicy2DTO.setUserGroups(Stream.of(user1Entity, user2Entity).collect(Collectors.toSet()));
+
+        final AccessPolicyEntity accessPolicy2Entity = new AccessPolicyEntity();
+        accessPolicy2Entity.setPermissions(permissed);
+        accessPolicy2Entity.setId(accessPolicy2DTO.getId());
+        accessPolicy2Entity.setComponent(accessPolicy2DTO);
+
+        final Map<NodeIdentifier, AccessPolicyEntity> nodeMap = new HashMap<>();
+        nodeMap.put(node1, accessPolicy1Entity);
+        nodeMap.put(node2, accessPolicy2Entity);
+
+        final AccessPolicyEntityMerger merger = new AccessPolicyEntityMerger();
+        merger.merge(accessPolicy1Entity, nodeMap);
+
+        assertEquals(1, accessPolicy1DTO.getUserGroups().size());
+        assertTrue(accessPolicy1DTO.getUsers().contains(user1Entity));
+
+        assertEquals(1, accessPolicy1DTO.getUserGroups().size());
+        assertTrue(accessPolicy1DTO.getUserGroups().contains(user2Entity));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserEntityMergerTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserEntityMergerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserEntityMergerTest.java
new file mode 100644
index 0000000..03db8b4
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserEntityMergerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.AccessPolicySummaryDTO;
+import org.apache.nifi.web.api.dto.PermissionsDTO;
+import org.apache.nifi.web.api.dto.TenantDTO;
+import org.apache.nifi.web.api.dto.UserDTO;
+import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
+import org.apache.nifi.web.api.entity.TenantEntity;
+import org.apache.nifi.web.api.entity.UserEntity;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class UserEntityMergerTest {
+
+    @Test
+    public void testMergeAccessPolicy() throws Exception {
+        final NodeIdentifier node1 = new NodeIdentifier("node-1", "host-1", 8080, "host-1", 19998, null, null, null, false);
+        final NodeIdentifier node2 = new NodeIdentifier("node-2", "host-2", 8081, "host-2", 19999, null, null, null, false);
+
+        final PermissionsDTO permissed = new PermissionsDTO();
+        permissed.setCanRead(true);
+        permissed.setCanWrite(true);
+
+        final TenantDTO userGroup1DTO = new TenantDTO();
+        userGroup1DTO.setId("user-group-1");
+
+        final TenantEntity userGroup1Entity = new TenantEntity();
+        userGroup1Entity.setPermissions(permissed);
+        userGroup1Entity.setId(userGroup1DTO.getId());
+        userGroup1Entity.setComponent(userGroup1DTO);
+
+        final TenantDTO userGroup2DTO = new TenantDTO();
+        userGroup1DTO.setId("user-group-2");
+
+        final TenantEntity userGroup2Entity = new TenantEntity();
+        userGroup2Entity.setPermissions(permissed);
+        userGroup2Entity.setId(userGroup2DTO.getId());
+        userGroup2Entity.setComponent(userGroup2DTO);
+
+        final AccessPolicySummaryDTO policy1DTO = new AccessPolicySummaryDTO();
+        policy1DTO.setId("policy-1");
+
+        final AccessPolicySummaryEntity policy1Entity = new AccessPolicySummaryEntity();
+        policy1Entity.setPermissions(permissed);
+        policy1Entity.setId(policy1DTO.getId());
+        policy1Entity.setComponent(policy1DTO);
+
+        final AccessPolicySummaryDTO policy2DTO = new AccessPolicySummaryDTO();
+        policy2DTO.setId("policy-2");
+
+        final AccessPolicySummaryEntity policy2Entity = new AccessPolicySummaryEntity();
+        policy2Entity.setPermissions(permissed);
+        policy2Entity.setId(policy2DTO.getId());
+        policy2Entity.setComponent(policy2DTO);
+
+        final UserDTO user1DTO = new UserDTO();
+        user1DTO.setId("user-1");
+        user1DTO.setAccessPolicies(Stream.of(policy1Entity, policy2Entity).collect(Collectors.toSet()));
+        user1DTO.setUserGroups(Stream.of(userGroup2Entity).collect(Collectors.toSet()));
+
+        final UserEntity user1Entity = new UserEntity();
+        user1Entity.setPermissions(permissed);
+        user1Entity.setId(user1DTO.getId());
+        user1Entity.setComponent(user1DTO);
+
+        final UserDTO user2DTO = new UserDTO();
+        user2DTO.setId("user-2");
+        user2DTO.setAccessPolicies(Stream.of(policy1Entity).collect(Collectors.toSet()));
+        user2DTO.setUserGroups(Stream.of(userGroup1Entity, userGroup2Entity).collect(Collectors.toSet()));
+
+        final UserEntity user2Entity = new UserEntity();
+        user2Entity.setPermissions(permissed);
+        user2Entity.setId(user2DTO.getId());
+        user2Entity.setComponent(user2DTO);
+
+        final Map<NodeIdentifier, UserEntity> nodeMap = new HashMap<>();
+        nodeMap.put(node1, user1Entity);
+        nodeMap.put(node2, user2Entity);
+
+        final UserEntityMerger merger = new UserEntityMerger();
+        merger.merge(user1Entity, nodeMap);
+
+        assertEquals(1, user1DTO.getUserGroups().size());
+        assertTrue(user1DTO.getAccessPolicies().contains(policy1Entity));
+
+        assertEquals(1, user1DTO.getUserGroups().size());
+        assertTrue(user1DTO.getUserGroups().contains(userGroup2Entity));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserGroupEntityMergerTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserGroupEntityMergerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserGroupEntityMergerTest.java
new file mode 100644
index 0000000..cc975cb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/java/org/apache/nifi/cluster/manager/UserGroupEntityMergerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.cluster.manager;
+
+import org.apache.nifi.cluster.protocol.NodeIdentifier;
+import org.apache.nifi.web.api.dto.AccessPolicyDTO;
+import org.apache.nifi.web.api.dto.PermissionsDTO;
+import org.apache.nifi.web.api.dto.TenantDTO;
+import org.apache.nifi.web.api.dto.UserGroupDTO;
+import org.apache.nifi.web.api.entity.AccessPolicyEntity;
+import org.apache.nifi.web.api.entity.TenantEntity;
+import org.apache.nifi.web.api.entity.UserGroupEntity;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class UserGroupEntityMergerTest {
+
+    @Test
+    public void testMergeAccessPolicy() throws Exception {
+        final NodeIdentifier node1 = new NodeIdentifier("node-1", "host-1", 8080, "host-1", 19998, null, null, null, false);
+        final NodeIdentifier node2 = new NodeIdentifier("node-2", "host-2", 8081, "host-2", 19999, null, null, null, false);
+
+        final PermissionsDTO permissed = new PermissionsDTO();
+        permissed.setCanRead(true);
+        permissed.setCanWrite(true);
+
+        final TenantDTO user1DTO = new TenantDTO();
+        user1DTO.setId("user-1");
+
+        final TenantEntity user1Entity = new TenantEntity();
+        user1Entity.setPermissions(permissed);
+        user1Entity.setId(user1DTO.getId());
+        user1Entity.setComponent(user1DTO);
+
+        final TenantDTO user2DTO = new TenantDTO();
+        user1DTO.setId("user-2");
+
+        final TenantEntity user2Entity = new TenantEntity();
+        user2Entity.setPermissions(permissed);
+        user2Entity.setId(user2DTO.getId());
+        user2Entity.setComponent(user2DTO);
+
+        final AccessPolicyDTO policy1DTO = new AccessPolicyDTO();
+        policy1DTO.setId("policy-1");
+
+        final AccessPolicyEntity policy1Entity = new AccessPolicyEntity();
+        policy1Entity.setPermissions(permissed);
+        policy1Entity.setId(policy1DTO.getId());
+        policy1Entity.setComponent(policy1DTO);
+
+        final AccessPolicyDTO policy2DTO = new AccessPolicyDTO();
+        policy2DTO.setId("policy-2");
+
+        final AccessPolicyEntity policy2Entity = new AccessPolicyEntity();
+        policy2Entity.setPermissions(permissed);
+        policy2Entity.setId(policy2DTO.getId());
+        policy2Entity.setComponent(policy2DTO);
+
+        final UserGroupDTO userGroup1DTO = new UserGroupDTO();
+        userGroup1DTO.setId("user-1");
+        userGroup1DTO.setAccessPolicies(Stream.of(policy1Entity, policy2Entity).collect(Collectors.toSet()));
+        userGroup1DTO.setUsers(Stream.of(user2Entity).collect(Collectors.toSet()));
+
+        final UserGroupEntity userGroup1Entity = new UserGroupEntity();
+        userGroup1Entity.setPermissions(permissed);
+        userGroup1Entity.setId(userGroup1DTO.getId());
+        userGroup1Entity.setComponent(userGroup1DTO);
+
+        final UserGroupDTO userGroup2DTO = new UserGroupDTO();
+        userGroup2DTO.setId("user-2");
+        userGroup2DTO.setAccessPolicies(Stream.of(policy1Entity).collect(Collectors.toSet()));
+        userGroup2DTO.setUsers(Stream.of(user1Entity, user2Entity).collect(Collectors.toSet()));
+
+        final UserGroupEntity userGroup2Entity = new UserGroupEntity();
+        userGroup2Entity.setPermissions(permissed);
+        userGroup2Entity.setId(userGroup2DTO.getId());
+        userGroup2Entity.setComponent(userGroup2DTO);
+
+        final Map<NodeIdentifier, UserGroupEntity> nodeMap = new HashMap<>();
+        nodeMap.put(node1, userGroup1Entity);
+        nodeMap.put(node2, userGroup2Entity);
+
+        final UserGroupEntityMerger merger = new UserGroupEntityMerger();
+        merger.merge(userGroup1Entity, nodeMap);
+
+        assertEquals(1, userGroup1DTO.getUsers().size());
+        assertTrue(userGroup1DTO.getAccessPolicies().contains(policy1Entity));
+
+        assertEquals(1, userGroup1DTO.getUsers().size());
+        assertTrue(userGroup1DTO.getUsers().contains(user2Entity));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/cluster/protocol/DataFlow.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/cluster/protocol/DataFlow.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/cluster/protocol/DataFlow.java
index 6e2b9fe..0317584 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/cluster/protocol/DataFlow.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/cluster/protocol/DataFlow.java
@@ -32,7 +32,7 @@ public interface DataFlow {
 
     /**
      * @return the raw byte array of the Authorizer's fingerprint,
-     *              null when not using a sub-class of AbstractPolicyBasedAuthorizer
+     *              null when not using a ManagedAuthorizer
      */
     public byte[] getAuthorizerFingerprint();
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
index 57522f5..9d00f49 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
@@ -188,6 +188,10 @@
             <version>4.3.1.201605051710-r</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-authorizer</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
index b2c1628..f2387c2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
@@ -17,8 +17,9 @@
 package org.apache.nifi.controller;
 
 import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
 import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
+import org.apache.nifi.authorization.ManagedAuthorizer;
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.cluster.ConnectionException;
 import org.apache.nifi.cluster.coordination.ClusterCoordinator;
@@ -572,8 +573,8 @@ public class StandardFlowService implements FlowService, ProtocolHandler {
     }
 
     private byte[] getAuthorizerFingerprint() {
-        final boolean isInternalAuthorizer = (authorizer instanceof AbstractPolicyBasedAuthorizer);
-        return isInternalAuthorizer ? ((AbstractPolicyBasedAuthorizer) authorizer).getFingerprint().getBytes(StandardCharsets.UTF_8) : null;
+        final boolean isInternalAuthorizer = AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer);
+        return isInternalAuthorizer ? ((ManagedAuthorizer) authorizer).getFingerprint().getBytes(StandardCharsets.UTF_8) : null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
index 6f1e8e1..01dd35e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
@@ -18,8 +18,10 @@ package org.apache.nifi.controller;
 
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
 import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
+import org.apache.nifi.authorization.ManagedAuthorizer;
+import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
 import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.cluster.protocol.DataFlow;
 import org.apache.nifi.cluster.protocol.StandardDataFlow;
@@ -224,15 +226,15 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
         logger.trace("Getting Authorizer fingerprint from controller");
 
         final byte[] existingAuthFingerprint;
-        final AbstractPolicyBasedAuthorizer policyBasedAuthorizer;
+        final ManagedAuthorizer managedAuthorizer;
         final Authorizer authorizer = controller.getAuthorizer();
 
-        if (authorizer instanceof AbstractPolicyBasedAuthorizer) {
-            policyBasedAuthorizer = (AbstractPolicyBasedAuthorizer) authorizer;
-            existingAuthFingerprint = policyBasedAuthorizer.getFingerprint().getBytes(StandardCharsets.UTF_8);
+        if (AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
+            managedAuthorizer = (ManagedAuthorizer) authorizer;
+            existingAuthFingerprint = managedAuthorizer.getFingerprint().getBytes(StandardCharsets.UTF_8);
         } else {
             existingAuthFingerprint = null;
-            policyBasedAuthorizer = null;
+            managedAuthorizer = null;
         }
 
         final Set<String> missingComponents = new HashSet<>();
@@ -249,7 +251,7 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
             if (existingFlowEmpty) {
                 configuration = parseFlowBytes(proposedFlow.getFlow());
                 if (configuration != null) {
-                    logger.trace("Checking bunde compatibility");
+                    logger.trace("Checking bundle compatibility");
                     checkBundleCompatibility(configuration);
                 }
             } else {
@@ -272,7 +274,7 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
 
         logger.trace("Checking authorizer inheritability");
 
-        final AuthorizerInheritability authInheritability = checkAuthorizerInheritability(existingDataFlow, proposedFlow);
+        final AuthorizerInheritability authInheritability = checkAuthorizerInheritability(authorizer, existingDataFlow, proposedFlow);
         if (!authInheritability.isInheritable() && authInheritability.getReason() != null) {
             throw new UninheritableFlowException("Proposed Authorizer is not inheritable by the flow controller because of Authorizer differences: " + authInheritability.getReason());
         }
@@ -415,10 +417,10 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
             }
 
             // if auths are inheritable and we have a policy based authorizer, then inherit
-            if (authInheritability.isInheritable() && policyBasedAuthorizer != null) {
+            if (authInheritability.isInheritable() && managedAuthorizer != null) {
                 logger.trace("Inheriting authorizations");
                 final String proposedAuthFingerprint = new String(proposedFlow.getAuthorizerFingerprint(), StandardCharsets.UTF_8);
-                policyBasedAuthorizer.inheritFingerprint(proposedAuthFingerprint);
+                managedAuthorizer.inheritFingerprint(proposedAuthFingerprint);
             }
 
             logger.debug("Finished syncing flows");
@@ -1391,7 +1393,7 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
      * @param proposedFlow the proposed DataFlow
      * @return the AuthorizerInheritability result
      */
-    public AuthorizerInheritability checkAuthorizerInheritability(final DataFlow existingFlow, final DataFlow proposedFlow) {
+    private AuthorizerInheritability checkAuthorizerInheritability(final Authorizer authorizer, final DataFlow existingFlow, final DataFlow proposedFlow) {
         final byte[] existing = existingFlow.getAuthorizerFingerprint();
         final byte[] proposed = proposedFlow.getAuthorizerFingerprint();
 
@@ -1414,15 +1416,20 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
 
         // both are internal, but not the same
         if (!Arrays.equals(existing, proposed)) {
-            final byte[] emptyAuthBytes = AbstractPolicyBasedAuthorizer.EMPTY_FINGERPRINT.getBytes(StandardCharsets.UTF_8);
+            if (AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
+                final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) authorizer;
 
-            // if current is empty then we can take all the proposed authorizations
-            // otherwise they are both internal authorizers and don't match so we can't proceed
-            if (Arrays.equals(emptyAuthBytes, existing)) {
-                return AuthorizerInheritability.inheritable();
+                try {
+                    // if the configurations are not equal, see if the manager indicates the proposed configuration is inheritable
+                    managedAuthorizer.checkInheritability(new String(proposed, StandardCharsets.UTF_8));
+                    return AuthorizerInheritability.inheritable();
+                } catch (final UninheritableAuthorizationsException e) {
+                    return AuthorizerInheritability.uninheritable("Proposed Authorizations do not match current Authorizations: " + e.getMessage());
+                }
             } else {
+                // should never hit since the existing is only null when authorizer is not managed
                 return AuthorizerInheritability.uninheritable(
-                        "Proposed Authorizations do not match current Authorizations");
+                        "Proposed Authorizations do not match current Authorizations and are not configured with an internal Authorizer");
             }
         }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
index 14d3dcc..8326889 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java
@@ -18,7 +18,9 @@ package org.apache.nifi.nar;
 
 import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading;
 import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authorization.AccessPolicyProvider;
 import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.UserGroupProvider;
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.components.ConfigurableComponent;
@@ -82,6 +84,8 @@ public class ExtensionManager {
         definitionMap.put(ReportingTask.class, new HashSet<>());
         definitionMap.put(ControllerService.class, new HashSet<>());
         definitionMap.put(Authorizer.class, new HashSet<>());
+        definitionMap.put(UserGroupProvider.class, new HashSet<>());
+        definitionMap.put(AccessPolicyProvider.class, new HashSet<>());
         definitionMap.put(LoginIdentityProvider.class, new HashSet<>());
         definitionMap.put(ProvenanceRepository.class, new HashSet<>());
         definitionMap.put(ComponentStatusRepository.class, new HashSet<>());

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
index e7faa02..0be99dc 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/NarThreadContextClassLoader.java
@@ -17,7 +17,9 @@
 package org.apache.nifi.nar;
 
 import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authorization.AccessPolicyProvider;
 import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.UserGroupProvider;
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.components.state.StateProvider;
@@ -64,6 +66,8 @@ public class NarThreadContextClassLoader extends URLClassLoader {
         narSpecificClasses.add(StreamCallback.class);
         narSpecificClasses.add(ControllerService.class);
         narSpecificClasses.add(Authorizer.class);
+        narSpecificClasses.add(UserGroupProvider.class);
+        narSpecificClasses.add(AccessPolicyProvider.class);
         narSpecificClasses.add(LoginIdentityProvider.class);
         narSpecificClasses.add(ProvenanceRepository.class);
         narSpecificClasses.add(ComponentStatusRepository.class);

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
index 1816297..8e740a9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
@@ -141,7 +141,7 @@
         <nifi.security.truststoreType />
         <nifi.security.truststorePasswd />
         <nifi.security.needClientAuth />
-        <nifi.security.user.authorizer>file-provider</nifi.security.user.authorizer>
+        <nifi.security.user.authorizer>managed-authorizer</nifi.security.user.authorizer>
         <nifi.security.user.login.identity.provider />
         <nifi.security.x509.principal.extractor />
         <nifi.security.ocsp.responder.url />

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/authorizers.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/authorizers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/authorizers.xml
index 46bf637..247c0e8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/authorizers.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/authorizers.xml
@@ -14,13 +14,105 @@
   limitations under the License.
 -->
 <!--
-    This file lists the authority providers to use when running securely. In order
-    to use a specific provider it must be configured here and it's identifier
-    must be specified in the nifi.properties file.
+    This file lists the userGroupProviders, accessPolicyProviders, and authorizers to use when running securely. In order
+    to use a specific authorizer it must be configured here and it's identifier must be specified in the nifi.properties file.
+    If the authorizer is a managedAuthorizer, it may need to be configured with an accessPolicyProvider and an userGroupProvider.
+    This file allows for configuration of them, but they must be configured in order:
+
+    ...
+    all userGroupProviders
+    all accessPolicyProviders
+    all Authorizers
+    ...
 -->
 <authorizers>
 
     <!--
+        The FileUserGroupProvider will provide support for managing users and groups which is backed by a file
+        on the local file system.
+
+        - Users File - The file where the FileUserGroupProvider will store users and groups.
+
+        - Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically
+            be used to load the users and groups into the Users File.
+
+        - Initial User Identity [unique key] - The identity of a users and systems to seed the Users File. The name of
+            each property must be unique, for example: "Initial User Identity A", "Initial User Identity B",
+            "Initial User Identity C" or "Initial User Identity 1", "Initial User Identity 2", "Initial User Identity 3"
+
+            NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the user identities,
+            so the values should be the unmapped identities (i.e. full DN from a certificate).
+    -->
+    <userGroupProvider>
+        <identifier>file-user-group-provider</identifier>
+        <class>org.apache.nifi.authorization.FileUserGroupProvider</class>
+        <property name="Users File">./conf/users.xml</property>
+        <property name="Legacy Authorized Users File"></property>
+
+        <property name="Initial User Identity 1"></property>
+    </userGroupProvider>
+
+    <!--
+        The FileAccessPolicyProvider will provide support for managing access policies which is backed by a file
+        on the local file system.
+
+        - User Group Provider - The identifier for an User Group Provider defined above that will be used to access
+            users and groups for use in the managed access policies.
+
+        - Authorizations File - The file where the FileAccessPolicyProvider will store policies.
+
+        - Initial Admin Identity - The identity of an initial admin user that will be granted access to the UI and
+            given the ability to create additional users, groups, and policies. The value of this property could be
+            a DN when using certificates or LDAP, or a Kerberos principal. This property will only be used when there
+            are no other policies defined. If this property is specified then a Legacy Authorized Users File can not be specified.
+
+            NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the initial admin identity,
+            so the value should be the unmapped identity. This identity must be found in the configured User Group Provider.
+
+        - Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically
+            converted to the new authorizations model. If this property is specified then an Initial Admin Identity can
+            not be specified, and this property will only be used when there are no other users, groups, and policies defined.
+
+            NOTE: Any users in the legacy users file must be found in the configured User Group Provider.
+
+        - Node Identity [unique key] - The identity of a NiFi cluster node. When clustered, a property for each node
+            should be defined, so that every node knows about every other node. If not clustered these properties can be ignored.
+            The name of each property must be unique, for example for a three node cluster:
+            "Node Identity A", "Node Identity B", "Node Identity C" or "Node Identity 1", "Node Identity 2", "Node Identity 3"
+
+            NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the node identities,
+            so the values should be the unmapped identities (i.e. full DN from a certificate). This identity must be found
+            in the configured User Group Provider.
+    -->
+    <accessPolicyProvider>
+        <identifier>file-access-policy-provider</identifier>
+        <class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
+        <property name="User Group Provider">file-user-group-provider</property>
+        <property name="Authorizations File">./conf/authorizations.xml</property>
+        <property name="Initial Admin Identity"></property>
+        <property name="Legacy Authorized Users File"></property>
+
+        <property name="Node Identity 1"></property>
+    </accessPolicyProvider>
+
+    <!--
+        The StandardManagedAuthorizer. This authorizer implementation must be configured with the
+        Access Policy Provider which it will use to access and manage users, groups, and policies.
+        These users, groups, and policies will be used to make all access decisions during authorization
+        requests.
+
+        - Access Policy Provider - The identifier for an Access Policy Provider defined above.
+    -->
+    <authorizer>
+        <identifier>managed-authorizer</identifier>
+        <class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
+        <property name="Access Policy Provider">file-access-policy-provider</property>
+    </authorizer>
+
+    <!--
+        NOTE: This Authorizer has been replaced with the more granular approach configured above with the Standard
+        Managed Authorizer. However, it is still available for backwards compatibility reasons.
+
         The FileAuthorizer is NiFi's provided authorizer and has the following properties:
 
         - Authorizations File - The file where the FileAuthorizer will store policies.
@@ -48,7 +140,7 @@
             NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the node identities,
             so the values should be the unmapped identities (i.e. full DN from a certificate).
     -->
-    <authorizer>
+    <!-- <authorizer>
         <identifier>file-provider</identifier>
         <class>org.apache.nifi.authorization.FileAuthorizer</class>
         <property name="Authorizations File">./conf/authorizations.xml</property>
@@ -56,9 +148,7 @@
         <property name="Initial Admin Identity"></property>
         <property name="Legacy Authorized Users File"></property>
 
-        <!-- Provide the identity (typically a DN) of each node when clustered, see above description of Node Identity.
         <property name="Node Identity 1"></property>
-        <property name="Node Identity 2"></property>
-        -->
     </authorizer>
+    -->
 </authorizers>
\ No newline at end of file