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:18 UTC

[11/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

NIFI-3653: - Introducing UserGroup and Policy provider interfaces.
- Introducing FileUserGroupProvider and FileAccessPolicyProvider.
- Refactoring FileAuthorizer to utilize the file based implementations.
- Introducing the StandardManagedAuthorizer.
- Decorating the configured ManagedAuthorizer to ensure integrity checks are still performed.
- Loading user groups if possible to use during access decisions.
- Merging responses for requests for AccessPolicies, Users, and UserGroups.
- Adding unit tests as appropriate.
- Adding methods to the User, Group, and AccessPolicy builder that more easily supports generating UUIDs.
- Fixing typo when seeding policies during startup.
- Fixing type in documentation and error messages.

This closes #1897.

Signed-off-by: Bryan Bende <bb...@apache.org>


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

Branch: refs/heads/master
Commit: 4ed7511bee11b319487d07b4bdcff3438a44b6dc
Parents: f447fc7
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri May 26 15:02:44 2017 -0400
Committer: Bryan Bende <bb...@apache.org>
Committed: Fri Jun 9 13:54:10 2017 -0400

----------------------------------------------------------------------
 .../src/main/asciidoc/administration-guide.adoc |  133 +-
 .../AbstractPolicyBasedAuthorizer.java          |  364 ++++--
 .../apache/nifi/authorization/AccessPolicy.java |   37 +
 .../authorization/AccessPolicyProvider.java     |   90 ++
 ...cessPolicyProviderInitializationContext.java |   30 +
 .../AccessPolicyProviderLookup.java             |   31 +
 .../authorization/AuthorizationRequest.java     |   19 +
 .../apache/nifi/authorization/Authorizer.java   |    4 +
 .../AuthorizerConfigurationContext.java         |    4 +-
 .../AuthorizerInitializationContext.java        |   11 +-
 .../ConfigurableAccessPolicyProvider.java       |   84 ++
 .../ConfigurableUserGroupProvider.java          |  115 ++
 .../org/apache/nifi/authorization/Group.java    |   37 +
 .../nifi/authorization/ManagedAuthorizer.java   |   59 +
 .../org/apache/nifi/authorization/User.java     |   37 +
 .../nifi/authorization/UserAndGroups.java       |   40 +
 .../nifi/authorization/UserGroupProvider.java   |  108 ++
 .../UserGroupProviderInitializationContext.java |   37 +
 .../authorization/UserGroupProviderLookup.java  |   31 +
 .../UninheritableAuthorizationsException.java   |   28 +
 .../authorization/resource/Authorizable.java    |    2 +
 .../nifi/authorization/user/NiFiUser.java       |    7 +
 .../TestAbstractPolicyBasedAuthorizer.java      |  211 ----
 .../nifi-framework/nifi-authorizer/pom.xml      |   18 +-
 .../AccessPolicyProviderFactory.java            |  179 +++
 .../AuthorizerCapabilityDetection.java          |   45 +
 .../nifi/authorization/AuthorizerFactory.java   |  426 +++++++
 .../authorization/AuthorizerFactoryBean.java    |  332 +++--
 .../authorization/UserGroupProviderFactory.java |  228 ++++
 .../src/main/xsd/authorizers.xsd                |   24 +-
 .../authorization/AuthorizerFactoryTest.java    |  264 ++++
 .../MockPolicyBasedAuthorizer.java              |  183 +++
 .../nifi/web/api/dto/FlowConfigurationDTO.java  |   33 +
 .../nifi/web/api/entity/AccessPolicyEntity.java |    2 +-
 .../nifi/web/api/entity/TenantsEntity.java      |    2 +-
 .../apache/nifi/web/api/entity/UserEntity.java  |    2 +-
 .../nifi/web/api/entity/UserGroupEntity.java    |    5 +-
 .../nifi/web/api/entity/UserGroupsEntity.java   |    2 +-
 .../nifi-framework/nifi-file-authorizer/pom.xml |   15 +-
 .../authorization/AuthorizationsHolder.java     |  209 +---
 .../authorization/FileAccessPolicyProvider.java |  947 ++++++++++++++
 .../nifi/authorization/FileAuthorizer.java      | 1151 ++----------------
 .../authorization/FileUserGroupProvider.java    |  820 +++++++++++++
 .../nifi/authorization/IdentifierUtil.java      |   35 +
 .../nifi/authorization/UserGroupHolder.java     |  239 ++++
 ...ache.nifi.authorization.AccessPolicyProvider |   15 +
 ....apache.nifi.authorization.UserGroupProvider |   15 +
 .../FileAccessPolicyProviderTest.java           | 1050 ++++++++++++++++
 .../nifi/authorization/FileAuthorizerTest.java  |  125 +-
 .../FileUserGroupProviderTest.java              |  698 +++++++++++
 ...StandardAuthorizerInitializationContext.java |   16 +-
 .../StandardManagedAuthorizer.java              |  251 ++++
 .../authorization/user/StandardNiFiUser.java    |  132 +-
 .../nifi/authorization/util/UserGroupUtil.java  |   54 +
 .../org.apache.nifi.authorization.Authorizer    |   15 +
 .../StandardManagedAuthorizerTest.java          |  438 +++++++
 .../resource/DataAuthorizableTest.java          |   38 +-
 .../http/StandardHttpResponseMapper.java        |   12 +
 .../endpoints/AccessPolicyEndpointMerger.java   |   61 +
 .../endpoints/SearchUsersEndpointMerger.java    |   59 +
 .../http/endpoints/UserEndpointMerger.java      |   58 +
 .../http/endpoints/UserGroupEndpointMerger.java |   58 +
 .../endpoints/UserGroupsEndpointMerger.java     |   76 ++
 .../http/endpoints/UsersEndpointMerger.java     |   76 ++
 .../manager/AccessPolicyEntityMerger.java       |   74 ++
 .../nifi/cluster/manager/UserEntityMerger.java  |   76 ++
 .../cluster/manager/UserGroupEntityMerger.java  |   75 ++
 .../cluster/manager/UserGroupsEntityMerger.java |   39 +
 .../nifi/cluster/manager/UsersEntityMerger.java |   39 +
 .../AccessPolicyEndpointMergerTest.java         |   41 +
 .../TestThreadPoolRequestReplicator.java        |    7 +-
 .../manager/AccessPolicyEntityMergerTest.java   |   96 ++
 .../cluster/manager/UserEntityMergerTest.java   |  114 ++
 .../manager/UserGroupEntityMergerTest.java      |  114 ++
 .../apache/nifi/cluster/protocol/DataFlow.java  |    2 +-
 .../nifi-framework/nifi-framework-core/pom.xml  |    4 +
 .../nifi/controller/StandardFlowService.java    |    7 +-
 .../controller/StandardFlowSynchronizer.java    |   41 +-
 .../org/apache/nifi/nar/ExtensionManager.java   |    4 +
 .../nifi/nar/NarThreadContextClassLoader.java   |    4 +
 .../nifi-framework/nifi-resources/pom.xml       |    2 +-
 .../src/main/resources/conf/authorizers.xml     |  104 +-
 .../nifi/remote/StandardRootGroupPort.java      |   43 +-
 .../nifi/web/StandardNiFiServiceFacade.java     |    1 +
 .../StandardNiFiWebConfigurationContext.java    |   30 +-
 .../nifi/web/api/AccessPolicyResource.java      |   22 +-
 .../apache/nifi/web/api/ControllerResource.java |    1 +
 .../apache/nifi/web/api/CountersResource.java   |    1 +
 .../org/apache/nifi/web/api/FlowResource.java   |    2 +
 .../apache/nifi/web/api/ProvenanceResource.java |    1 +
 .../apache/nifi/web/api/ResourceResource.java   |    1 +
 .../apache/nifi/web/api/SiteToSiteResource.java |    1 +
 .../nifi/web/api/SystemDiagnosticsResource.java |    1 +
 .../apache/nifi/web/api/TenantsResource.java    |   48 +-
 .../api/config/AccessDeniedExceptionMapper.java |    2 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java |    6 +-
 .../apache/nifi/web/dao/AccessPolicyDAO.java    |    4 +-
 .../impl/StandardPolicyBasedAuthorizerDAO.java  |  270 ++--
 .../web/StandardNiFiServiceFacadeSpec.groovy    |   30 +-
 .../StandardPolicyBasedAuthorizerDAOSpec.groovy |  116 +-
 .../audit/TestRemoteProcessGroupAuditor.java    |    4 +-
 .../nifi/web/StandardNiFiServiceFacadeTest.java |   12 +-
 .../web/revision/TestNaiveRevisionManager.java  |   10 +-
 .../security/NiFiAuthenticationProvider.java    |    9 +-
 .../security/jwt/JwtAuthenticationProvider.java |   13 +-
 .../security/otp/OtpAuthenticationProvider.java |   10 +-
 .../x509/X509AuthenticationProvider.java        |   26 +-
 .../resources/nifi-web-security-context.xml     |   12 +-
 .../NiFiAuthenticationProviderTest.java         |    7 +-
 .../otp/OtpAuthenticationProviderTest.java      |    4 +-
 .../x509/X509AuthenticationProviderTest.java    |   33 +-
 .../WEB-INF/partials/canvas/canvas-header.jsp   |    6 +-
 .../WEB-INF/partials/canvas/navigation.jsp      |    4 +-
 .../src/main/webapp/css/policy-management.css   |    2 +
 .../nifi-web-ui/src/main/webapp/css/users.css   |    1 +
 .../webapp/js/nf/canvas/nf-canvas-bootstrap.js  |    2 +
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |   14 +
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |   34 +
 .../main/webapp/js/nf/canvas/nf-context-menu.js |    2 +-
 .../js/nf/canvas/nf-controller-services.js      |    2 +-
 .../webapp/js/nf/canvas/nf-policy-management.js |   84 +-
 .../src/main/webapp/js/nf/canvas/nf-settings.js |    2 +-
 .../js/nf/templates/nf-templates-table.js       |    2 +-
 .../main/webapp/js/nf/users/nf-users-table.js   |   36 +-
 .../src/main/webapp/js/nf/users/nf-users.js     |   18 +-
 .../TestPersistentProvenanceRepository.java     |   77 +-
 .../index/lucene/TestLuceneEventIndex.java      |   42 +-
 .../TestVolatileProvenanceRepository.java       |    7 +
 pom.xml                                         |    2 +-
 129 files changed, 9476 insertions(+), 2351 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-docs/src/main/asciidoc/administration-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index 36ae533..cc1197c 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -385,7 +385,32 @@ Authorizers are configured using two properties in the 'nifi.properties' file:
 Authorizers.xml Setup
 ~~~~~~~~~~~~~~~~~~~~~
 
-The 'authorizers.xml' file is used to define and configure available authorizers.  The default authorizer is the FileAuthorizer, however, you can develop additional authorizers as extensions.  The FileAuthorizer has the following properties:
+The 'authorizers.xml' file is used to define and configure available authorizers.  The default authorizer is the StandardManagedAuthorizer.  The managed authorizer is comprised of a UserGroupProvider
+and a AccessPolicyProvider.  The users, group, and access policies will be loaded and optionally configured through these providers.  The managed authorizer will make all access decisions based on
+these provided users, groups, and access policies.
+
+The default UserGroupProvider is the FileUserGroupProvider, however, you can develop additional UserGroupProviders as extensions.  The FileUserGroupProvider has the following properties:
+
+* Users File - The file where the FileUserGroupProvider stores users and groups.  By default, the 'users.xml' in the 'conf' directory is chosen.
+* 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 - 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"
+
+The default AccessPolicyProvider is the FileAccessPolicyProvider, however, you can develop additional AccessPolicyProvider as extensions.  The FileAccessPolicyProvider has the following properties:
+
+* 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.
+* 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.
+* Node Identity - 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"
+
+The identities configured in the Initial Admin Identity, the Node Identity properties, or discovered in a Legacy Authorized Users File must be available in the configured User Group Provider.
+
+The default authorizer is the StandardManagedAuthorizer, however, you can develop additional authorizers as extensions.  The StandardManagedAuthorizer has the following properties:
+
+* Access Policy Provider - The identifier for an Access Policy Provider defined above.
+
+The FileAuthorizer has been replaced with the more granular StandardManagedAuthorizer approach described above. However, it is still available for backwards compatibility reasons. The
+FileAuthorizer has the following properties.
 
 * Authorizations File - The file where the FileAuthorizer stores policies.  By default, the 'authorizations.xml' in the 'conf' directory is chosen.
 * Users File - The file where the FileAuthorizer stores users and groups.  By default, the 'users.xml' in the 'conf' directory is chosen.
@@ -402,17 +427,29 @@ If you are setting up a secured NiFi instance for the first time, you must manua
 Here is an example LDAP entry using the name John Smith:
 
 ----
-<authorizer>
-        <identifier>file-provider</identifier>
-        <class>org.apache.nifi.authorization.FileAuthorizer</class>
-        <property name="Authorizations File">./conf/authorizations.xml</property>
+<authorizers>
+    <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">cn=John Smith,ou=people,dc=example,dc=com</property>
+    </userGroupProvider>
+    <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">cn=John Smith,ou=people,dc=example,dc=com</property>
         <property name="Legacy Authorized Users File"></property>
-        <!--
+
         <property name="Node Identity 1"></property>
-        <property name="Node Identity 2"></property>
-        -->
+    </accessPolicyProvider>
+    <authorizer>
+        <identifier>managed-authorizer</identifier>
+        <class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
+        <property name="Access Policy Provider">file-access-policy-provider</property>
     </authorizer>
 </authorizers>
 ----
@@ -420,17 +457,29 @@ Here is an example LDAP entry using the name John Smith:
 Here is an example Kerberos entry using the name John Smith and realm `NIFI.APACHE.ORG`:
 
 ----
-<authorizer>
-        <identifier>file-provider</identifier>
-        <class>org.apache.nifi.authorization.FileAuthorizer</class>
-        <property name="Authorizations File">./conf/authorizations.xml</property>
+<authorizers>
+    <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">johnsmith@NIFI.APACHE.ORG</property>
+    </userGroupProvider>
+    <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">johnsmith@NIFI.APACHE.ORG</property>
         <property name="Legacy Authorized Users File"></property>
-        <!--
+
         <property name="Node Identity 1"></property>
-        <property name="Node Identity 2"></property>
-        -->
+    </accessPolicyProvider>
+    <authorizer>
+        <identifier>managed-authorizer</identifier>
+        <class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
+        <property name="Access Policy Provider">file-access-policy-provider</property>
     </authorizer>
 </authorizers>
 ----
@@ -449,13 +498,28 @@ Here is an example entry:
 
 ----
 <authorizers>
-    <authorizer>
-        <identifier>file-provider</identifier>
-        <class>org.apache.nifi.authorization.FileAuthorizer</class>
-        <property name="Authorizations File">./conf/authorizations.xml</property>
+    <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">/Users/johnsmith/config_files/authorized-users.xml</property>
+
+        <property name="Initial User Identity 1"></property>
+    </userGroupProvider>
+    <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">/Users/johnsmith/config_files/authorized-users.xml</property>
+
+        <property name="Node Identity 1"></property>
+    </accessPolicyProvider>
+    <authorizer>
+        <identifier>managed-authorizer</identifier>
+        <class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
+        <property name="Access Policy Provider">file-access-policy-provider</property>
     </authorizer>
 </authorizers>
 ----
@@ -514,15 +578,32 @@ cn=nifi-2,ou=people,dc=example,dc=com
 ----
 
 ----
-<authorizer>
-        <identifier>file-provider</identifier>
-        <class>org.apache.nifi.authorization.FileAuthorizer</class>
-        <property name="Authorizations File">./conf/authorizations.xml</property>
+<authorizers>
+    <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">johnsmith@NIFI.APACHE.ORG</property>
+        <property name="Initial User Identity 2">cn=nifi-1,ou=people,dc=example,dc=com</property>
+        <property name="Initial User Identity 3">cn=nifi-2,ou=people,dc=example,dc=com</property>
+    </userGroupProvider>
+    <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">johnsmith@NIFI.APACHE.ORG</property>
         <property name="Legacy Authorized Users File"></property>
+
         <property name="Node Identity 1">cn=nifi-1,ou=people,dc=example,dc=com</property>
         <property name="Node Identity 2">cn=nifi-2,ou=people,dc=example,dc=com</property>
+    </accessPolicyProvider>
+    <authorizer>
+        <identifier>managed-authorizer</identifier>
+        <class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
+        <property name="Access Policy Provider">file-access-policy-provider</property>
     </authorizer>
 </authorizers>
 ----
@@ -535,7 +616,11 @@ Now that initial authorizations have been created, additional users, groups and
 Configuring Users & Access Policies
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-This section describes:
+Depending on the capabilities of the configured UserGroupProvider and AccessPolicyProvider the users, groups, and policies will be configurable in the UI. If the extensions are not configurable the
+users, groups, and policies will read-only in the UI. If the configured authorizer does not use UserGroupProvider and AccessPolicyProvider the users and policies may or may not be visible and
+configurable in the UI based on the underlying implementation.
+
+This section assumes the users, groups, and policies are configurable in the UI and describes:
 
 * How to create users and groups
 * How access policies are used to define authorizations

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
index 0d047f1..929e2ad 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java
@@ -18,6 +18,8 @@ package org.apache.nifi.authorization;
 
 import org.apache.nifi.authorization.exception.AuthorizationAccessException;
 import org.apache.nifi.authorization.exception.AuthorizerCreationException;
+import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
+import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -43,7 +45,7 @@ import java.util.Set;
 /**
  * An Authorizer that provides management of users, groups, and policies.
  */
-public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
+public abstract class AbstractPolicyBasedAuthorizer implements ManagedAuthorizer {
 
     static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
     static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
@@ -60,32 +62,9 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
     static final String RESOURCE_ATTR = "resource";
     static final String ACTIONS_ATTR = "actions";
 
-    public static final String EMPTY_FINGERPRINT = "EMPTY";
-
     @Override
     public final void onConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
         doOnConfigured(configurationContext);
-
-        // ensure that only one policy per resource-action exists
-        for (AccessPolicy accessPolicy : getAccessPolicies()) {
-            if (policyExists(accessPolicy)) {
-                throw new AuthorizerCreationException(String.format("Found multiple policies for '%s' with '%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
-            }
-        }
-
-        // ensure that only one group exists per identity
-        for (User user : getUsers()) {
-            if (tenantExists(user.getIdentifier(), user.getIdentity())) {
-                throw new AuthorizerCreationException(String.format("Found multiple users/user groups with identity '%s'.", user.getIdentity()));
-            }
-        }
-
-        // ensure that only one group exists per identity
-        for (Group group : getGroups()) {
-            if (tenantExists(group.getIdentifier(), group.getName())) {
-                throw new AuthorizerCreationException(String.format("Found multiple users/user groups with name '%s'.", group.getName()));
-            }
-        }
     }
 
     /**
@@ -96,48 +75,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      */
     protected abstract void doOnConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException;
 
-    /**
-     * Checks if another policy exists with the same resource and action as the given policy.
-     *
-     * @param checkAccessPolicy an access policy being checked
-     * @return true if another access policy exists with the same resource and action, false otherwise
-     */
-    private boolean policyExists(final AccessPolicy checkAccessPolicy) {
-        for (AccessPolicy accessPolicy : getAccessPolicies()) {
-            if (!accessPolicy.getIdentifier().equals(checkAccessPolicy.getIdentifier())
-                    && accessPolicy.getResource().equals(checkAccessPolicy.getResource())
-                    && accessPolicy.getAction().equals(checkAccessPolicy.getAction())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Checks if another user exists with the same identity.
-     *
-     * @param identifier identity of the user
-     * @param identity identity of the user
-     * @return true if another user exists with the same identity, false otherwise
-     */
-    private boolean tenantExists(final String identifier, final String identity) {
-        for (User user : getUsers()) {
-            if (!user.getIdentifier().equals(identifier)
-                    && user.getIdentity().equals(identity)) {
-                return true;
-            }
-        }
-
-        for (Group group : getGroups()) {
-            if (!group.getIdentifier().equals(identifier)
-                    && group.getName().equals(identity)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     @Override
     public final AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
         final UsersAndAccessPolicies usersAndAccessPolicies = getUsersAndAccessPolicies();
@@ -191,9 +128,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      * @throws IllegalStateException if a group with the same name already exists
      */
     public final synchronized Group addGroup(Group group) throws AuthorizationAccessException {
-        if (tenantExists(group.getIdentifier(), group.getName())) {
-            throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", group.getName()));
-        }
         return doAddGroup(group);
     }
 
@@ -224,9 +158,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      * @throws IllegalStateException if there is already a group with the same name
      */
     public final synchronized Group updateGroup(Group group) throws AuthorizationAccessException {
-        if (tenantExists(group.getIdentifier(), group.getName())) {
-            throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", group.getName()));
-        }
         return doUpdateGroup(group);
     }
 
@@ -266,9 +197,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      * @throws IllegalStateException if there is already a user with the same identity
      */
     public final synchronized User addUser(User user) throws AuthorizationAccessException {
-        if (tenantExists(user.getIdentifier(), user.getIdentity())) {
-            throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", user.getIdentity()));
-        }
         return doAddUser(user);
     }
 
@@ -308,9 +236,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      * @throws IllegalStateException if there is already a user with the same identity
      */
     public final synchronized User updateUser(final User user) throws AuthorizationAccessException {
-        if (tenantExists(user.getIdentifier(), user.getIdentity())) {
-            throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", user.getIdentity()));
-        }
         return doUpdateUser(user);
     }
 
@@ -348,9 +273,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      * @throws AuthorizationAccessException if there was an unexpected error performing the operation
      */
     public final synchronized AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
-        if (policyExists(accessPolicy)) {
-            throw new IllegalStateException(String.format("Found multiple policies for '%s' with '%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
-        }
         return doAddAccessPolicy(accessPolicy);
     }
 
@@ -407,17 +329,54 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
     public abstract UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException;
 
     /**
+     * Returns whether the proposed fingerprint is inheritable.
+     *
+     * @param proposedFingerprint the proposed fingerprint
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
+     */
+    @Override
+    public final void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+        try {
+            // ensure we understand the proposed fingerprint
+            parsePoliciesUsersAndGroups(proposedFingerprint);
+        } catch (final AuthorizationAccessException e) {
+            throw new UninheritableAuthorizationsException("Unable to parse proposed fingerprint: " + e);
+        }
+
+        final List<User> users = getSortedUsers();
+        final List<Group> groups = getSortedGroups();
+        final List<AccessPolicy> accessPolicies = getSortedAccessPolicies();
+
+        // ensure we're in a state to inherit
+        if (!users.isEmpty() || !groups.isEmpty() || !accessPolicies.isEmpty()) {
+            throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current Authorizations is not empty..");
+        }
+    }
+
+    /**
      * Parses the fingerprint and adds any users, groups, and policies to the current Authorizer.
      *
      * @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
      */
+    @Override
     public final void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException {
         if (fingerprint == null || fingerprint.trim().isEmpty()) {
             return;
         }
 
-        final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
+        final PoliciesUsersAndGroups policiesUsersAndGroups = parsePoliciesUsersAndGroups(fingerprint);
+        policiesUsersAndGroups.getUsers().forEach(user -> addUser(user));
+        policiesUsersAndGroups.getGroups().forEach(group -> addGroup(group));
+        policiesUsersAndGroups.getAccessPolicies().forEach(policy -> addAccessPolicy(policy));
+    }
+
+    private PoliciesUsersAndGroups parsePoliciesUsersAndGroups(final String fingerprint) {
+        final List<AccessPolicy> accessPolicies = new ArrayList<>();
+        final List<User> users = new ArrayList<>();
+        final List<Group> groups = new ArrayList<>();
 
+        final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
         try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
             final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
             final Document document = docBuilder.parse(in);
@@ -427,29 +386,27 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
             NodeList userNodes = rootElement.getElementsByTagName(USER_ELEMENT);
             for (int i=0; i < userNodes.getLength(); i++) {
                 Node userNode = userNodes.item(i);
-                User user = parseUser((Element) userNode);
-                addUser(user);
+                users.add(parseUser((Element) userNode));
             }
 
             // parse all the groups and add them to the current authorizer
             NodeList groupNodes = rootElement.getElementsByTagName(GROUP_ELEMENT);
             for (int i=0; i < groupNodes.getLength(); i++) {
                 Node groupNode = groupNodes.item(i);
-                Group group = parseGroup((Element) groupNode);
-                addGroup(group);
+                groups.add(parseGroup((Element) groupNode));
             }
 
             // parse all the policies and add them to the current authorizer
             NodeList policyNodes = rootElement.getElementsByTagName(POLICY_ELEMENT);
             for (int i=0; i < policyNodes.getLength(); i++) {
                 Node policyNode = policyNodes.item(i);
-                AccessPolicy policy = parsePolicy((Element) policyNode);
-                addAccessPolicy(policy);
+                accessPolicies.add(parsePolicy((Element) policyNode));
             }
-
         } catch (SAXException | ParserConfigurationException | IOException e) {
             throw new AuthorizationAccessException("Unable to parse fingerprint", e);
         }
+
+        return new PoliciesUsersAndGroups(accessPolicies, users, groups);
     }
 
     private User parseUser(final Element element) {
@@ -503,6 +460,181 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
         return builder.build();
     }
 
+    @Override
+    public final AccessPolicyProvider getAccessPolicyProvider() {
+        return new ConfigurableAccessPolicyProvider() {
+            @Override
+            public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.getAccessPolicies();
+            }
+
+            @Override
+            public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.getAccessPolicy(identifier);
+            }
+
+            @Override
+            public AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.addAccessPolicy(accessPolicy);
+            }
+
+            @Override
+            public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.updateAccessPolicy(accessPolicy);
+            }
+
+            @Override
+            public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
+                return AbstractPolicyBasedAuthorizer.this.deleteAccessPolicy(accessPolicy);
+            }
+
+            @Override
+            public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
+                final UsersAndAccessPolicies usersAndAccessPolicies = AbstractPolicyBasedAuthorizer.this.getUsersAndAccessPolicies();
+                return usersAndAccessPolicies.getAccessPolicy(resourceIdentifier, action);
+            }
+
+            @Override
+            public String getFingerprint() throws AuthorizationAccessException {
+                // fingerprint is managed by the encapsulating class
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+                // fingerprint is managed by the encapsulating class
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+                // fingerprint is managed by the encapsulating class
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public UserGroupProvider getUserGroupProvider() {
+                return new ConfigurableUserGroupProvider() {
+                    @Override
+                    public User addUser(User user) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.addUser(user);
+                    }
+
+                    @Override
+                    public User updateUser(User user) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.updateUser(user);
+                    }
+
+                    @Override
+                    public User deleteUser(User user) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.deleteUser(user);
+                    }
+
+                    @Override
+                    public Group addGroup(Group group) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.addGroup(group);
+                    }
+
+                    @Override
+                    public Group updateGroup(Group group) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.updateGroup(group);
+                    }
+
+                    @Override
+                    public Group deleteGroup(Group group) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.deleteGroup(group);
+                    }
+
+                    @Override
+                    public Set<User> getUsers() throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getUsers();
+                    }
+
+                    @Override
+                    public User getUser(String identifier) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getUser(identifier);
+                    }
+
+                    @Override
+                    public User getUserByIdentity(String identity) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getUserByIdentity(identity);
+                    }
+
+                    @Override
+                    public Set<Group> getGroups() throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getGroups();
+                    }
+
+                    @Override
+                    public Group getGroup(String identifier) throws AuthorizationAccessException {
+                        return AbstractPolicyBasedAuthorizer.this.getGroup(identifier);
+                    }
+
+                    @Override
+                    public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
+                        final UsersAndAccessPolicies usersAndAccessPolicies = AbstractPolicyBasedAuthorizer.this.getUsersAndAccessPolicies();
+                        final User user = usersAndAccessPolicies.getUser(identity);
+                        final Set<Group> groups = usersAndAccessPolicies.getGroups(identity);
+
+                        return new UserAndGroups() {
+                            @Override
+                            public User getUser() {
+                                return user;
+                            }
+
+                            @Override
+                            public Set<Group> getGroups() {
+                                return groups;
+                            }
+                        };
+                    }
+
+                    @Override
+                    public String getFingerprint() throws AuthorizationAccessException {
+                        // fingerprint is managed by the encapsulating class
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+                        // fingerprint is managed by the encapsulating class
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+                        // fingerprint is managed by the encapsulating class
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
+                    }
+
+                    @Override
+                    public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
+                    }
+
+                    @Override
+                    public void preDestruction() throws AuthorizerDestructionException {
+                    }
+                };
+            }
+
+            @Override
+            public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
+            }
+
+            @Override
+            public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
+            }
+
+            @Override
+            public void preDestruction() throws AuthorizerDestructionException {
+            }
+        };
+    }
+
     /**
      * Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
      * used for comparison to determine if two policy-based authorizers represent a compatible set of users,
@@ -510,17 +642,12 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
      *
      * @return the fingerprint for this Authorizer
      */
