http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/IdentityMappingUtil.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/IdentityMappingUtil.java
 
b/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/IdentityMappingUtil.java
new file mode 100644
index 0000000..fcd827b
--- /dev/null
+++ 
b/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/util/IdentityMappingUtil.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.properties.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class IdentityMappingUtil {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(IdentityMappingUtil.class);
+    private static final Pattern backReferencePattern = 
Pattern.compile("\\$(\\d+)");
+
+    /**
+     * Builds the identity mappings from NiFiRegistryProperties.
+     *
+     * @param properties the NiFiRegistryProperties instance
+     * @return a list of identity mappings
+     */
+    public static List<IdentityMapping> getIdentityMappings(final 
NiFiRegistryProperties properties) {
+        final List<IdentityMapping> mappings = new ArrayList<>();
+
+        // go through each property
+        for (String propertyName : properties.getPropertyKeys()) {
+            if (StringUtils.startsWith(propertyName, 
NiFiRegistryProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX)) {
+                final String key = StringUtils.substringAfter(propertyName, 
NiFiRegistryProperties.SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX);
+                final String identityPattern = 
properties.getProperty(propertyName);
+
+                if (StringUtils.isBlank(identityPattern)) {
+                    LOGGER.warn("Identity Mapping property {} was found, but 
was empty", new Object[]{propertyName});
+                    continue;
+                }
+
+                final String identityValueProperty = 
NiFiRegistryProperties.SECURITY_IDENTITY_MAPPING_VALUE_PREFIX + key;
+                final String identityValue = 
properties.getProperty(identityValueProperty);
+
+                if (StringUtils.isBlank(identityValue)) {
+                    LOGGER.warn("Identity Mapping property {} was found, but 
corresponding value {} was not found",
+                            new Object[]{propertyName, identityValueProperty});
+                    continue;
+                }
+
+                final IdentityMapping identityMapping = new 
IdentityMapping(key, Pattern.compile(identityPattern), identityValue);
+                mappings.add(identityMapping);
+
+                LOGGER.debug("Found Identity Mapping with key = {}, pattern = 
{}, value = {}",
+                        new Object[] {key, identityPattern, identityValue});
+            }
+        }
+
+        // sort the list by the key so users can control the ordering in 
nifi.properties
+        Collections.sort(mappings, new Comparator<IdentityMapping>() {
+            @Override
+            public int compare(IdentityMapping m1, IdentityMapping m2) {
+                return m1.getKey().compareTo(m2.getKey());
+            }
+        });
+
+        return mappings;
+    }
+
+    /**
+     * Checks the given identity against each provided mapping and performs 
the mapping using the first one that matches.
+     * If none match then the identity is returned as is.
+     *
+     * @param identity the identity to map
+     * @param mappings the mappings
+     * @return the mapped identity, or the same identity if no mappings matched
+     */
+    public static String mapIdentity(final String identity, 
List<IdentityMapping> mappings) {
+        for (IdentityMapping mapping : mappings) {
+            Matcher m = mapping.getPattern().matcher(identity);
+            if (m.matches()) {
+                final String pattern = mapping.getPattern().pattern();
+                final String replacementValue = 
escapeLiteralBackReferences(mapping.getReplacementValue(), m.groupCount());
+                return identity.replaceAll(pattern, replacementValue);
+            }
+        }
+
+        return identity;
+    }
+
+    // If we find a back reference that is not valid, then we will treat it as 
a literal string. For example, if we have 3 capturing
+    // groups and the Replacement Value has the value is "I owe $8 to him", 
then we want to treat the $8 as a literal "$8", rather
+    // than attempting to use it as a back reference.
+    private static String escapeLiteralBackReferences(final String unescaped, 
final int numCapturingGroups) {
+        if (numCapturingGroups == 0) {
+            return unescaped;
+        }
+
+        String value = unescaped;
+        final Matcher backRefMatcher = backReferencePattern.matcher(value);
+        while (backRefMatcher.find()) {
+            final String backRefNum = backRefMatcher.group(1);
+            if (backRefNum.startsWith("0")) {
+                continue;
+            }
+            final int originalBackRefIndex = Integer.parseInt(backRefNum);
+            int backRefIndex = originalBackRefIndex;
+
+            // if we have a replacement value like $123, and we have less than 
123 capturing groups, then
+            // we want to truncate the 3 and use capturing group 12; if we 
have less than 12 capturing groups,
+            // then we want to truncate the 2 and use capturing group 1; if we 
don't have a capturing group then
+            // we want to truncate the 1 and get 0.
+            while (backRefIndex > numCapturingGroups && backRefIndex >= 10) {
+                backRefIndex /= 10;
+            }
+
+            if (backRefIndex > numCapturingGroups) {
+                final StringBuilder sb = new StringBuilder(value.length() + 1);
+                final int groupStart = backRefMatcher.start(1);
+
+                sb.append(value.substring(0, groupStart - 1));
+                sb.append("\\");
+                sb.append(value.substring(groupStart - 1));
+                value = sb.toString();
+            }
+        }
+
+        return value;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-provider-api/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-provider-api/pom.xml 
b/nifi-registry-provider-api/pom.xml
index 294eac4..ecf77bd 100644
--- a/nifi-registry-provider-api/pom.xml
+++ b/nifi-registry-provider-api/pom.xml
@@ -25,11 +25,5 @@
     <packaging>jar</packaging>
 
     <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-resources/src/main/resources/conf/authorized-users.xml
----------------------------------------------------------------------
diff --git 
a/nifi-registry-resources/src/main/resources/conf/authorized-users.xml 
b/nifi-registry-resources/src/main/resources/conf/authorized-users.xml
deleted file mode 100644
index 7519756..0000000
--- a/nifi-registry-resources/src/main/resources/conf/authorized-users.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<!--
-  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.
--->
-<users>
-    <!--
-    <user dn="[user dn]"></user>
-    -->
-</users>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-resources/src/main/resources/conf/authorizers.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-resources/src/main/resources/conf/authorizers.xml 
b/nifi-registry-resources/src/main/resources/conf/authorizers.xml
new file mode 100644
index 0000000..e4696bb
--- /dev/null
+++ b/nifi-registry-resources/src/main/resources/conf/authorizers.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  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.
+-->
+<!--
+    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 its 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.
+
+        - 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.registry.authorization.file.FileUserGroupProvider</class>
+        <property name="Users File">./conf/users.xml</property>
+        <property name="Initial User Identity 1"><!--CN=abc, 
OU=xyz--></property>
+    </userGroupProvider>
+
+    <!--
+        The CompositeUserGroupProvider will provide support for retrieving 
users and groups from multiple sources.
+
+        - User Group Provider [unique key] - The identifier of user group 
providers to load from. The name of
+            each property must be unique, for example: "User Group Provider 
A", "User Group Provider B",
+            "User Group Provider C" or "User Group Provider 1", "User Group 
Provider 2", "User Group Provider 3"
+
+            NOTE: Any identity mapping rules specified in nifi.properties are 
not applied in this implementation. This behavior
+            would need to be applied by the base implementation.
+    -->
+    <!-- To enable the composite-user-group-provider remove 2 lines. This is 1 
of 2.
+    <userGroupProvider>
+        <identifier>composite-user-group-provider</identifier>
+        <class>org.apache.nifi.authorization.CompositeUserGroupProvider</class>
+        <property name="User Group Provider 1"></property>
+    </userGroupProvider>
+    To enable the composite-user-group-provider remove 2 lines. This is 2 of 
2. -->
+
+    <!--
+        The CompositeConfigurableUserGroupProvider will provide support for 
retrieving users and groups from multiple sources.
+        Additionally, a single configurable user group provider is required. 
Users from the configurable user group provider
+        are configurable, however users loaded from one of the User Group 
Provider [unique key] will not be.
+
+        - Configurable User Group Provider - A configurable user group 
provider.
+
+        - User Group Provider [unique key] - The identifier of user group 
providers to load from. The name of
+            each property must be unique, for example: "User Group Provider 
A", "User Group Provider B",
+            "User Group Provider C" or "User Group Provider 1", "User Group 
Provider 2", "User Group Provider 3"
+
+            NOTE: Any identity mapping rules specified in nifi.properties are 
not applied in this implementation. This behavior
+            would need to be applied by the base implementation.
+    -->
+    <!-- To enable the composite-configurable-user-group-provider remove 2 
lines. This is 1 of 2.
+    <userGroupProvider>
+        <identifier>composite-configurable-user-group-provider</identifier>
+        
<class>org.apache.nifi.authorization.CompositeConfigurableUserGroupProvider</class>
+        <property name="Configurable User Group 
Provider">file-user-group-provider</property>
+        <property name="User Group Provider 1"></property>
+    </userGroupProvider>
+    To enable the composite-configurable-user-group-provider remove 2 lines. 
This is 2 of 2. -->
+
+    <!--
+        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. This property will only be 
used when there
+            are no other policies defined.
+
+            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.
+
+        - 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.registry.authorization.file.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=abc, OU=xyz 
--></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.registry.authorization.StandardManagedAuthorizer</class>
+        <property name="Access Policy 
Provider">file-access-policy-provider</property>
+    </authorizer>
+
+</authorizers>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
----------------------------------------------------------------------
diff --git 
a/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties 
b/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
index db62768..3b3b2ab 100644
--- a/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
+++ b/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
@@ -31,7 +31,8 @@ 
nifi.registry.security.truststore=${nifi.registry.security.truststore}
 nifi.registry.security.truststoreType=${nifi.registry.security.truststoreType}
 
nifi.registry.security.truststorePasswd=${nifi.registry.security.truststorePasswd}
 nifi.registry.security.needClientAuth=${nifi.registry.security.needClientAuth}
-nifi.registry.security.authorized.users=${nifi.registry.security.authorized.users}
+nifi.registry.security.authorizers.configuration.file=${nifi.registry.security.authorizers.configuration.file}
+nifi.registry.security.authorizer=${nifi.registry.security.authorizer}
 
 # providers properties #
 
nifi.registry.providers.configuration.file=${nifi.registry.providers.configuration.file}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-security-api-impl/pom.xml 
b/nifi-registry-security-api-impl/pom.xml
new file mode 100644
index 0000000..4f2e29e
--- /dev/null
+++ b/nifi-registry-security-api-impl/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <artifactId>nifi-registry</artifactId>
+        <groupId>org.apache.nifi.registry</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>nifi-registry-security-api-impl</artifactId>
+    <packaging>jar</packaging>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/xsd</directory>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>authorizations</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                
<source>src/main/xsd/authorizations.xsd</source>
+                            </sources>
+                            
<packageName>org.apache.nifi.registry.authorization.file.generated</packageName>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>tenants</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>src/main/xsd/tenants.xsd</source>
+                            </sources>
+                            
<packageName>org.apache.nifi.registry.authorization.file.tenants.generated</packageName>
+                            <clearOutputDir>false</clearOutputDir>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    
<outputDirectory>${project.build.directory}/generated-sources/jaxb</outputDirectory>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <configuration>
+                    
<excludes>**/authorization/file/generated/*.java,**/authorization/file/tenants/generated/*.java</excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi.registry</groupId>
+            <artifactId>nifi-registry-properties</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.registry</groupId>
+            <artifactId>nifi-registry-security-api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
new file mode 100644
index 0000000..5313911
--- /dev/null
+++ 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java
@@ -0,0 +1,822 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.authorization;
+
+import 
org.apache.nifi.registry.authorization.exception.AuthorizationAccessException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerCreationException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException;
+import 
org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An Authorizer that provides management of users, groups, and policies.
+ */
+public abstract class AbstractPolicyBasedAuthorizer implements 
ManagedAuthorizer {
+
+    static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = 
DocumentBuilderFactory.newInstance();
+    static final XMLOutputFactory XML_OUTPUT_FACTORY = 
XMLOutputFactory.newInstance();
+
+    static final String USER_ELEMENT = "user";
+    static final String GROUP_USER_ELEMENT = "groupUser";
+    static final String GROUP_ELEMENT = "group";
+    static final String POLICY_ELEMENT = "policy";
+    static final String POLICY_USER_ELEMENT = "policyUser";
+    static final String POLICY_GROUP_ELEMENT = "policyGroup";
+    static final String IDENTIFIER_ATTR = "identifier";
+    static final String IDENTITY_ATTR = "identity";
+    static final String NAME_ATTR = "name";
+    static final String RESOURCE_ATTR = "resource";
+    static final String ACTIONS_ATTR = "actions";
+
+    @Override
+    public final void onConfigured(final AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+        doOnConfigured(configurationContext);
+    }
+
+    /**
+     * Allows sub-classes to take action when onConfigured is called.
+     *
+     * @param configurationContext the configuration context
+     * @throws AuthorizerCreationException if an error occurs during 
onConfigured process
+     */
+    protected abstract void doOnConfigured(final 
AuthorizerConfigurationContext configurationContext) throws 
AuthorizerCreationException;
+
+    @Override
+    public final AuthorizationResult authorize(AuthorizationRequest request) 
throws AuthorizationAccessException {
+        final UsersAndAccessPolicies usersAndAccessPolicies = 
getUsersAndAccessPolicies();
+        final String resourceIdentifier = 
request.getResource().getIdentifier();
+
+        final AccessPolicy policy = 
usersAndAccessPolicies.getAccessPolicy(resourceIdentifier, request.getAction());
+        if (policy == null) {
+            return AuthorizationResult.resourceNotFound();
+        }
+
+        final User user = 
usersAndAccessPolicies.getUser(request.getIdentity());
+        if (user == null) {
+            return AuthorizationResult.denied(String.format("Unknown user with 
identity '%s'.", request.getIdentity()));
+        }
+
+        final Set<Group> userGroups = 
usersAndAccessPolicies.getGroups(user.getIdentity());
+        if (policy.getUsers().contains(user.getIdentifier()) || 
containsGroup(userGroups, policy)) {
+            return AuthorizationResult.approved();
+        }
+
+        return 
AuthorizationResult.denied(request.getExplanationSupplier().get());
+    }
+
+    /**
+     * Determines if the policy contains one of the user's groups.
+     *
+     * @param userGroups the set of the user's groups
+     * @param policy the policy
+     * @return true if one of the Groups in userGroups is contained in the 
policy
+     */
+    private boolean containsGroup(final Set<Group> userGroups, final 
AccessPolicy policy) {
+        if (userGroups.isEmpty() || policy.getGroups().isEmpty()) {
+            return false;
+        }
+
+        for (Group userGroup : userGroups) {
+            if (policy.getGroups().contains(userGroup.getIdentifier())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 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
+     */
+    public final synchronized Group addGroup(Group group) throws 
AuthorizationAccessException {
+        return doAddGroup(group);
+    }
+
+    /**
+     * 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
+     */
+    public abstract Group doAddGroup(Group group) throws 
AuthorizationAccessException;
+
+    /**
+     * Retrieves a Group by id.
+     *
+     * @param identifier the identifier of the Group to retrieve
+     * @return the Group with the given identifier, or null if no matching 
group was found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    public abstract Group getGroup(String identifier) 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
+     */
+    public final synchronized Group updateGroup(Group group) throws 
AuthorizationAccessException {
+        return doUpdateGroup(group);
+    }
+
+    /**
+     * 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
+     */
+    public abstract Group doUpdateGroup(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
+     */
+    public abstract Group deleteGroup(Group group) throws 
AuthorizationAccessException;
+
+    /**
+     * Deletes the group with the given identifier.
+     *
+     * @param groupIdentifier the id of 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
+     */
+    public abstract Group deleteGroup(String groupIdentifier) throws 
AuthorizationAccessException;
+
+    /**
+     * Retrieves all groups.
+     *
+     * @return a list of groups
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    public abstract Set<Group> getGroups() throws AuthorizationAccessException;
+
+
+    /**
+     * 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
+     */
+    public final synchronized User addUser(User user) throws 
AuthorizationAccessException {
+        return doAddUser(user);
+    }
+
+    /**
+     * 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
+     */
+    public abstract User doAddUser(User user) throws 
AuthorizationAccessException;
+
+    /**
+     * Retrieves the user with the given identifier.
+     *
+     * @param identifier the id of the user to retrieve
+     * @return the user with the given id, or null if no matching user was 
found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    public abstract User getUser(String identifier) throws 
AuthorizationAccessException;
+
+    /**
+     * Retrieves the user with the given identity.
+     *
+     * @param identity the identity of the user to retrieve
+     * @return the user with the given identity, or null if no matching user 
was found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    public abstract User getUserByIdentity(String identity) 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
+     */
+    public final synchronized User updateUser(final User user) throws 
AuthorizationAccessException {
+        return doUpdateUser(user);
+    }
+
+    /**
+     * 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
+     */
+    public abstract User doUpdateUser(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
+     */
+    public abstract User deleteUser(User user) throws 
AuthorizationAccessException;
+
+    /**
+     * Deletes the user with the given id.
+     *
+     * @param userIdentifier the identifier of 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
+     */
+    public abstract User deleteUser(String userIdentifier) throws 
AuthorizationAccessException;
+
+    /**
+     * Retrieves all users.
+     *
+     * @return a list of users
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    public abstract Set<User> getUsers() throws AuthorizationAccessException;
+
+    /**
+     * 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
+     */
+    public final synchronized AccessPolicy addAccessPolicy(AccessPolicy 
accessPolicy) throws AuthorizationAccessException {
+        return doAddAccessPolicy(accessPolicy);
+    }
+
+    /**
+     * Adds the given policy.
+     *
+     * @param accessPolicy the policy to add
+     * @return the policy that was added
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    protected abstract AccessPolicy doAddAccessPolicy(AccessPolicy 
accessPolicy) 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
+     */
+    public abstract AccessPolicy getAccessPolicy(String identifier) 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
+     */
+    public abstract AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) 
throws AuthorizationAccessException;
+
+    /**
+     * Deletes the given policy.
+     *
+     * @param policy 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
+     */
+    public abstract AccessPolicy deleteAccessPolicy(AccessPolicy policy) 
throws AuthorizationAccessException;
+
+    /**
+     * Deletes the policy with the given id.
+     *
+     * @param policyIdentifier the id of 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
+     */
+    public abstract AccessPolicy deleteAccessPolicy(String policyIdentifier) 
throws AuthorizationAccessException;
+
+    /**
+     * Retrieves all access policies.
+     *
+     * @return a list of policies
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    public abstract Set<AccessPolicy> getAccessPolicies() throws 
AuthorizationAccessException;
+
+    /**
+     * Returns the UserAccessPolicies instance.
+     *
+     * @return the UserAccessPolicies instance
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    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 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);
+            final Element rootElement = document.getDocumentElement();
+
+            // parse all the users and add them to the current authorizer
+            NodeList userNodes = 
rootElement.getElementsByTagName(USER_ELEMENT);
+            for (int i=0; i < userNodes.getLength(); i++) {
+                Node userNode = userNodes.item(i);
+                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);
+                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);
+                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) {
+        final User.Builder builder = new User.Builder()
+                .identifier(element.getAttribute(IDENTIFIER_ATTR))
+                .identity(element.getAttribute(IDENTITY_ATTR));
+
+        return builder.build();
+    }
+
+    private Group parseGroup(final Element element) {
+        final Group.Builder builder = new Group.Builder()
+                .identifier(element.getAttribute(IDENTIFIER_ATTR))
+                .name(element.getAttribute(NAME_ATTR));
+
+        NodeList groupUsers = element.getElementsByTagName(GROUP_USER_ELEMENT);
+        for (int i=0; i < groupUsers.getLength(); i++) {
+            Element groupUserNode = (Element) groupUsers.item(i);
+            builder.addUser(groupUserNode.getAttribute(IDENTIFIER_ATTR));
+        }
+
+        return builder.build();
+    }
+
+    private AccessPolicy parsePolicy(final Element element) {
+        final AccessPolicy.Builder builder = new AccessPolicy.Builder()
+                .identifier(element.getAttribute(IDENTIFIER_ATTR))
+                .resource(element.getAttribute(RESOURCE_ATTR));
+
+        final String actions = element.getAttribute(ACTIONS_ATTR);
+        if (actions.equals(RequestAction.READ.name())) {
+            builder.action(RequestAction.READ);
+        } else if (actions.equals(RequestAction.WRITE.name())) {
+            builder.action(RequestAction.WRITE);
+        } else {
+            throw new IllegalStateException("Unknown Policy Action: " + 
actions);
+        }
+
+        NodeList policyUsers = 
element.getElementsByTagName(POLICY_USER_ELEMENT);
+        for (int i=0; i < policyUsers.getLength(); i++) {
+            Element policyUserNode = (Element) policyUsers.item(i);
+            builder.addUser(policyUserNode.getAttribute(IDENTIFIER_ATTR));
+        }
+
+        NodeList policyGroups = 
element.getElementsByTagName(POLICY_GROUP_ELEMENT);
+        for (int i=0; i < policyGroups.getLength(); i++) {
+            Element policyGroupNode = (Element) policyGroups.item(i);
+            builder.addGroup(policyGroupNode.getAttribute(IDENTIFIER_ATTR));
+        }
+
+        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 deleteAccessPolicy(String 
accessPolicyIdentifier) throws AuthorizationAccessException {
+                return 
AbstractPolicyBasedAuthorizer.this.deleteAccessPolicy(accessPolicyIdentifier);
+            }
+
+            @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 User deleteUser(String userIdentifier) throws 
AuthorizationAccessException {
+                        return 
AbstractPolicyBasedAuthorizer.this.deleteUser(userIdentifier);
+                    }
+
+                    @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 Group deleteGroup(String groupIdentifier) throws 
AuthorizationAccessException {
+                        return 
AbstractPolicyBasedAuthorizer.this.deleteGroup(groupIdentifier);
+                    }
+
+                    @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,
+     * groups, and policies.
+     *
+     * @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();
+
+        XMLStreamWriter writer = null;
+        final StringWriter out = new StringWriter();
+        try {
+            writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+            writer.writeStartDocument();
+            writer.writeStartElement("authorizations");
+
+            for (User user : users) {
+                writeUser(writer, user);
+            }
+            for (Group group : groups) {
+                writeGroup(writer, group);
+            }
+            for (AccessPolicy policy : policies) {
+                writePolicy(writer, policy);
+            }
+
+            writer.writeEndElement();
+            writer.writeEndDocument();
+            writer.flush();
+        } catch (XMLStreamException e) {
+            throw new AuthorizationAccessException("Unable to generate 
fingerprint", e);
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (XMLStreamException e) {
+                    // nothing to do here
+                }
+            }
+        }
+
+        return out.toString();
+    }
+
+    private void writeUser(final XMLStreamWriter writer, final User user) 
throws XMLStreamException {
+        writer.writeStartElement(USER_ELEMENT);
+        writer.writeAttribute(IDENTIFIER_ATTR, user.getIdentifier());
+        writer.writeAttribute(IDENTITY_ATTR, user.getIdentity());
+        writer.writeEndElement();
+    }
+
+    private void writeGroup(final XMLStreamWriter writer, final Group group) 
throws XMLStreamException {
+        List<String> users = new ArrayList<>(group.getUsers());
+        Collections.sort(users);
+
+        writer.writeStartElement(GROUP_ELEMENT);
+        writer.writeAttribute(IDENTIFIER_ATTR, group.getIdentifier());
+        writer.writeAttribute(NAME_ATTR, group.getName());
+
+        for (String user : users) {
+            writer.writeStartElement(GROUP_USER_ELEMENT);
+            writer.writeAttribute(IDENTIFIER_ATTR, user);
+            writer.writeEndElement();
+        }
+
+        writer.writeEndElement();
+    }
+
+    private void writePolicy(final XMLStreamWriter writer, final AccessPolicy 
policy) throws XMLStreamException {
+        // sort the users for the policy
+        List<String> policyUsers = new ArrayList<>(policy.getUsers());
+        Collections.sort(policyUsers);
+
+        // sort the groups for this policy
+        List<String> policyGroups = new ArrayList<>(policy.getGroups());
+        Collections.sort(policyGroups);
+
+        writer.writeStartElement(POLICY_ELEMENT);
+        writer.writeAttribute(IDENTIFIER_ATTR, policy.getIdentifier());
+        writer.writeAttribute(RESOURCE_ATTR, policy.getResource());
+        writer.writeAttribute(ACTIONS_ATTR, policy.getAction().name());
+
+        for (String policyUser : policyUsers) {
+            writer.writeStartElement(POLICY_USER_ELEMENT);
+            writer.writeAttribute(IDENTIFIER_ATTR, policyUser);
+            writer.writeEndElement();
+        }
+
+        for (String policyGroup : policyGroups) {
+            writer.writeStartElement(POLICY_GROUP_ELEMENT);
+            writer.writeAttribute(IDENTIFIER_ATTR, policyGroup);
+            writer.writeEndElement();
+        }
+
+        writer.writeEndElement();
+    }
+
+    private List<AccessPolicy> getSortedAccessPolicies() {
+        final List<AccessPolicy> policies = new 
ArrayList<>(getAccessPolicies());
+        Collections.sort(policies, 
Comparator.comparing(AccessPolicy::getIdentifier));
+        return policies;
+    }
+
+    private List<Group> getSortedGroups() {
+        final List<Group> groups = new ArrayList<>(getGroups());
+        Collections.sort(groups, Comparator.comparing(Group::getIdentifier));
+        return groups;
+    }
+
+    private List<User> getSortedUsers() {
+        final List<User> users = new ArrayList<>(getUsers());
+        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-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java
 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java
new file mode 100644
index 0000000..798f974
--- /dev/null
+++ 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.authorization;
+
+import org.apache.nifi.registry.util.PropertyValue;
+import org.apache.nifi.registry.util.StandardPropertyValue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+public class StandardAuthorizerConfigurationContext implements 
AuthorizerConfigurationContext {
+
+    private final String identifier;
+    private final Map<String, String> properties;
+
+    public StandardAuthorizerConfigurationContext(String identifier, 
Map<String, String> properties) {
+        this.identifier = identifier;
+        this.properties = Collections.unmodifiableMap(new HashMap<String, 
String>(properties));
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    @Override
+    public PropertyValue getProperty(String property) {
+        return new StandardPropertyValue(properties.get(property));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java
 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java
new file mode 100644
index 0000000..e23a93e
--- /dev/null
+++ 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.authorization;
+
+/**
+ *
+ */
+public class StandardAuthorizerInitializationContext implements 
AuthorizerInitializationContext {
+
+    private final String identifier;
+    private final UserGroupProviderLookup userGroupProviderLookup;
+    private final AccessPolicyProviderLookup accessPolicyProviderLookup;
+    private final AuthorizerLookup authorizerLookup;
+
+    public StandardAuthorizerInitializationContext(String identifier, 
UserGroupProviderLookup userGroupProviderLookup,
+                                                   AccessPolicyProviderLookup 
accessPolicyProviderLookup, AuthorizerLookup authorizerLookup) {
+        this.identifier = identifier;
+        this.userGroupProviderLookup = userGroupProviderLookup;
+        this.accessPolicyProviderLookup = accessPolicyProviderLookup;
+        this.authorizerLookup = authorizerLookup;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public AuthorizerLookup getAuthorizerLookup() {
+        return authorizerLookup;
+    }
+
+    @Override
+    public AccessPolicyProviderLookup getAccessPolicyProviderLookup() {
+        return accessPolicyProviderLookup;
+    }
+
+    @Override
+    public UserGroupProviderLookup getUserGroupProviderLookup() {
+        return userGroupProviderLookup;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
new file mode 100644
index 0000000..8cd4fea
--- /dev/null
+++ 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.authorization;
+
+import org.apache.commons.lang3.StringUtils;
+import 
org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException;
+import org.apache.nifi.registry.util.PropertyValue;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizationAccessException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerCreationException;
+import 
org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Set;
+
+public class StandardManagedAuthorizer implements ManagedAuthorizer {
+
+    private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = 
DocumentBuilderFactory.newInstance();
+    private static final XMLOutputFactory XML_OUTPUT_FACTORY = 
XMLOutputFactory.newInstance();
+
+    private static final String USER_GROUP_PROVIDER_ELEMENT = 
"userGroupProvider";
+    private static final String ACCESS_POLICY_PROVIDER_ELEMENT = 
"accessPolicyProvider";
+
+    private AccessPolicyProviderLookup accessPolicyProviderLookup;
+    private AccessPolicyProvider accessPolicyProvider;
+    private UserGroupProvider userGroupProvider;
+
+    public StandardManagedAuthorizer() {}
+
+    // exposed for testing to inject mocks
+    public StandardManagedAuthorizer(AccessPolicyProvider 
accessPolicyProvider, UserGroupProvider userGroupProvider) {
+        this.accessPolicyProvider = accessPolicyProvider;
+        this.userGroupProvider = userGroupProvider;
+    }
+
+    @Override
+    public void initialize(AuthorizerInitializationContext 
initializationContext) throws AuthorizerCreationException {
+        accessPolicyProviderLookup = 
initializationContext.getAccessPolicyProviderLookup();
+    }
+
+    @Override
+    public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+        final PropertyValue accessPolicyProviderKey = 
configurationContext.getProperty("Access Policy Provider");
+        if (!accessPolicyProviderKey.isSet()) {
+            throw new AuthorizerCreationException("The Access Policy Provider 
must be set.");
+        }
+
+        accessPolicyProvider = 
accessPolicyProviderLookup.getAccessPolicyProvider(accessPolicyProviderKey.getValue());
+
+        // ensure the desired access policy provider was found
+        if (accessPolicyProvider == null) {
+            throw new AuthorizerCreationException(String.format("Unable to 
locate configured Access Policy Provider: %s", accessPolicyProviderKey));
+        }
+
+        userGroupProvider = accessPolicyProvider.getUserGroupProvider();
+
+        // ensure the desired access policy provider has a user group provider
+        if (userGroupProvider == null) {
+            throw new AuthorizerCreationException(String.format("Configured 
Access Policy Provider %s does not contain a User Group Provider", 
accessPolicyProviderKey));
+        }
+    }
+
+    @Override
+    public AuthorizationResult authorize(AuthorizationRequest request) throws 
AuthorizationAccessException {
+        final String resourceIdentifier = 
request.getResource().getIdentifier();
+        final AccessPolicy policy = 
accessPolicyProvider.getAccessPolicy(resourceIdentifier, request.getAction());
+        if (policy == null) {
+            return AuthorizationResult.resourceNotFound();
+        }
+
+        final UserAndGroups userAndGroups = 
userGroupProvider.getUserAndGroups(request.getIdentity());
+
+        final User user = userAndGroups.getUser();
+        if (user == null) {
+            return AuthorizationResult.denied(String.format("Unknown user with 
identity '%s'.", request.getIdentity()));
+        }
+
+        final Set<Group> userGroups = userAndGroups.getGroups();
+        if (policy.getUsers().contains(user.getIdentifier()) || 
containsGroup(userGroups, policy)) {
+            return AuthorizationResult.approved();
+        }
+
+        return 
AuthorizationResult.denied(request.getExplanationSupplier().get());
+    }
+
+    /**
+     * Determines if the policy contains one of the user's groups.
+     *
+     * @param userGroups the set of the user's groups
+     * @param policy the policy
+     * @return true if one of the Groups in userGroups is contained in the 
policy
+     */
+    private boolean containsGroup(final Set<Group> userGroups, final 
AccessPolicy policy) {
+        if (userGroups == null || userGroups.isEmpty() || 
policy.getGroups().isEmpty()) {
+            return false;
+        }
+
+        for (Group userGroup : userGroups) {
+            if (policy.getGroups().contains(userGroup.getIdentifier())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public String getFingerprint() throws AuthorizationAccessException {
+        XMLStreamWriter writer = null;
+        final StringWriter out = new StringWriter();
+        try {
+            writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+            writer.writeStartDocument();
+            writer.writeStartElement("managedAuthorizations");
+
+            writer.writeStartElement(ACCESS_POLICY_PROVIDER_ELEMENT);
+            if (accessPolicyProvider instanceof 
ConfigurableAccessPolicyProvider) {
+                writer.writeCharacters(((ConfigurableAccessPolicyProvider) 
accessPolicyProvider).getFingerprint());
+            }
+            writer.writeEndElement();
+
+            writer.writeStartElement(USER_GROUP_PROVIDER_ELEMENT);
+            if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
+                writer.writeCharacters(((ConfigurableUserGroupProvider) 
userGroupProvider).getFingerprint());
+            }
+            writer.writeEndElement();
+
+            writer.writeEndElement();
+            writer.writeEndDocument();
+            writer.flush();
+        } catch (XMLStreamException e) {
+            throw new AuthorizationAccessException("Unable to generate 
fingerprint", e);
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (XMLStreamException e) {
+                    // nothing to do here
+                }
+            }
+        }
+
+        return out.toString();
+    }
+
+    @Override
+    public void inheritFingerprint(String fingerprint) throws 
AuthorizationAccessException {
+        if (StringUtils.isBlank(fingerprint)) {
+            return;
+        }
+
+        final FingerprintHolder fingerprintHolder = 
parseFingerprint(fingerprint);
+
+        if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint()) 
&& accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
+            ((ConfigurableAccessPolicyProvider) 
accessPolicyProvider).inheritFingerprint(fingerprintHolder.getPolicyFingerprint());
+        }
+
+        if 
(StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint()) && 
userGroupProvider instanceof ConfigurableUserGroupProvider) {
+            ((ConfigurableUserGroupProvider) 
userGroupProvider).inheritFingerprint(fingerprintHolder.getUserGroupFingerprint());
+        }
+    }
+
+    @Override
+    public void checkInheritability(String proposedFingerprint) throws 
AuthorizationAccessException, UninheritableAuthorizationsException {
+        final FingerprintHolder fingerprintHolder = 
parseFingerprint(proposedFingerprint);
+
+        if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint())) {
+            if (accessPolicyProvider instanceof 
ConfigurableAccessPolicyProvider) {
+                ((ConfigurableAccessPolicyProvider) 
accessPolicyProvider).checkInheritability(fingerprintHolder.getPolicyFingerprint());
+            } else {
+                throw new UninheritableAuthorizationsException("Policy 
fingerprint is not blank and the configured AccessPolicyProvider does not 
support fingerprinting.");
+            }
+        }
+
+        if 
(StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint())) {
+            if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
+                ((ConfigurableUserGroupProvider) 
userGroupProvider).checkInheritability(fingerprintHolder.getUserGroupFingerprint());
+            } else {
+                throw new UninheritableAuthorizationsException("User/Group 
fingerprint is not blank and the configured UserGroupProvider does not support 
fingerprinting.");
+            }
+        }
+    }
+
+    private final FingerprintHolder parseFingerprint(final String fingerprint) 
throws AuthorizationAccessException {
+        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);
+            final Element rootElement = document.getDocumentElement();
+
+            final NodeList accessPolicyProviderList = 
rootElement.getElementsByTagName(ACCESS_POLICY_PROVIDER_ELEMENT);
+            if (accessPolicyProviderList.getLength() != 1) {
+                throw new AuthorizationAccessException(String.format("Only one 
%s element is allowed: %s", ACCESS_POLICY_PROVIDER_ELEMENT, fingerprint));
+            }
+
+            final NodeList userGroupProviderList = 
rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT);
+            if (userGroupProviderList.getLength() != 1) {
+                throw new AuthorizationAccessException(String.format("Only one 
%s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint));
+            }
+
+            final Node accessPolicyProvider = accessPolicyProviderList.item(0);
+            final Node userGroupProvider = userGroupProviderList.item(0);
+            return new 
FingerprintHolder(accessPolicyProvider.getTextContent(), 
userGroupProvider.getTextContent());
+        } catch (SAXException | ParserConfigurationException | IOException e) {
+            throw new AuthorizationAccessException("Unable to parse 
fingerprint", e);
+        }
+    }
+
+    @Override
+    public AccessPolicyProvider getAccessPolicyProvider() {
+        return accessPolicyProvider;
+    }
+
+    @Override
+    public void preDestruction() throws AuthorizerDestructionException {
+
+    }
+
+    private static class FingerprintHolder {
+        private final String policyFingerprint;
+        private final String userGroupFingerprint;
+
+        public FingerprintHolder(String policyFingerprint, String 
userGroupFingerprint) {
+            this.policyFingerprint = policyFingerprint;
+            this.userGroupFingerprint = userGroupFingerprint;
+        }
+
+        public String getPolicyFingerprint() {
+            return policyFingerprint;
+        }
+
+        public String getUserGroupFingerprint() {
+            return userGroupFingerprint;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java
 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java
new file mode 100644
index 0000000..7e05c9c
--- /dev/null
+++ 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.authorization;
+
+import java.util.Set;
+
+/**
+ * A holder object to provide atomic access to policies for a given resource 
and users by
+ * identity. Implementations must ensure consistent access to the data backing 
this instance.
+ */
+public interface UsersAndAccessPolicies {
+
+    /**
+     * Retrieves the set of access policies for a given resource and action.
+     *
+     * @param resourceIdentifier the resource identifier to retrieve policies 
for
+     * @param action the action to retrieve policies for
+     * @return the access policy for the given resource and action
+     */
+    AccessPolicy getAccessPolicy(final String resourceIdentifier, final 
RequestAction action);
+
+    /**
+     * Retrieves a user by an identity string.
+     *
+     * @param identity the identity of the user to retrieve
+     * @return the user with the given identity
+     */
+    User getUser(final String identity);
+
+    /**
+     * Retrieves the groups for a given user identity.
+     *
+     * @param userIdentity a user identity
+     * @return the set of groups for the given user identity
+     */
+    Set<Group> getGroups(final String userIdentity);
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java
 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java
new file mode 100644
index 0000000..131238c
--- /dev/null
+++ 
b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.authorization.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ *
+ */
+@Documented
+@Target({ElementType.FIELD, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface AuthorizerContext {
+}

Reply via email to