http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java
new file mode 100644
index 0000000..314b9ec
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java
@@ -0,0 +1,796 @@
+/*
+ * 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.annotation.AuthorizerContext;
+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.apache.nifi.registry.authorization.generated.Authorizers;
+import org.apache.nifi.registry.authorization.generated.Prop;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.registry.provider.StandardProviderFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This implementation of AuthorizerFactory in NiFi Registry is based on a 
combination of
+ * NiFi's AuthorizerFactory and AuthorizerFactoryBean.
+ */
+public class StandardAuthorizerFactory implements AuthorizerFactory, 
UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup{
+
+    private static final Logger logger = 
LoggerFactory.getLogger(StandardProviderFactory.class);
+
+    private static final String AUTHORIZERS_XSD = "/authorizers.xsd";
+    private static final String JAXB_GENERATED_PATH = 
"org.apache.nifi.registry.authorization.generated";
+    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
+
+    /**
+     * Load the JAXBContext.
+     */
+    private static JAXBContext initializeJaxbContext() {
+        try {
+            return JAXBContext.newInstance(JAXB_GENERATED_PATH, 
StandardAuthorizerFactory.class.getClassLoader());
+        } catch (JAXBException e) {
+            throw new RuntimeException("Unable to create JAXBContext.", e);
+        }
+    }
+
+    private final NiFiRegistryProperties properties;
+    private Authorizer authorizer;
+    private final Map<String, UserGroupProvider> userGroupProviders = new 
HashMap<>();
+    private final Map<String, AccessPolicyProvider> accessPolicyProviders = 
new HashMap<>();
+    private final Map<String, Authorizer> authorizers = new HashMap<>();
+
+    public StandardAuthorizerFactory(final NiFiRegistryProperties properties) {
+        this.properties = properties;
+
+        if (this.properties == null) {
+            throw new IllegalStateException("NiFiRegistryProperties cannot be 
null");
+        }
+    }
+
+    /***** UserGroupProviderLookup *****/
+
+    @Override
+    public UserGroupProvider getUserGroupProvider(String identifier) {
+        return userGroupProviders.get(identifier);
+    }
+
+    /***** AccessPolicyProviderLookup *****/
+
+    @Override
+    public AccessPolicyProvider getAccessPolicyProvider(String identifier) {
+        return accessPolicyProviders.get(identifier);
+    }
+
+
+    /***** AuthorizerLookup *****/
+
+    @Override
+    public Authorizer getAuthorizer(String identifier) {
+        return authorizers.get(identifier);
+    }
+
+
+    /***** AuthorizerFactory *****/
+
+    @Override
+    public void initialize() throws AuthorizerFactoryException {
+//        if (authorizerHolder.get() == null) {
+//            final File authorizersConfigFile = 
properties.getAuthorizersConfigurationFile();
+//            if (authorizersConfigFile.exists()) {
+//                try {
+//                    // find the schema
+//                    final SchemaFactory schemaFactory = 
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+//                    final Schema schema = 
schemaFactory.newSchema(StandardProviderFactory.class.getResource(AUTHORIZERS_XSD));
+//
+//                    // attempt to unmarshal
+//                    final Unmarshaller unmarshaller = 
JAXB_CONTEXT.createUnmarshaller();
+//                    unmarshaller.setSchema(schema);
+//
+//                    // set the holder for later use
+//                    final JAXBElement<Authorizers> element = 
unmarshaller.unmarshal(new StreamSource(authorizersConfigFile), 
Authorizers.class);
+//                    authorizerHolder.set(element.getValue());
+//                } catch (SAXException | JAXBException e) {
+//                    throw new AuthorizerFactoryException("Unable to load the 
authorizer configuration file at: " + authorizersConfigFile.getAbsolutePath(), 
e);
+//                }
+//            } else {
+//                throw new AuthorizerFactoryException("Unable to find the 
providers configuration file at " + authorizersConfigFile.getAbsolutePath());
+//            }
+//        }
+    }
+
+    @Override
+    public Authorizer getAuthorizer() throws AuthorizerFactoryException {
+        if (authorizer == null) {
+            if (properties.getSslPort() == null) {
+                // use a default authorizer... only allowable when running not 
securely
+                authorizer = createDefaultAuthorizer();
+            } else {
+                // look up the authorizer to use
+                final String authorizerIdentifier = 
properties.getProperty(NiFiRegistryProperties.SECURITY_AUTHORIZER);
+
+                // ensure the authorizer class name was specified
+                if (StringUtils.isBlank(authorizerIdentifier)) {
+                    throw new AuthorizerFactoryException("When running 
securely, the authorizer identifier must be specified in the nifi properties 
file.");
+                } else {
+
+                    try {
+                        final Authorizers authorizerConfiguration = 
loadAuthorizersConfiguration();
+
+                        // create each user group provider
+                        for (final 
org.apache.nifi.registry.authorization.generated.UserGroupProvider 
userGroupProvider : authorizerConfiguration.getUserGroupProvider()) {
+                            
userGroupProviders.put(userGroupProvider.getIdentifier(), 
createUserGroupProvider(userGroupProvider.getIdentifier(), 
userGroupProvider.getClazz()));
+                        }
+
+                        // configure each user group provider
+                        for (final 
org.apache.nifi.registry.authorization.generated.UserGroupProvider provider : 
authorizerConfiguration.getUserGroupProvider()) {
+                            final UserGroupProvider instance = 
userGroupProviders.get(provider.getIdentifier());
+                            
instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), 
provider.getProperty()));
+                        }
+
+                        // create each access policy provider
+                        for (final 
org.apache.nifi.registry.authorization.generated.AccessPolicyProvider 
accessPolicyProvider : authorizerConfiguration.getAccessPolicyProvider()) {
+                            
accessPolicyProviders.put(accessPolicyProvider.getIdentifier(), 
createAccessPolicyProvider(accessPolicyProvider.getIdentifier(), 
accessPolicyProvider.getClazz()));
+                        }
+
+                        // configure each access policy provider
+                        for (final 
org.apache.nifi.registry.authorization.generated.AccessPolicyProvider provider 
: authorizerConfiguration.getAccessPolicyProvider()) {
+                            final AccessPolicyProvider instance = 
accessPolicyProviders.get(provider.getIdentifier());
+                            
instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), 
provider.getProperty()));
+                        }
+
+                        // create each authorizer
+                        for (final 
org.apache.nifi.registry.authorization.generated.Authorizer authorizer : 
authorizerConfiguration.getAuthorizer()) {
+                            authorizers.put(authorizer.getIdentifier(), 
createAuthorizer(authorizer.getIdentifier(), authorizer.getClazz(), 
authorizer.getClasspath()));
+                        }
+
+                        // configure each authorizer
+                        for (final 
org.apache.nifi.registry.authorization.generated.Authorizer provider : 
authorizerConfiguration.getAuthorizer()) {
+                            final Authorizer instance = 
authorizers.get(provider.getIdentifier());
+                            
instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), 
provider.getProperty()));
+                        }
+
+                        // get the authorizer instance
+                        authorizer = getAuthorizer(authorizerIdentifier);
+
+                        // ensure it was found
+                        if (authorizer == null) {
+                            throw new 
AuthorizerFactoryException(String.format("The specified authorizer '%s' could 
not be found.", authorizerIdentifier));
+                        }
+                    } catch (Exception e) {
+                        throw new AuthorizerFactoryException("Failed to 
construct Authorizer.", e);
+                    }
+                }
+            }
+        }
+        return authorizer;
+    }
+
+    private Authorizers loadAuthorizersConfiguration() throws Exception {
+        final File authorizersConfigurationFile = 
properties.getAuthorizersConfigurationFile();
+
+        // load the authorizers from the specified file
+        if (authorizersConfigurationFile.exists()) {
+            try {
+                // find the schema
+                final SchemaFactory schemaFactory = 
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+                final Schema schema = 
schemaFactory.newSchema(Authorizers.class.getResource(AUTHORIZERS_XSD));
+
+                // attempt to unmarshal
+                final Unmarshaller unmarshaller = 
JAXB_CONTEXT.createUnmarshaller();
+                unmarshaller.setSchema(schema);
+                final JAXBElement<Authorizers> element = 
unmarshaller.unmarshal(new StreamSource(authorizersConfigurationFile), 
Authorizers.class);
+                return element.getValue();
+            } catch (SAXException | JAXBException e) {
+                throw new Exception("Unable to load the authorizer 
configuration file at: " + authorizersConfigurationFile.getAbsolutePath(), e);
+            }
+        } else {
+            throw new Exception("Unable to find the authorizer configuration 
file at " + authorizersConfigurationFile.getAbsolutePath());
+        }
+    }
+
+    private AuthorizerConfigurationContext loadAuthorizerConfiguration(final 
String identifier, final List<Prop> properties) {
+        final Map<String, String> authorizerProperties = new HashMap<>();
+
+        for (final Prop property : properties) {
+            authorizerProperties.put(property.getName(), property.getValue());
+        }
+        return new StandardAuthorizerConfigurationContext(identifier, 
authorizerProperties);
+    }
+
+    private UserGroupProvider createUserGroupProvider(final String identifier, 
final String userGroupProviderClassName) throws Exception {
+
+        final UserGroupProvider instance;
+
+        // attempt to load the class
+        Class<?> rawUserGroupProviderClass = 
Class.forName(userGroupProviderClassName);
+        Class<? extends UserGroupProvider> userGroupProviderClass = 
rawUserGroupProviderClass.asSubclass(UserGroupProvider.class);
+
+        // otherwise create a new instance
+        Constructor constructor = userGroupProviderClass.getConstructor();
+        instance = (UserGroupProvider) constructor.newInstance();
+
+        // method injection
+        performMethodInjection(instance, userGroupProviderClass);
+
+        // field injection
+        performFieldInjection(instance, userGroupProviderClass);
+
+        // call post construction lifecycle event
+        instance.initialize(new 
StandardAuthorizerInitializationContext(identifier, this, this, this));
+
+        return instance;
+    }
+
+    private AccessPolicyProvider createAccessPolicyProvider(final String 
identifier, final String accessPolicyProviderClassName) throws Exception {
+        final AccessPolicyProvider instance;
+
+        // attempt to load the class
+        Class<?> rawAccessPolicyProviderClass = 
Class.forName(accessPolicyProviderClassName);
+        Class<? extends AccessPolicyProvider> accessPolicyClass = 
rawAccessPolicyProviderClass.asSubclass(AccessPolicyProvider.class);
+
+        // otherwise create a new instance
+        Constructor constructor = accessPolicyClass.getConstructor();
+        instance = (AccessPolicyProvider) constructor.newInstance();
+
+        // method injection
+        performMethodInjection(instance, accessPolicyClass);
+
+        // field injection
+        performFieldInjection(instance, accessPolicyClass);
+
+        // call post construction lifecycle event
+        instance.initialize(new 
StandardAuthorizerInitializationContext(identifier, this, this, this));
+
+        return instance;
+    }
+
+    private Authorizer createAuthorizer(final String identifier, final String 
authorizerClassName, final String classpathResources) throws Exception {
+        final Authorizer instance;
+        // attempt to load the class
+        Class<?> rawAuthorizerClass = Class.forName(authorizerClassName);
+        Class<? extends Authorizer> authorizerClass = 
rawAuthorizerClass.asSubclass(Authorizer.class);
+
+        // otherwise create a new instance
+        Constructor constructor = authorizerClass.getConstructor();
+        instance = (Authorizer) constructor.newInstance();
+
+        // method injection
+        performMethodInjection(instance, authorizerClass);
+
+        // field injection
+        performFieldInjection(instance, authorizerClass);
+
+        // call post construction lifecycle event
+        instance.initialize(new 
StandardAuthorizerInitializationContext(identifier, this, this, this));
+
+        // TODO, implement and test loading additional resources from the 
classpath for custom authorizer impls.
+//        if (StringUtils.isNotEmpty(classpathResources)) {
+//            URL[] urls = 
ClassLoaderUtils.getURLsForClasspath(classpathResources, null, true);
+//            authorizerClassLoader = new URLClassLoader(urls, 
authorizerClassLoader);
+//        }
+
+        return installIntegrityChecks(instance);
+    }
+
+        private void performMethodInjection(final Object instance, final Class 
authorizerClass) throws IllegalAccessException, IllegalArgumentException, 
InvocationTargetException {
+        for (final Method method : authorizerClass.getMethods()) {
+            if (method.isAnnotationPresent(AuthorizerContext.class)) {
+                // make the method accessible
+                final boolean isAccessible = method.isAccessible();
+                method.setAccessible(true);
+
+                try {
+                    final Class<?>[] argumentTypes = 
method.getParameterTypes();
+
+                    // look for setters (single argument)
+                    if (argumentTypes.length == 1) {
+                        final Class<?> argumentType = argumentTypes[0];
+
+                        // look for well known types
+                        if 
(NiFiRegistryProperties.class.isAssignableFrom(argumentType)) {
+                            // nifi properties injection
+                            method.invoke(instance, properties);
+                        }
+                    }
+                } finally {
+                    method.setAccessible(isAccessible);
+                }
+            }
+        }
+
+        final Class parentClass = authorizerClass.getSuperclass();
+        if (parentClass != null && 
Authorizer.class.isAssignableFrom(parentClass)) {
+            performMethodInjection(instance, parentClass);
+        }
+    }
+
+    private void performFieldInjection(final Object instance, final Class 
authorizerClass) throws IllegalArgumentException, IllegalAccessException {
+        for (final Field field : authorizerClass.getDeclaredFields()) {
+            if (field.isAnnotationPresent(AuthorizerContext.class)) {
+                // make the method accessible
+                final boolean isAccessible = field.isAccessible();
+                field.setAccessible(true);
+
+                try {
+                    // get the type
+                    final Class<?> fieldType = field.getType();
+
+                    // only consider this field if it isn't set yet
+                    if (field.get(instance) == null) {
+                        // look for well known types
+                        if 
(NiFiRegistryProperties.class.isAssignableFrom(fieldType)) {
+                            // nifi properties injection
+                            field.set(instance, properties);
+                        }
+                    }
+
+                } finally {
+                    field.setAccessible(isAccessible);
+                }
+            }
+        }
+
+        final Class parentClass = authorizerClass.getSuperclass();
+        if (parentClass != null && 
Authorizer.class.isAssignableFrom(parentClass)) {
+            performFieldInjection(instance, parentClass);
+        }
+    }
+
+
+    /**
+     * @return a default Authorizer to use when running unsecurely with no 
authorizer configured
+     */
+    private Authorizer createDefaultAuthorizer() {
+        return new Authorizer() {
+            @Override
+            public AuthorizationResult authorize(final AuthorizationRequest 
request) throws AuthorizationAccessException {
+                return AuthorizationResult.approved();
+            }
+
+            @Override
+            public void initialize(AuthorizerInitializationContext 
initializationContext) throws AuthorizerCreationException {
+            }
+
+            @Override
+            public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+            }
+
+            @Override
+            public void preDestruction() throws AuthorizerDestructionException 
{
+            }
+        };
+    }
+
+    public static Authorizer installIntegrityChecks(final Authorizer 
baseAuthorizer) {
+        if (baseAuthorizer instanceof ManagedAuthorizer) {
+            final ManagedAuthorizer baseManagedAuthorizer = 
(ManagedAuthorizer) baseAuthorizer;
+            return new ManagedAuthorizer() {
+                @Override
+                public String getFingerprint() throws 
AuthorizationAccessException {
+                    return baseManagedAuthorizer.getFingerprint();
+                }
+
+                @Override
+                public void inheritFingerprint(String fingerprint) throws 
AuthorizationAccessException {
+                    baseManagedAuthorizer.inheritFingerprint(fingerprint);
+                }
+
+                @Override
+                public void checkInheritability(String proposedFingerprint) 
throws AuthorizationAccessException, UninheritableAuthorizationsException {
+                    
baseManagedAuthorizer.checkInheritability(proposedFingerprint);
+                }
+
+                @Override
+                public AccessPolicyProvider getAccessPolicyProvider() {
+                    final AccessPolicyProvider baseAccessPolicyProvider = 
baseManagedAuthorizer.getAccessPolicyProvider();
+                    if (baseAccessPolicyProvider instanceof 
ConfigurableAccessPolicyProvider) {
+                        final ConfigurableAccessPolicyProvider 
baseConfigurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) 
baseAccessPolicyProvider;
+                        return new ConfigurableAccessPolicyProvider() {
+                            @Override
+                            public String getFingerprint() throws 
AuthorizationAccessException {
+                                return 
baseConfigurableAccessPolicyProvider.getFingerprint();
+                            }
+
+                            @Override
+                            public void inheritFingerprint(String fingerprint) 
throws AuthorizationAccessException {
+                                
baseConfigurableAccessPolicyProvider.inheritFingerprint(fingerprint);
+                            }
+
+                            @Override
+                            public void checkInheritability(String 
proposedFingerprint) throws AuthorizationAccessException, 
UninheritableAuthorizationsException {
+                                
baseConfigurableAccessPolicyProvider.checkInheritability(proposedFingerprint);
+                            }
+
+                            @Override
+                            public AccessPolicy addAccessPolicy(AccessPolicy 
accessPolicy) throws AuthorizationAccessException {
+                                if 
(policyExists(baseConfigurableAccessPolicyProvider, accessPolicy)) {
+                                    throw new 
IllegalStateException(String.format("Found multiple policies for '%s' with 
'%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
+                                }
+                                return 
baseConfigurableAccessPolicyProvider.addAccessPolicy(accessPolicy);
+                            }
+
+                            @Override
+                            public boolean isConfigurable(AccessPolicy 
accessPolicy) {
+                                return 
baseConfigurableAccessPolicyProvider.isConfigurable(accessPolicy);
+                            }
+
+                            @Override
+                            public AccessPolicy 
updateAccessPolicy(AccessPolicy accessPolicy) throws 
AuthorizationAccessException {
+                                if 
(!baseConfigurableAccessPolicyProvider.isConfigurable(accessPolicy)) {
+                                    throw new IllegalArgumentException("The 
specified access policy is not support modification.");
+                                }
+                                return 
baseConfigurableAccessPolicyProvider.updateAccessPolicy(accessPolicy);
+                            }
+
+                            @Override
+                            public AccessPolicy 
deleteAccessPolicy(AccessPolicy accessPolicy) throws 
AuthorizationAccessException {
+                                if 
(!baseConfigurableAccessPolicyProvider.isConfigurable(accessPolicy)) {
+                                    throw new IllegalArgumentException("The 
specified access policy is not support modification.");
+                                }
+                                return 
baseConfigurableAccessPolicyProvider.deleteAccessPolicy(accessPolicy);
+                            }
+
+                            @Override
+                            public AccessPolicy deleteAccessPolicy(String 
accessPolicyIdentifier) throws AuthorizationAccessException {
+                                if 
(!baseConfigurableAccessPolicyProvider.isConfigurable(baseConfigurableAccessPolicyProvider.getAccessPolicy(accessPolicyIdentifier)))
 {
+                                    throw new IllegalArgumentException("The 
specified access policy is not support modification.");
+                                }
+                                return 
baseConfigurableAccessPolicyProvider.deleteAccessPolicy(accessPolicyIdentifier);
+                            }
+
+                            @Override
+                            public Set<AccessPolicy> getAccessPolicies() 
throws AuthorizationAccessException {
+                                return 
baseConfigurableAccessPolicyProvider.getAccessPolicies();
+                            }
+
+                            @Override
+                            public AccessPolicy getAccessPolicy(String 
identifier) throws AuthorizationAccessException {
+                                return 
baseConfigurableAccessPolicyProvider.getAccessPolicy(identifier);
+                            }
+
+                            @Override
+                            public AccessPolicy getAccessPolicy(String 
resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
+                                return 
baseConfigurableAccessPolicyProvider.getAccessPolicy(resourceIdentifier, 
action);
+                            }
+
+                            @Override
+                            public UserGroupProvider getUserGroupProvider() {
+                                final UserGroupProvider baseUserGroupProvider 
= baseConfigurableAccessPolicyProvider.getUserGroupProvider();
+                                if (baseUserGroupProvider instanceof 
ConfigurableUserGroupProvider) {
+                                    final ConfigurableUserGroupProvider 
baseConfigurableUserGroupProvider = (ConfigurableUserGroupProvider) 
baseUserGroupProvider;
+                                    return new ConfigurableUserGroupProvider() 
{
+                                        @Override
+                                        public String getFingerprint() throws 
AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getFingerprint();
+                                        }
+
+                                        @Override
+                                        public void inheritFingerprint(String 
fingerprint) throws AuthorizationAccessException {
+                                            
baseConfigurableUserGroupProvider.inheritFingerprint(fingerprint);
+                                        }
+
+                                        @Override
+                                        public void checkInheritability(String 
proposedFingerprint) throws AuthorizationAccessException, 
UninheritableAuthorizationsException {
+                                            
baseConfigurableUserGroupProvider.checkInheritability(proposedFingerprint);
+                                        }
+
+                                        @Override
+                                        public User addUser(User user) throws 
AuthorizationAccessException {
+                                            if 
(tenantExists(baseConfigurableUserGroupProvider, user.getIdentifier(), 
user.getIdentity())) {
+                                                throw new 
IllegalStateException(String.format("User/user group already exists with the 
identity '%s'.", user.getIdentity()));
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.addUser(user);
+                                        }
+
+                                        @Override
+                                        public boolean isConfigurable(User 
user) {
+                                            return 
baseConfigurableUserGroupProvider.isConfigurable(user);
+                                        }
+
+                                        @Override
+                                        public User updateUser(User user) 
throws AuthorizationAccessException {
+                                            if 
(tenantExists(baseConfigurableUserGroupProvider, user.getIdentifier(), 
user.getIdentity())) {
+                                                throw new 
IllegalStateException(String.format("User/user group already exists with the 
identity '%s'.", user.getIdentity()));
+                                            }
+                                            if 
(!baseConfigurableUserGroupProvider.isConfigurable(user)) {
+                                                throw new 
IllegalArgumentException("The specified user does not support modification.");
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.updateUser(user);
+                                        }
+
+                                        @Override
+                                        public User deleteUser(User user) 
throws AuthorizationAccessException {
+                                            if 
(!baseConfigurableUserGroupProvider.isConfigurable(user)) {
+                                                throw new 
IllegalArgumentException("The specified user does not support modification.");
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.deleteUser(user);
+                                        }
+
+                                        @Override
+                                        public User deleteUser(String 
userIdentifier) throws AuthorizationAccessException {
+                                            if 
(!baseConfigurableUserGroupProvider.isConfigurable(baseConfigurableUserGroupProvider.getUser(userIdentifier)))
 {
+                                                throw new 
IllegalArgumentException("The specified user does not support modification.");
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.deleteUser(userIdentifier);
+                                        }
+
+                                        @Override
+                                        public Group addGroup(Group group) 
throws AuthorizationAccessException {
+                                            if 
(tenantExists(baseConfigurableUserGroupProvider, group.getIdentifier(), 
group.getName())) {
+                                                throw new 
IllegalStateException(String.format("User/user group already exists with the 
identity '%s'.", group.getName()));
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.addGroup(group);
+                                        }
+
+                                        @Override
+                                        public boolean isConfigurable(Group 
group) {
+                                            return 
baseConfigurableUserGroupProvider.isConfigurable(group);
+                                        }
+
+                                        @Override
+                                        public Group updateGroup(Group group) 
throws AuthorizationAccessException {
+                                            if 
(tenantExists(baseConfigurableUserGroupProvider, group.getIdentifier(), 
group.getName())) {
+                                                throw new 
IllegalStateException(String.format("User/user group already exists with the 
identity '%s'.", group.getName()));
+                                            }
+                                            if 
(!baseConfigurableUserGroupProvider.isConfigurable(group)) {
+                                                throw new 
IllegalArgumentException("The specified group does not support modification.");
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.updateGroup(group);
+                                        }
+
+                                        @Override
+                                        public Group deleteGroup(Group group) 
throws AuthorizationAccessException {
+                                            if 
(!baseConfigurableUserGroupProvider.isConfigurable(group)) {
+                                                throw new 
IllegalArgumentException("The specified group does not support modification.");
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.deleteGroup(group);
+                                        }
+
+                                        @Override
+                                        public Group deleteGroup(String 
groupId) throws AuthorizationAccessException {
+                                            if 
(!baseConfigurableUserGroupProvider.isConfigurable(baseConfigurableUserGroupProvider.getGroup(groupId)))
 {
+                                                throw new 
IllegalArgumentException("The specified group does not support modification.");
+                                            }
+                                            return 
baseConfigurableUserGroupProvider.deleteGroup(groupId);
+                                        }
+
+                                        @Override
+                                        public Set<User> getUsers() throws 
AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getUsers();
+                                        }
+
+                                        @Override
+                                        public User getUser(String identifier) 
throws AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getUser(identifier);
+                                        }
+
+                                        @Override
+                                        public User getUserByIdentity(String 
identity) throws AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getUserByIdentity(identity);
+                                        }
+
+                                        @Override
+                                        public Set<Group> getGroups() throws 
AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getGroups();
+                                        }
+
+                                        @Override
+                                        public Group getGroup(String 
identifier) throws AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getGroup(identifier);
+                                        }
+
+                                        @Override
+                                        public UserAndGroups 
getUserAndGroups(String identity) throws AuthorizationAccessException {
+                                            return 
baseConfigurableUserGroupProvider.getUserAndGroups(identity);
+                                        }
+
+                                        @Override
+                                        public void 
initialize(UserGroupProviderInitializationContext initializationContext) throws 
AuthorizerCreationException {
+                                            
baseConfigurableUserGroupProvider.initialize(initializationContext);
+                                        }
+
+                                        @Override
+                                        public void 
onConfigured(AuthorizerConfigurationContext configurationContext) throws 
AuthorizerCreationException {
+                                            
baseConfigurableUserGroupProvider.onConfigured(configurationContext);
+                                        }
+
+                                        @Override
+                                        public void preDestruction() throws 
AuthorizerDestructionException {
+                                            
baseConfigurableUserGroupProvider.preDestruction();
+                                        }
+                                    };
+                                } else {
+                                    return baseUserGroupProvider;
+                                }
+                            }
+
+                            @Override
+                            public void 
initialize(AccessPolicyProviderInitializationContext initializationContext) 
throws AuthorizerCreationException {
+                                
baseConfigurableAccessPolicyProvider.initialize(initializationContext);
+                            }
+
+                            @Override
+                            public void 
onConfigured(AuthorizerConfigurationContext configurationContext) throws 
AuthorizerCreationException {
+                                
baseConfigurableAccessPolicyProvider.onConfigured(configurationContext);
+                            }
+
+                            @Override
+                            public void preDestruction() throws 
AuthorizerDestructionException {
+                                
baseConfigurableAccessPolicyProvider.preDestruction();
+                            }
+                        };
+                    } else {
+                        return baseAccessPolicyProvider;
+                    }
+                }
+
+                @Override
+                public AuthorizationResult authorize(AuthorizationRequest 
request) throws AuthorizationAccessException {
+                    final AuthorizationResult result = 
baseAuthorizer.authorize(request);
+
+                    // audit the authorization request
+                    audit(baseAuthorizer, request, result);
+
+                    return result;
+                }
+
+                @Override
+                public void initialize(AuthorizerInitializationContext 
initializationContext) throws AuthorizerCreationException {
+                    baseManagedAuthorizer.initialize(initializationContext);
+                }
+
+                @Override
+                public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+                    baseManagedAuthorizer.onConfigured(configurationContext);
+
+                    final AccessPolicyProvider accessPolicyProvider = 
baseManagedAuthorizer.getAccessPolicyProvider();
+                    final UserGroupProvider userGroupProvider = 
accessPolicyProvider.getUserGroupProvider();
+
+                    // ensure that only one policy per resource-action exists
+                    for (AccessPolicy accessPolicy : 
accessPolicyProvider.getAccessPolicies()) {
+                        if (policyExists(accessPolicyProvider, 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 : userGroupProvider.getUsers()) {
+                        if (tenantExists(userGroupProvider, 
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 : userGroupProvider.getGroups()) {
+                        if (tenantExists(userGroupProvider, 
group.getIdentifier(), group.getName())) {
+                            throw new 
AuthorizerCreationException(String.format("Found multiple users/user groups 
with name '%s'.", group.getName()));
+                        }
+                    }
+                }
+
+                @Override
+                public void preDestruction() throws 
AuthorizerDestructionException {
+                    baseManagedAuthorizer.preDestruction();
+                }
+            };
+        } else {
+            return new Authorizer() {
+                @Override
+                public AuthorizationResult authorize(AuthorizationRequest 
request) throws AuthorizationAccessException {
+                    final AuthorizationResult result = 
baseAuthorizer.authorize(request);
+
+                    // audit the authorization request
+                    audit(baseAuthorizer, request, result);
+
+                    return result;
+                }
+
+                @Override
+                public void initialize(AuthorizerInitializationContext 
initializationContext) throws AuthorizerCreationException {
+                    baseAuthorizer.initialize(initializationContext);
+                }
+
+                @Override
+                public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+                    baseAuthorizer.onConfigured(configurationContext);
+                }
+
+                @Override
+                public void preDestruction() throws 
AuthorizerDestructionException {
+                    baseAuthorizer.preDestruction();
+                }
+            };
+        }
+    }
+
+    private static void audit(final Authorizer authorizer, final 
AuthorizationRequest request, final AuthorizationResult result) {
+        // audit when...
+        // 1 - the authorizer supports auditing
+        // 2 - the request is an access attempt
+        // 3 - the result is either approved/denied, when resource is not 
found a subsequent request may be following with the parent resource
+        if (authorizer instanceof AuthorizationAuditor && 
request.isAccessAttempt() && 
!AuthorizationResult.Result.ResourceNotFound.equals(result.getResult())) {
+            ((AuthorizationAuditor) authorizer).auditAccessAttempt(request, 
result);
+        }
+    }
+
+    /**
+     * 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 static boolean policyExists(final AccessPolicyProvider 
accessPolicyProvider, final AccessPolicy checkAccessPolicy) {
+        for (AccessPolicy accessPolicy : 
accessPolicyProvider.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 static boolean tenantExists(final UserGroupProvider 
userGroupProvider, final String identifier, final String identity) {
+        for (User user : userGroupProvider.getUsers()) {
+            if (!user.getIdentifier().equals(identifier)
+                    && user.getIdentity().equals(identity)) {
+                return true;
+            }
+        }
+
+        for (Group group : userGroupProvider.getGroups()) {
+            if (!group.getIdentifier().equals(identifier)
+                    && group.getName().equals(identity)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java
new file mode 100644
index 0000000..5d9997f
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java
@@ -0,0 +1,122 @@
+/*
+ * 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.resource;
+
+import org.apache.nifi.registry.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.authorization.AuthorizationResult;
+import org.apache.nifi.registry.authorization.AuthorizationResult.Result;
+import org.apache.nifi.registry.authorization.Authorizer;
+import org.apache.nifi.registry.authorization.RequestAction;
+import org.apache.nifi.registry.authorization.Resource;
+import org.apache.nifi.registry.authorization.user.NiFiUser;
+
+import java.util.Map;
+
+/**
+ * Authorizable for policies of an Authorizable.
+ */
+public class AccessPolicyAuthorizable implements Authorizable, 
EnforcePolicyPermissionsThroughBaseResource {
+
+    private static final Authorizable POLICIES_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getPoliciesResource();
+        }
+    };
+
+    final Authorizable authorizable;
+
+    public AccessPolicyAuthorizable(Authorizable authorizable) {
+        this.authorizable = authorizable;
+    }
+
+    @Override
+    public Authorizable getBaseAuthorizable() {
+        return authorizable;
+    }
+
+    @Override
+    public Authorizable getParentAuthorizable() {
+        final Authorizable effectiveAuthorizable = getEffectiveAuthorizable();
+        if (effectiveAuthorizable.getParentAuthorizable() == null) {
+            return POLICIES_AUTHORIZABLE;
+        } else {
+            return new 
AccessPolicyAuthorizable(effectiveAuthorizable.getParentAuthorizable());
+        }
+    }
+
+    @Override
+    public Resource getResource() {
+        return 
ResourceFactory.getPolicyResource(getEffectiveAuthorizable().getResource());
+    }
+
+    private Authorizable getEffectiveAuthorizable() {
+        // possibly consider the base resource if the authorizable uses it to 
enforce policy permissions
+        if (authorizable instanceof 
EnforcePolicyPermissionsThroughBaseResource) {
+            final Authorizable baseAuthorizable = 
((EnforcePolicyPermissionsThroughBaseResource) 
authorizable).getBaseAuthorizable();
+
+            // if the base authorizable is for a policy, we don't want to use 
the base otherwise it would keep unwinding and would eventually
+            // evaluate to the policy of the component and not the policy of 
the policies for the component
+            if (baseAuthorizable instanceof AccessPolicyAuthorizable) {
+                return authorizable;
+            } else {
+                return baseAuthorizable;
+            }
+        } else {
+            return authorizable;
+        }
+    }
+
+    @Override
+    public AuthorizationResult checkAuthorization(Authorizer authorizer, 
RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
+        if (user == null) {
+            throw new AccessDeniedException("Unknown user.");
+        }
+
+        final AuthorizationResult resourceResult = 
Authorizable.super.checkAuthorization(authorizer, action, user, 
resourceContext);
+
+        // if we're denied from the resource try inheriting
+        if (Result.Denied.equals(resourceResult.getResult())) {
+            return getParentAuthorizable().checkAuthorization(authorizer, 
action, user, resourceContext);
+        } else {
+            return resourceResult;
+        }
+    }
+
+    @Override
+    public void authorize(Authorizer authorizer, RequestAction action, 
NiFiUser user, Map<String, String> resourceContext) throws 
AccessDeniedException {
+        if (user == null) {
+            throw new AccessDeniedException("Unknown user.");
+        }
+
+        try {
+            Authorizable.super.authorize(authorizer, action, user, 
resourceContext);
+        } catch (final AccessDeniedException resourceDenied) {
+            // if we're denied from the resource try inheriting
+            try {
+                getParentAuthorizable().authorize(authorizer, action, user, 
resourceContext);
+            } catch (final AccessDeniedException policiesDenied) {
+                throw resourceDenied;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
new file mode 100644
index 0000000..3e694e7
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java
@@ -0,0 +1,300 @@
+/*
+ * 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.resource;
+
+import org.apache.nifi.registry.authorization.AuthorizationResult.Result;
+import org.apache.nifi.registry.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.authorization.user.NiFiUser;
+import org.apache.nifi.registry.authorization.AuthorizationAuditor;
+import org.apache.nifi.registry.authorization.AuthorizationRequest;
+import org.apache.nifi.registry.authorization.AuthorizationResult;
+import org.apache.nifi.registry.authorization.Authorizer;
+import org.apache.nifi.registry.authorization.RequestAction;
+import org.apache.nifi.registry.authorization.Resource;
+import org.apache.nifi.registry.authorization.UserContextKeys;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public interface Authorizable {
+
+    /**
+     * The parent for this Authorizable. May be null.
+     *
+     * @return the parent authorizable or null
+     */
+    Authorizable getParentAuthorizable();
+
+    /**
+     * The Resource for this Authorizable.
+     *
+     * @return the resource
+     */
+    Resource getResource();
+
+    /**
+     * The originally requested resource for this Authorizable. Because 
policies are inherited, if a resource
+     * does not have a policy, this Authorizable may represent a parent 
resource and this method will return
+     * the originally requested resource.
+     *
+     * @return the originally requested resource
+     */
+    default Resource getRequestedResource() {
+        return getResource();
+    }
+
+    /**
+     * Returns whether the current user is authorized for the specified action 
on the specified resource. This
+     * method does not imply the user is directly attempting to access the 
specified resource. If the user is
+     * attempting a direct access use Authorizable.authorize().
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @return is authorized
+     */
+    default boolean isAuthorized(Authorizer authorizer, RequestAction action, 
NiFiUser user) {
+        return Result.Approved.equals(checkAuthorization(authorizer, action, 
user).getResult());
+    }
+
+    /**
+     * Returns the result of an authorization request for the specified user 
for the specified action on the specified
+     * resource. This method does not imply the user is directly attempting to 
access the specified resource. If the user is
+     * attempting a direct access use Authorizable.authorize().
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @param user user
+     * @return is authorized
+     */
+    default AuthorizationResult checkAuthorization(Authorizer authorizer, 
RequestAction action, NiFiUser user, Map<String, String> resourceContext) {
+        if (user == null) {
+            return AuthorizationResult.denied("Unknown user.");
+        }
+
+        final Map<String,String> userContext;
+        if (user.getClientAddress() != null && 
!user.getClientAddress().trim().isEmpty()) {
+            userContext = new HashMap<>();
+            userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), 
user.getClientAddress());
+        } else {
+            userContext = null;
+        }
+
+        final Resource resource = getResource();
+        final Resource requestedResource = getRequestedResource();
+        final AuthorizationRequest request = new AuthorizationRequest.Builder()
+                .identity(user.getIdentity())
+                .groups(user.getGroups())
+                .anonymous(user.isAnonymous())
+                .accessAttempt(false)
+                .action(action)
+                .resource(resource)
+                .requestedResource(requestedResource)
+                .resourceContext(resourceContext)
+                .userContext(userContext)
+                .explanationSupplier(() -> {
+                    // build the safe explanation
+                    final StringBuilder safeDescription = new 
StringBuilder("Unable to ");
+
+                    if (RequestAction.READ.equals(action)) {
+                        safeDescription.append("view ");
+                    } else {
+                        safeDescription.append("modify ");
+                    }
+                    
safeDescription.append(resource.getSafeDescription()).append(".");
+
+                    return safeDescription.toString();
+                })
+                .build();
+
+        // perform the authorization
+        final AuthorizationResult result = authorizer.authorize(request);
+
+        // verify the results
+        if (Result.ResourceNotFound.equals(result.getResult())) {
+            final Authorizable parent = getParentAuthorizable();
+            if (parent == null) {
+                return AuthorizationResult.denied("No applicable policies 
could be found.");
+            } else {
+                // create a custom authorizable to override the safe 
description but still defer to the parent authorizable
+                final Authorizable parentProxy = new Authorizable() {
+                    @Override
+                    public Authorizable getParentAuthorizable() {
+                        return parent.getParentAuthorizable();
+                    }
+
+                    @Override
+                    public Resource getRequestedResource() {
+                        return requestedResource;
+                    }
+
+                    @Override
+                    public Resource getResource() {
+                        final Resource parentResource = parent.getResource();
+                        return new Resource() {
+                            @Override
+                            public String getIdentifier() {
+                                return parentResource.getIdentifier();
+                            }
+
+                            @Override
+                            public String getName() {
+                                return parentResource.getName();
+                            }
+
+                            @Override
+                            public String getSafeDescription() {
+                                return resource.getSafeDescription();
+                            }
+                        };
+                    }
+                };
+                return parentProxy.checkAuthorization(authorizer, action, 
user, resourceContext);
+            }
+        } else {
+            return result;
+        }
+    }
+
+    /**
+     * Returns the result of an authorization request for the specified user 
for the specified action on the specified
+     * resource. This method does not imply the user is directly attempting to 
access the specified resource. If the user is
+     * attempting a direct access use Authorizable.authorize().
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @param user user
+     * @return is authorized
+     */
+    default AuthorizationResult checkAuthorization(Authorizer authorizer, 
RequestAction action, NiFiUser user) {
+        return checkAuthorization(authorizer, action, user, null);
+    }
+
+    /**
+     * Authorizes the current user for the specified action on the specified 
resource. This method does imply the user is
+     * directly accessing the specified resource.
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @param user user
+     * @param resourceContext resource context
+     */
+    default void authorize(Authorizer authorizer, RequestAction action, 
NiFiUser user, Map<String, String> resourceContext) throws 
AccessDeniedException {
+        if (user == null) {
+            throw new AccessDeniedException("Unknown user.");
+        }
+
+        final Map<String,String> userContext;
+        if (user.getClientAddress() != null && 
!user.getClientAddress().trim().isEmpty()) {
+            userContext = new HashMap<>();
+            userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), 
user.getClientAddress());
+        } else {
+            userContext = null;
+        }
+
+        final Resource resource = getResource();
+        final Resource requestedResource = getRequestedResource();
+        final AuthorizationRequest request = new AuthorizationRequest.Builder()
+                .identity(user.getIdentity())
+                .groups(user.getGroups())
+                .anonymous(user.isAnonymous())
+                .accessAttempt(true)
+                .action(action)
+                .resource(resource)
+                .requestedResource(requestedResource)
+                .resourceContext(resourceContext)
+                .userContext(userContext)
+                .explanationSupplier(() -> {
+                    // build the safe explanation
+                    final StringBuilder safeDescription = new 
StringBuilder("Unable to ");
+
+                    if (RequestAction.READ.equals(action)) {
+                        safeDescription.append("view ");
+                    } else {
+                        safeDescription.append("modify ");
+                    }
+                    
safeDescription.append(resource.getSafeDescription()).append(".");
+
+                    return safeDescription.toString();
+                })
+                .build();
+
+        final AuthorizationResult result = authorizer.authorize(request);
+        if (Result.ResourceNotFound.equals(result.getResult())) {
+            final Authorizable parent = getParentAuthorizable();
+            if (parent == null) {
+                final AuthorizationResult failure = 
AuthorizationResult.denied("No applicable policies could be found.");
+
+                // audit authorization request
+                if (authorizer instanceof AuthorizationAuditor) {
+                    ((AuthorizationAuditor) 
authorizer).auditAccessAttempt(request, failure);
+                }
+
+                // denied
+                throw new AccessDeniedException(failure.getExplanation());
+            } else {
+                // create a custom authorizable to override the safe 
description but still defer to the parent authorizable
+                final Authorizable parentProxy = new Authorizable() {
+                    @Override
+                    public Authorizable getParentAuthorizable() {
+                        return parent.getParentAuthorizable();
+                    }
+
+                    @Override
+                    public Resource getRequestedResource() {
+                        return requestedResource;
+                    }
+
+                    @Override
+                    public Resource getResource() {
+                        final Resource parentResource = parent.getResource();
+                        return new Resource() {
+                            @Override
+                            public String getIdentifier() {
+                                return parentResource.getIdentifier();
+                            }
+
+                            @Override
+                            public String getName() {
+                                return parentResource.getName();
+                            }
+
+                            @Override
+                            public String getSafeDescription() {
+                                return resource.getSafeDescription();
+                            }
+                        };
+                    }
+                };
+                parentProxy.authorize(authorizer, action, user, 
resourceContext);
+            }
+        } else if (Result.Denied.equals(result.getResult())) {
+            throw new AccessDeniedException(result.getExplanation());
+        }
+    }
+
+    /**
+     * Authorizes the current user for the specified action on the specified 
resource. This method does imply the user is
+     * directly accessing the specified resource.
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @param user user
+     */
+    default void authorize(Authorizer authorizer, RequestAction action, 
NiFiUser user) throws AccessDeniedException {
+        authorize(authorizer, action, user, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java
new file mode 100644
index 0000000..2d6b1a8
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java
@@ -0,0 +1,36 @@
+/*
+ * 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.resource;
+
+/**
+ * Defers permissions on policies to the policies of the base authorizable. 
Required because we don't
+ * want to change the enforcement of the policies on the authorizable. For 
example...
+ *
+ * if a user has permissions to /policies/input-ports/1234 then they have 
permissions to the following
+ *
+ * - the policy for /buckets/1234                    -> /policies/buckets/1234
+ * - the policy for /policies/buckets/1234           -> 
/policies/policies/buckets/1234
+ */
+public interface EnforcePolicyPermissionsThroughBaseResource {
+
+    /**
+     * Returns the base authorizable. Cannot be null.
+     *
+     * @return base authorizable
+     */
+    Authorizable getBaseAuthorizable();
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java
new file mode 100644
index 0000000..1d0b023
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java
@@ -0,0 +1,261 @@
+/*
+ * 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.resource;
+
+import org.apache.nifi.registry.authorization.Resource;
+
+import java.util.Objects;
+
+public final class ResourceFactory {
+
+    private final static Resource BUCKETS_RESOURCE = new Resource() {
+        @Override
+        public String getIdentifier() {
+            return ResourceType.Bucket.getValue();
+        }
+
+        @Override
+        public String getName() {
+            return "Buckets";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "buckets";
+        }
+    };
+
+
+    private final static Resource POLICY_RESOURCE = new Resource() {
+        @Override
+        public String getIdentifier() {
+            return ResourceType.Policy.getValue();
+        }
+
+        @Override
+        public String getName() {
+            return "Policies for ";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "the policies for ";
+        }
+    };
+
+    private final static Resource PROXY_RESOURCE = new Resource() {
+        @Override
+        public String getIdentifier() {
+            return ResourceType.Proxy.getValue();
+        }
+
+        @Override
+        public String getName() {
+            return "Proxy User Requests";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "proxy requests on behalf of users";
+        }
+    };
+
+    private final static Resource RESOURCE_RESOURCE = new Resource() {
+        @Override
+        public String getIdentifier() {
+            return ResourceType.Resource.getValue();
+        }
+
+        @Override
+        public String getName() {
+            return "NiFi Resources";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "resources";
+        }
+    };
+
+    private final static Resource TENANT_RESOURCE = new Resource() {
+        @Override
+        public String getIdentifier() {
+            return ResourceType.Tenant.getValue();
+        }
+
+        @Override
+        public String getName() {
+            return "Tenant";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "users/user groups";
+        }
+    };
+
+    private final static Resource POLICIES_RESOURCE = new Resource() {
+
+        @Override
+        public String getIdentifier() {
+            return "/policies";
+        }
+
+        @Override
+        public String getName() {
+            return "Access Policies";
+        }
+
+        @Override
+        public String getSafeDescription() {
+            return "policies";
+        }
+    };
+
+
+    /**
+     * Gets the Resource for proxying a user request.
+     *
+     * @return  The resource for proxying a user request
+     */
+    public static Resource getProxyResource() {
+        return PROXY_RESOURCE;
+    }
+
+    /**
+     * Gets the Resource for detailing all available NiFi Resources.
+     *
+     * @return  The Resource resource
+     */
+    public static Resource getResourceResource() {
+        return RESOURCE_RESOURCE;
+    }
+
+    /**
+     * Gets the Resource for accessing Tenants which includes creating, 
modifying, and deleting Users and UserGroups.
+     *
+     * @return The Resource for accessing Tenants
+     */
+    public static Resource getTenantResource() {
+        return TENANT_RESOURCE;
+    }
+
+    /**
+     * Gets the {@link Resource} for accessing access policies.
+     * @return The policies resource
+     */
+    public static Resource getPoliciesResource() {
+        return POLICIES_RESOURCE;
+    }
+
+    /**
+     * Gets the {@link Resource} for accessing buckets.
+     * @return The buckets resource
+     */
+    public static Resource getBucketsResource() {
+        return BUCKETS_RESOURCE;
+    }
+
+    /**
+     * Gets the {@link Resource} for accessing buckets.
+     * @return The buckets resource
+     */
+    public static Resource getBucketResource(String bucketIdentifier, String 
bucketName) {
+        return getChildResource(ResourceType.Bucket, bucketIdentifier, 
bucketName);
+    }
+
+    /**
+     * Gets a Resource for accessing a resources's policies.
+     *
+     * @param resource      The resource being accessed
+     * @return              The resource
+     */
+    public static Resource getPolicyResource(final Resource resource) {
+        Objects.requireNonNull(resource, "The resource type must be 
specified.");
+
+        return new Resource() {
+            @Override
+            public String getIdentifier() {
+                return String.format("%s%s", POLICY_RESOURCE.getIdentifier(), 
resource.getIdentifier());
+            }
+
+            @Override
+            public String getName() {
+                return POLICY_RESOURCE.getName() + resource.getName();
+            }
+
+            @Override
+            public String getSafeDescription() {
+                return POLICY_RESOURCE.getSafeDescription() + 
resource.getSafeDescription();
+            }
+        };
+    }
+
+    /**
+     * Get a Resource object for any object that has a base type and an 
identifier, ie:
+     * /buckets/{uuid}
+     *
+     * @param parentResourceType - Required, the base resource type
+     * @param childIdentifier - Required, the identity of this sub resource
+     * @param name - Optional, the name of the subresource
+     * @return A resource for this object
+     */
+    public static Resource getChildResource(final ResourceType 
parentResourceType, final String childIdentifier, final String name) {
+        Objects.requireNonNull(parentResourceType, "The base resource type 
must be specified.");
+        Objects.requireNonNull(childIdentifier, "The child identifier 
identifier must be specified.");
+
+        return new Resource() {
+            @Override
+            public String getIdentifier() {
+                return String.format("%s/%s", parentResourceType.getValue(), 
childIdentifier);
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public String getSafeDescription() {
+                final StringBuilder safeDescription = new StringBuilder();
+                switch (parentResourceType) {
+                    case Bucket:
+                        safeDescription.append("Bucket");
+                        break;
+                    case Policy:
+                        safeDescription.append("Policy");
+                        break;
+                    case Tenant:
+                        safeDescription.append("Tenant");
+                        break;
+                    default:
+                        safeDescription.append("Unknown resource type");
+                        break;
+                }
+                safeDescription.append(" with ID ");
+                safeDescription.append(childIdentifier);
+                return safeDescription.toString();
+            }
+        };
+
+    }
+
+    /**
+     * Prevent outside instantiation.
+     */
+    private ResourceFactory() {}
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java
new file mode 100644
index 0000000..4987a38
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.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.resource;
+
+public enum ResourceType {
+    Bucket("/buckets"),
+    Policy("/policies"),
+    Proxy("/proxy"),
+    Resource("/resources"),
+    Tenant("/tenants");
+
+    final String value;
+
+    private ResourceType(final String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public static ResourceType valueOfValue(final String rawValue) {
+        ResourceType type = null;
+
+        for (final ResourceType rt : values()) {
+            if (rt.getValue().equals(rawValue)) {
+                type = rt;
+                break;
+            }
+        }
+
+        if (type == null) {
+            throw new IllegalArgumentException("Unknown resource type value " 
+ rawValue);
+        }
+
+        return type;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java
new file mode 100644
index 0000000..5f49241
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.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.user;
+
+import java.util.Set;
+
+/**
+ * A representation of a NiFi user that has logged into the application
+ */
+public interface NiFiUser {
+
+    /**
+     * @return the unique identity of this user
+     */
+    String getIdentity();
+
+    /**
+     * @return the groups that this user belongs to if this nifi is configured 
to load user groups, null otherwise.
+     */
+    Set<String> getGroups();
+
+    /**
+     * @return the next user in the proxied entities chain, or 
<code>null</code> if no more users exist in the chain.
+     */
+    NiFiUser getChain();
+
+    /**
+     * @return <code>true</code> if the user is the unauthenticated Anonymous 
user
+     */
+    boolean isAnonymous();
+
+    /**
+     * @return the address of the client that made the request which created 
this user
+     */
+    String getClientAddress();
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
new file mode 100644
index 0000000..7b0da47
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java
@@ -0,0 +1,91 @@
+/*
+ * 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.user;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * User details for a NiFi user.
+ */
+public class NiFiUserDetails implements UserDetails {
+
+    private final NiFiUser user;
+
+    /**
+     * Creates a new NiFiUserDetails.
+     *
+     * @param user user
+     */
+    public NiFiUserDetails(NiFiUser user) {
+        this.user = user;
+    }
+
+    /**
+     * Get the user for this UserDetails.
+     *
+     * @return user
+     */
+    public NiFiUser getNiFiUser() {
+        return user;
+    }
+
+    /**
+     * Returns the authorities that this NiFi user has.
+     *
+     * @return authorities
+     */
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return Collections.EMPTY_SET;
+    }
+
+    @Override
+    public String getPassword() {
+        return StringUtils.EMPTY;
+    }
+
+    @Override
+    public String getUsername() {
+        return user.getIdentity();
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
new file mode 100644
index 0000000..2ad508a
--- /dev/null
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java
@@ -0,0 +1,91 @@
+/*
+ * 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.user;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods for retrieving information about the current application 
user.
+ *
+ */
+public final class NiFiUserUtils {
+
+    /**
+     * Returns the current NiFiUser or null if the current user is not a 
NiFiUser.
+     *
+     * @return user
+     */
+    public static NiFiUser getNiFiUser() {
+        NiFiUser user = null;
+
+        // obtain the principal in the current authentication
+        final SecurityContext context = SecurityContextHolder.getContext();
+        final Authentication authentication = context.getAuthentication();
+        if (authentication != null) {
+            Object principal = authentication.getPrincipal();
+            if (principal instanceof NiFiUserDetails) {
+                user = ((NiFiUserDetails) principal).getNiFiUser();
+            }
+        }
+
+        return user;
+    }
+
+    public static String getNiFiUserIdentity() {
+        // get the nifi user to extract the username
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user == null) {
+            return "unknown";
+        } else {
+            return user.getIdentity();
+        }
+    }
+
+    /**
+     * Builds the proxy chain for the specified user.
+     *
+     * @param user The current user
+     * @return The proxy chain for that user in List form
+     */
+    public static List<String> buildProxiedEntitiesChain(final NiFiUser user) {
+        // calculate the dn chain
+        final List<String> proxyChain = new ArrayList<>();
+
+        // build the dn chain
+        NiFiUser chainedUser = user;
+        while (chainedUser != null) {
+            // add the entry for this user
+            if (chainedUser.isAnonymous()) {
+                // use an empty string to represent an anonymous user in the 
proxy entities chain
+                proxyChain.add(StringUtils.EMPTY);
+            } else {
+                proxyChain.add(chainedUser.getIdentity());
+            }
+
+            // go to the next user in the chain
+            chainedUser = chainedUser.getChain();
+        }
+
+        return proxyChain;
+    }
+}

Reply via email to