+    @Override
     public final String getFingerprint() throws AuthorizationAccessException {
         final List<User> users = getSortedUsers();
         final List<Group> groups = getSortedGroups();
         final List<AccessPolicy> policies = getSortedAccessPolicies();
 
-        // when there are no users, groups, policies we want to always return a simple indicator so
-        // it can easily be determined when comparing fingerprints
-        if (users.isEmpty() && groups.isEmpty() && policies.isEmpty()) {
-            return EMPTY_FINGERPRINT;
-        }
-
         XMLStreamWriter writer = null;
         final StringWriter out = new StringWriter();
         try {
@@ -611,38 +738,43 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
 
     private List<AccessPolicy> getSortedAccessPolicies() {
         final List<AccessPolicy> policies = new ArrayList<>(getAccessPolicies());
-
-        Collections.sort(policies, new Comparator<AccessPolicy>() {
-            @Override
-            public int compare(AccessPolicy p1, AccessPolicy p2) {
-                return p1.getIdentifier().compareTo(p2.getIdentifier());
-            }
-        });
+        Collections.sort(policies, Comparator.comparing(AccessPolicy::getIdentifier));
         return policies;
     }
 
     private List<Group> getSortedGroups() {
         final List<Group> groups = new ArrayList<>(getGroups());
-
-        Collections.sort(groups, new Comparator<Group>() {
-            @Override
-            public int compare(Group g1, Group g2) {
-                return g1.getIdentifier().compareTo(g2.getIdentifier());
-            }
-        });
+        Collections.sort(groups, Comparator.comparing(Group::getIdentifier));
         return groups;
     }
 
     private List<User> getSortedUsers() {
         final List<User> users = new ArrayList<>(getUsers());
-
-        Collections.sort(users, new Comparator<User>() {
-            @Override
-            public int compare(User u1, User u2) {
-                return u1.getIdentifier().compareTo(u2.getIdentifier());
-            }
-        });
+        Collections.sort(users, Comparator.comparing(User::getIdentifier));
         return users;
     }
 
+    private static class PoliciesUsersAndGroups {
+        final List<AccessPolicy> accessPolicies;
+        final List<User> users;
+        final List<Group> groups;
+
+        public PoliciesUsersAndGroups(List<AccessPolicy> accessPolicies, List<User> users, List<Group> groups) {
+            this.accessPolicies = accessPolicies;
+            this.users = users;
+            this.groups = groups;
+        }
+
+        public List<AccessPolicy> getAccessPolicies() {
+            return accessPolicies;
+        }
+
+        public List<User> getUsers() {
+            return users;
+        }
+
+        public List<Group> getGroups() {
+            return groups;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java
index 93cabb2..1a7f751 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java
@@ -16,10 +16,12 @@
  */
 package org.apache.nifi.authorization;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * Defines a policy for a set of userIdentifiers to perform a set of actions on a given resource.
@@ -174,6 +176,41 @@ public class AccessPolicy {
         }
 
         /**
+         * Sets the identifier of the builder to a random UUID.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this builder was constructed from an existing Policy
+         */
+        public Builder identifierGenerateRandom() {
+            if (fromPolicy) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from an existing policy");
+            }
+
+            this.identifier = UUID.randomUUID().toString();
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder with a UUID generated from the specified seed string.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this builder was constructed from an existing Policy
+         */
+        public Builder identifierGenerateFromSeed(final String seed) {
+            if (fromPolicy) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from an existing policy");
+            }
+            if (seed == null) {
+                throw new IllegalArgumentException("Cannot seed the policy identifier with a null value.");
+            }
+
+            this.identifier = UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
+            return this;
+        }
+
+        /**
          * Sets the resource of the builder.
          *
          * @param resource the resource to set

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProvider.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProvider.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProvider.java
new file mode 100644
index 0000000..59ada24
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProvider.java
@@ -0,0 +1,90 @@
+/*
+ * 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.authorization;
+
+import org.apache.nifi.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.authorization.exception.AuthorizerCreationException;
+import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
+
+import java.util.Set;
+
+/**
+ * Provides access to AccessPolicies and the configured UserGroupProvider.
+ *
+ * NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
+ * make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
+ *
+ * Additionally, extensions need to be thread safe.
+ */
+public interface AccessPolicyProvider {
+
+    /**
+     * Retrieves all access policies. Must be non null
+     *
+     * @return a list of policies
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException;
+
+    /**
+     * Retrieves the policy with the given identifier.
+     *
+     * @param identifier the id of the policy to retrieve
+     * @return the policy with the given id, or null if no matching policy exists
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException;
+
+    /**
+     * Gets the access policies for the specified resource identifier and request action.
+     *
+     * @param resourceIdentifier the resource identifier
+     * @param action the request action
+     * @return the policy matching the resouce and action, or null if no matching policy exists
+     * @throws AuthorizationAccessException if there was any unexpected error performing the operation
+     */
+    AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException;
+
+    /**
+     * Returns the UserGroupProvider for this managed Authorizer. Must be non null
+     *
+     * @return the UserGroupProvider
+     */
+    UserGroupProvider getUserGroupProvider();
+
+    /**
+     * Called immediately after instance creation for implementers to perform additional setup
+     *
+     * @param initializationContext in which to initialize
+     */
+    void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException;
+
+    /**
+     * Called to configure the Authorizer.
+     *
+     * @param configurationContext at the time of configuration
+     * @throws AuthorizerCreationException for any issues configuring the provider
+     */
+    void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException;
+
+    /**
+     * Called immediately before instance destruction for implementers to release resources.
+     *
+     * @throws AuthorizerDestructionException If pre-destruction fails.
+     */
+    void preDestruction() throws AuthorizerDestructionException;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInitializationContext.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInitializationContext.java
new file mode 100644
index 0000000..1013c15
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderInitializationContext.java
@@ -0,0 +1,30 @@
+/*
+ * 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.authorization;
+
+/**
+ * Initialization content for AccessPolicyProviders.
+ */
+public interface AccessPolicyProviderInitializationContext extends UserGroupProviderInitializationContext {
+
+    /**
+     * The lookup for accessing other configured AccessPolicyProviders.
+     *
+     * @return  The AccessPolicyProvider lookup
+     */
+    AccessPolicyProviderLookup getAccessPolicyProviderLookup();
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderLookup.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderLookup.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderLookup.java
new file mode 100644
index 0000000..d387761
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AccessPolicyProviderLookup.java
@@ -0,0 +1,31 @@
+/*
+ * 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.authorization;
+
+/**
+ *
+ */
+public interface AccessPolicyProviderLookup {
+
+    /**
+     * Looks up the AccessPolicyProvider with the specified identifier
+     *
+     * @param identifier        The identifier of the AccessPolicyProvider
+     * @return                  The AccessPolicyProvider
+     */
+    AccessPolicyProvider getAccessPolicyProvider(String identifier);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
index 4f5a8f8..d5b1600 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizationRequest.java
@@ -20,6 +20,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -31,6 +32,7 @@ public class AuthorizationRequest {
 
     private final Resource resource;
     private final String identity;
+    private final Set<String> groups;
     private final RequestAction action;
     private final boolean isAccessAttempt;
     private final boolean isAnonymous;
@@ -46,6 +48,7 @@ public class AuthorizationRequest {
 
         this.resource = builder.resource;
         this.identity = builder.identity;
+        this.groups = builder.groups == null ? null : Collections.unmodifiableSet(builder.groups);
         this.action = builder.action;
         this.isAccessAttempt = builder.isAccessAttempt;
         this.isAnonymous = builder.isAnonymous;
@@ -82,6 +85,16 @@ public class AuthorizationRequest {
     }
 
     /**
+     * The groups the user making this request belongs to. May be null if this NiFi is not configured to load user
+     * groups or empty if the user has no groups
+     *
+     * @return The groups
+     */
+    public Set<String> getGroups() {
+        return groups;
+    }
+
+    /**
      * Whether this is a direct access attempt of the Resource if if it's being checked as part of another response.
      *
      * @return if this is a direct access attempt
@@ -142,6 +155,7 @@ public class AuthorizationRequest {
 
         private Resource resource;
         private String identity;
+        private Set<String> groups;
         private Boolean isAnonymous;
         private Boolean isAccessAttempt;
         private RequestAction action;
@@ -159,6 +173,11 @@ public class AuthorizationRequest {
             return this;
         }
 
+        public Builder groups(final Set<String> groups) {
+            this.groups = groups;
+            return this;
+        }
+
         public Builder anonymous(final Boolean isAnonymous) {
             this.isAnonymous = isAnonymous;
             return this;

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Authorizer.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Authorizer.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Authorizer.java
index cb8c7f1..28c73ff 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Authorizer.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Authorizer.java
@@ -27,6 +27,10 @@ public interface Authorizer {
 
     /**
      * Determines if the specified user/entity is authorized to access the specified resource within the given context.
+     * These details are all contained in the AuthorizationRequest.
+     *
+     * NOTE: This method will be called often and frequently. Because of this, if the underlying implementation needs to
+     * make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
      *
      * @param   request The authorization request
      * @return  the authorization result

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java
index 3721ab4..cd126ac 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java
@@ -42,9 +42,7 @@ public interface AuthorizerConfigurationContext {
 
     /**
      * @param property to lookup the descriptor and value of
-     * @return the value the component currently understands for the given
-     * PropertyDescriptor. This method does not substitute default
-     * PropertyDescriptor values, so the value returned will be null if not set
+     * @return the value the component currently understands for the given PropertyDescriptor
      */
     PropertyValue getProperty(String property);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerInitializationContext.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerInitializationContext.java
index 4b3d77c..179a179 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerInitializationContext.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/AuthorizerInitializationContext.java
@@ -19,19 +19,12 @@ package org.apache.nifi.authorization;
 /**
  * Initialization content for Authorizers.
  */
-public interface AuthorizerInitializationContext {
-
-    /**
-     * The identifier of the Authorizer.
-     *
-     * @return  The identifier
-     */
-    public String getIdentifier();
+public interface AuthorizerInitializationContext extends AccessPolicyProviderInitializationContext {
 
     /**
      * The lookup for accessing other configured Authorizers.
      *
      * @return  The Authorizer lookup
      */
-    public AuthorizerLookup getAuthorizerLookup();
+    AuthorizerLookup getAuthorizerLookup();
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableAccessPolicyProvider.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableAccessPolicyProvider.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableAccessPolicyProvider.java
new file mode 100644
index 0000000..71258c3
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableAccessPolicyProvider.java
@@ -0,0 +1,84 @@
+/*
+ * 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.authorization;
+
+import org.apache.nifi.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
+
+/**
+ * Provides support for configuring AccessPolicies.
+ *
+ * NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
+ * make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
+ *
+ * Additionally, extensions need to be thread safe.
+ */
+public interface ConfigurableAccessPolicyProvider extends AccessPolicyProvider {
+
+    /**
+     * Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
+     * used for comparison to determine if two policy-based authorizers represent a compatible set of policies.
+     *
+     * @return the fingerprint for this Authorizer
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    String getFingerprint() throws AuthorizationAccessException;
+
+    /**
+     * Parses the fingerprint and adds any policies to the current AccessPolicyProvider.
+     *
+     * @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException;
+
+    /**
+     * When the fingerprints are not equal, this method will check if the proposed fingerprint is inheritable.
+     * If the fingerprint is an exact match, this method will not be invoked as there is nothing to inherit.
+     *
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
+     */
+    void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException;
+
+    /**
+     * Adds the given policy ensuring that multiple policies can not be added for the same resource and action.
+     *
+     * @param accessPolicy the policy to add
+     * @return the policy that was added
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
+
+    /**
+     * The policy represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param accessPolicy an updated policy
+     * @return the updated policy, or null if no matching policy was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given policy.
+     *
+     * @param accessPolicy the policy to delete
+     * @return the deleted policy, or null if no matching policy was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableUserGroupProvider.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableUserGroupProvider.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableUserGroupProvider.java
new file mode 100644
index 0000000..90c0def
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ConfigurableUserGroupProvider.java
@@ -0,0 +1,115 @@
+/*
+ * 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.authorization;
+
+import org.apache.nifi.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
+
+/**
+ * Provides support for configuring Users and Groups.
+ *
+ * NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
+ * make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
+ *
+ * Additionally, extensions need to be thread safe.
+ */
+public interface ConfigurableUserGroupProvider extends UserGroupProvider {
+
+    /**
+     * Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
+     * used for comparison to determine if two policy-based authorizers represent a compatible set of users and/or groups.
+     *
+     * @return the fingerprint for this Authorizer
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    String getFingerprint() throws AuthorizationAccessException;
+
+    /**
+     * Parses the fingerprint and adds any users and groups to the current Authorizer.
+     *
+     * @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException;
+
+    /**
+     * When the fingerprints are not equal, this method will check if the proposed fingerprint is inheritable.
+     * If the fingerprint is an exact match, this method will not be invoked as there is nothing to inherit.
+     *
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
+     */
+    void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException;
+
+    /**
+     * Adds the given user.
+     *
+     * @param user the user to add
+     * @return the user that was added
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if there is already a user with the same identity
+     */
+    User addUser(User user) throws AuthorizationAccessException;
+
+    /**
+     * The user represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param user an updated user instance
+     * @return the updated user instance, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if there is already a user with the same identity
+     */
+    User updateUser(final User user) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given user.
+     *
+     * @param user the user to delete
+     * @return the user that was deleted, or null if no matching user was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    User deleteUser(User user) throws AuthorizationAccessException;
+
+    /**
+     * Adds a new group.
+     *
+     * @param group the Group to add
+     * @return the added Group
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if a group with the same name already exists
+     */
+    Group addGroup(Group group) throws AuthorizationAccessException;
+
+    /**
+     * The group represented by the provided instance will be updated based on the provided instance.
+     *
+     * @param group an updated group instance
+     * @return the updated group instance, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws IllegalStateException if there is already a group with the same name
+     */
+    Group updateGroup(Group group) throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given group.
+     *
+     * @param group the group to delete
+     * @return the deleted group, or null if no matching group was found
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    Group deleteGroup(Group group) throws AuthorizationAccessException;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Group.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Group.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Group.java
index 7db619a..7908e85 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Group.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/Group.java
@@ -16,10 +16,12 @@
  */
 package org.apache.nifi.authorization;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * A group that users can belong to.
@@ -142,6 +144,41 @@ public class Group { // TODO rename to UserGroup
         }
 
         /**
+         * Sets the identifier of the builder to a random UUID.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this builder was constructed from an existing Group
+         */
+        public Builder identifierGenerateRandom() {
+            if (fromGroup) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from an existing group");
+            }
+
+            this.identifier = UUID.randomUUID().toString();
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder with a UUID generated from the specified seed string.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this builder was constructed from an existing Group
+         */
+        public Builder identifierGenerateFromSeed(final String seed) {
+            if (fromGroup) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from an existing group");
+            }
+            if (seed == null) {
+                throw new IllegalArgumentException("Cannot seed the group identifier with a null value.");
+            }
+
+            this.identifier = UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
+            return this;
+        }
+
+        /**
          * Sets the name of the builder.
          *
          * @param name the name

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ManagedAuthorizer.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ManagedAuthorizer.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ManagedAuthorizer.java
new file mode 100644
index 0000000..d70ee55
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/ManagedAuthorizer.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.authorization;
+
+import org.apache.nifi.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
+
+public interface ManagedAuthorizer extends Authorizer {
+
+    /**
+     * Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
+     * used for comparison to determine if two managed authorizers represent a compatible set of users,
+     * groups, and/or policies. Must be non null
+     *
+     * @return the fingerprint for this Authorizer
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    String getFingerprint() throws AuthorizationAccessException;
+
+    /**
+     * Parses the fingerprint and adds any users, groups, and policies to the current Authorizer.
+     *
+     * @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     */
+    void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException;
+
+    /**
+     * When the fingerprints are not equal, this method will check if the proposed fingerprint is inheritable.
+     * If the fingerprint is an exact match, this method will not be invoked as there is nothing to inherit.
+     *
+     * @param proposedFingerprint the proposed fingerprint
+     * @throws AuthorizationAccessException if there was an unexpected error performing the operation
+     * @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
+     */
+    void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException;
+
+    /**
+     * Returns the AccessPolicy provider for this managed Authorizer. Must be non null
+     *
+     * @return the AccessPolicy provider
+     */
+    AccessPolicyProvider getAccessPolicyProvider();
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/User.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/User.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/User.java
index 371241b..83368ea 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/User.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/User.java
@@ -16,7 +16,9 @@
  */
 package org.apache.nifi.authorization;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Objects;
+import java.util.UUID;
 
 /**
  * A user to create authorization policies for.
@@ -129,6 +131,41 @@ public class User {
         }
 
         /**
+         * Sets the identifier of the builder to a random UUID.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this builder was constructed from an existing User
+         */
+        public Builder identifierGenerateRandom() {
+            if (fromUser) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from an existing user");
+            }
+
+            this.identifier = UUID.randomUUID().toString();
+            return this;
+        }
+
+        /**
+         * Sets the identifier of the builder with a UUID generated from the specified seed string.
+         *
+         * @return the builder
+         * @throws IllegalStateException if this method is called when this builder was constructed from an existing User
+         */
+        public Builder identifierGenerateFromSeed(final String seed) {
+            if (fromUser) {
+                throw new IllegalStateException(
+                        "Identifier can not be changed when initialized from an existing user");
+            }
+            if (seed == null) {
+                throw new IllegalArgumentException("Cannot seed the user identifier with a null value.");
+            }
+
+            this.identifier = UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
+            return this;
+        }
+
+        /**
          * Sets the identity of the builder.
          *
          * @param identity the identity to set

http://git-wip-us.apache.org/repos/asf/nifi/blob/4ed7511b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/UserAndGroups.java
----------------------------------------------------------------------
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/authorization/UserAndGroups.java b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/UserAndGroups.java
new file mode 100644
index 0000000..486eba4
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/authorization/UserAndGroups.java
@@ -0,0 +1,40 @@
+/*
+ * 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.authorization;
+
+import java.util.Set;
+
+/**
+ * A holder object to provide atomic access to a user and their groups.
+ */
+public interface UserAndGroups {
+
+    /**
+     * Retrieves the user, or null if the user is unknown
+     *
+     * @return the user with the given identity
+     */
+    User getUser();
+
+    /**
+     * Retrieves the groups for the user, or null if the user is unknown or has no groups.
+     *
+     * @return the set of groups for the given user identity
+     */
+    Set<Group> getGroups();
+
+}