You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by kd...@apache.org on 2018/09/22 02:11:35 UTC

[36/51] [partial] nifi-registry git commit: NIFIREG-201 Refactoring project structure to better isolate extensions

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
new file mode 100644
index 0000000..43862fe
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.extension.ExtensionCloseable;
+import org.apache.nifi.registry.extension.ExtensionManager;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.registry.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.registry.properties.SensitivePropertyProvider;
+import org.apache.nifi.registry.provider.StandardProviderFactory;
+import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
+import org.apache.nifi.registry.security.authorization.generated.Authorizers;
+import org.apache.nifi.registry.security.authorization.generated.Prop;
+import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
+import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.apache.nifi.registry.security.util.XmlUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.Nullable;
+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.stream.XMLStreamException;
+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;
+
+/**
+ * Creates and configures Authorizers and their providers based on the configuration (authorizers.xml).
+ *
+ * This implementation of AuthorizerFactory in NiFi Registry is based on a combination of
+ * NiFi's AuthorizerFactory and AuthorizerFactoryBean.
+ */
+@Configuration("authorizerFactory")
+public class AuthorizerFactory implements UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup, DisposableBean {
+
+    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.security.authorization.generated";
+    private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
+
+    /**
+     * Load the JAXBContext.
+     */
+    private static JAXBContext initializeJaxbContext() {
+        try {
+            return JAXBContext.newInstance(JAXB_GENERATED_PATH, AuthorizerFactory.class.getClassLoader());
+        } catch (JAXBException e) {
+            throw new RuntimeException("Unable to create JAXBContext.", e);
+        }
+    }
+
+    private final NiFiRegistryProperties properties;
+    private final ExtensionManager extensionManager;
+    private final SensitivePropertyProvider sensitivePropertyProvider;
+
+    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<>();
+
+    @Autowired
+    public AuthorizerFactory(
+            final NiFiRegistryProperties properties,
+            final ExtensionManager extensionManager,
+            @Nullable final SensitivePropertyProvider sensitivePropertyProvider) {
+
+        this.properties = properties;
+        this.extensionManager = extensionManager;
+        this.sensitivePropertyProvider = sensitivePropertyProvider;
+
+        if (this.properties == null) {
+            throw new IllegalStateException("NiFiRegistryProperties cannot be null");
+        }
+
+        if (this.extensionManager == null) {
+            throw new IllegalStateException("ExtensionManager 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 / DisposableBean *****/
+
+    @Bean
+    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-registry.properties file.");
+                } else {
+
+                    try {
+                        final Authorizers authorizerConfiguration = loadAuthorizersConfiguration();
+
+                        // create each user group provider
+                        for (final org.apache.nifi.registry.security.authorization.generated.UserGroupProvider userGroupProvider : authorizerConfiguration.getUserGroupProvider()) {
+                            if (userGroupProviders.containsKey(userGroupProvider.getIdentifier())) {
+                                throw new AuthorizerFactoryException("Duplicate User Group Provider identifier in Authorizers configuration: " + userGroupProvider.getIdentifier());
+                            }
+                            userGroupProviders.put(userGroupProvider.getIdentifier(), createUserGroupProvider(userGroupProvider.getIdentifier(), userGroupProvider.getClazz()));
+                        }
+
+                        // configure each user group provider
+                        for (final org.apache.nifi.registry.security.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.security.authorization.generated.AccessPolicyProvider accessPolicyProvider : authorizerConfiguration.getAccessPolicyProvider()) {
+                            if (accessPolicyProviders.containsKey(accessPolicyProvider.getIdentifier())) {
+                                throw new AuthorizerFactoryException("Duplicate Access Policy Provider identifier in Authorizers configuration: " + accessPolicyProvider.getIdentifier());
+                            }
+                            accessPolicyProviders.put(accessPolicyProvider.getIdentifier(), createAccessPolicyProvider(accessPolicyProvider.getIdentifier(), accessPolicyProvider.getClazz()));
+                        }
+
+                        // configure each access policy provider
+                        for (final org.apache.nifi.registry.security.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.security.authorization.generated.Authorizer authorizer : authorizerConfiguration.getAuthorizer()) {
+                            if (authorizers.containsKey(authorizer.getIdentifier())) {
+                                throw new AuthorizerFactoryException("Duplicate Authorizer identifier in Authorizers configuration: " + authorizer.getIdentifier());
+                            }
+                            authorizers.put(authorizer.getIdentifier(), createAuthorizer(authorizer.getIdentifier(), authorizer.getClazz(), authorizer.getClasspath()));
+                        }
+
+                        // configure each authorizer
+                        for (final org.apache.nifi.registry.security.authorization.generated.Authorizer provider : authorizerConfiguration.getAuthorizer()) {
+                            final Authorizer instance = authorizers.get(provider.getIdentifier());
+                            final Class authorizerClass = instance instanceof WrappedAuthorizer
+                                    ? ((WrappedAuthorizer) instance).getBaseAuthorizer().getClass()
+                                    : instance.getClass();
+                            try (ExtensionCloseable extCloseable = ExtensionCloseable.withComponentClassLoader(extensionManager, authorizerClass)) {
+                                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 (AuthorizerFactoryException e) {
+                        throw e;
+                    } catch (Exception e) {
+                        throw new AuthorizerFactoryException("Failed to construct Authorizer.", e);
+                    }
+                }
+            }
+        }
+        return authorizer;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        if (authorizers != null) {
+            authorizers.forEach((key, value) -> value.preDestruction());
+        }
+
+        if (accessPolicyProviders != null) {
+            accessPolicyProviders.forEach((key, value) -> value.preDestruction());
+        }
+
+        if (userGroupProviders != null) {
+            userGroupProviders.forEach((key, value) -> value.preDestruction());
+        }
+    }
+
+    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(XmlUtils.createSafeReader(new StreamSource(authorizersConfigurationFile)), Authorizers.class);
+                return element.getValue();
+            } catch (XMLStreamException | 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) {
+            if (!StringUtils.isBlank(property.getEncryption())) {
+                String decryptedValue = decryptValue(property.getValue(), property.getEncryption());
+                authorizerProperties.put(property.getName(), decryptedValue);
+            } else {
+                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;
+
+        final ClassLoader classLoader = extensionManager.getExtensionClassLoader(userGroupProviderClassName);
+        if (classLoader == null) {
+            throw new IllegalStateException("Extension not found in any of the configured class loaders: " + userGroupProviderClassName);
+        }
+
+        // attempt to load the class
+        Class<?> rawUserGroupProviderClass = Class.forName(userGroupProviderClassName, true, classLoader);
+        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;
+
+        final ClassLoader classLoader = extensionManager.getExtensionClassLoader(accessPolicyProviderClassName);
+        if (classLoader == null) {
+            throw new IllegalStateException("Extension not found in any of the configured class loaders: " + accessPolicyProviderClassName);
+        }
+
+        // attempt to load the class
+        Class<?> rawAccessPolicyProviderClass = Class.forName(accessPolicyProviderClassName, true, classLoader);
+        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;
+
+        final ClassLoader classLoader = extensionManager.getExtensionClassLoader(authorizerClassName);
+        if (classLoader == null) {
+            throw new IllegalStateException("Extension not found in any of the configured class loaders: " + authorizerClassName);
+        }
+
+        // attempt to load the class
+        Class<?> rawAuthorizerClass = Class.forName(authorizerClassName, true, classLoader);
+        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));
+
+        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);
+        }
+    }
+
+    private String decryptValue(String cipherText, String encryptionScheme) throws SensitivePropertyProtectionException {
+        if (sensitivePropertyProvider == null) {
+            throw new SensitivePropertyProtectionException("Sensitive Property Provider dependency was never wired, so protected" +
+                    "properties cannot be decrypted. This usually indicates that a master key for this NiFi Registry was not " +
+                    "detected and configured during the bootstrap startup sequence. Contact the system administrator.");
+        }
+
+        if (!sensitivePropertyProvider.getIdentifierKey().equalsIgnoreCase(encryptionScheme)) {
+            throw new SensitivePropertyProtectionException("Identity Provider configuration XML was protected using " +
+                    encryptionScheme +
+                    ", but the configured Sensitive Property Provider supports " +
+                    sensitivePropertyProvider.getIdentifierKey() +
+                    ". Cannot configure this Identity Provider due to failing to decrypt protected configuration properties.");
+        }
+
+        return sensitivePropertyProvider.unprotect(cipherText);
+    }
+
+
+    /**
+     * @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 SecurityProviderCreationException {
+            }
+
+            @Override
+            public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+            }
+
+            @Override
+            public void preDestruction() throws SecurityProviderCreationException {
+            }
+        };
+    }
+
+    private interface WrappedAuthorizer {
+        Authorizer getBaseAuthorizer();
+    }
+
+    private static class ManagedAuthorizerWrapper implements ManagedAuthorizer, WrappedAuthorizer {
+        private final ManagedAuthorizer baseManagedAuthorizer;
+
+        public ManagedAuthorizerWrapper(ManagedAuthorizer baseManagedAuthorizer) {
+            this.baseManagedAuthorizer = baseManagedAuthorizer;
+        }
+
+        @Override
+        public Authorizer getBaseAuthorizer() {
+            return baseManagedAuthorizer;
+        }
+
+        @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()));
+                                    }
+                                    if (!allGroupUsersExist(baseUserGroupProvider, group)) {
+                                        throw new IllegalStateException(String.format("Cannot create group '%s' with users that don't exist.", group.getName()));
+                                    }
+                                    return baseConfigurableUserGroupProvider.addGroup(group);
+                                }
+
+                                @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.");
+                                    }
+                                    if (!allGroupUsersExist(baseUserGroupProvider, group)) {
+                                        throw new IllegalStateException(String.format("Cannot update group '%s' to add users that don't exist.", group.getName()));
+                                    }
+                                    return baseConfigurableUserGroupProvider.updateGroup(group);
+                                }
+
+                                @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 SecurityProviderCreationException {
+                                    baseConfigurableUserGroupProvider.initialize(initializationContext);
+                                }
+
+                                @Override
+                                public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+                                    baseConfigurableUserGroupProvider.onConfigured(configurationContext);
+                                }
+
+                                @Override
+                                public void preDestruction() throws SecurityProviderDestructionException {
+                                    baseConfigurableUserGroupProvider.preDestruction();
+                                }
+                            };
+                        } else {
+                            return baseUserGroupProvider;
+                        }
+                    }
+
+                    @Override
+                    public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws SecurityProviderCreationException {
+                        baseConfigurableAccessPolicyProvider.initialize(initializationContext);
+                    }
+
+                    @Override
+                    public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+                        baseConfigurableAccessPolicyProvider.onConfigured(configurationContext);
+                    }
+
+                    @Override
+                    public void preDestruction() throws SecurityProviderDestructionException {
+                        baseConfigurableAccessPolicyProvider.preDestruction();
+                    }
+                };
+            } else {
+                return baseAccessPolicyProvider;
+            }
+        }
+
+        @Override
+        public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
+            final AuthorizationResult result = baseManagedAuthorizer.authorize(request);
+
+            // audit the authorization request
+            audit(baseManagedAuthorizer, request, result);
+
+            return result;
+        }
+
+        @Override
+        public void initialize(AuthorizerInitializationContext initializationContext) throws SecurityProviderCreationException {
+            baseManagedAuthorizer.initialize(initializationContext);
+        }
+
+        @Override
+        public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+            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 SecurityProviderCreationException(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 SecurityProviderCreationException(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 SecurityProviderCreationException(String.format("Found multiple users/user groups with name '%s'.", group.getName()));
+                }
+            }
+        }
+
+        @Override
+        public void preDestruction() throws SecurityProviderDestructionException {
+            baseManagedAuthorizer.preDestruction();
+        }
+    }
+
+    private static class AuthorizerWrapper implements Authorizer, WrappedAuthorizer {
+        private final Authorizer baseAuthorizer;
+
+        public AuthorizerWrapper(Authorizer baseAuthorizer) {
+            this.baseAuthorizer = baseAuthorizer;
+        }
+
+        @Override
+        public Authorizer getBaseAuthorizer() {
+            return baseAuthorizer;
+        }
+
+        @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 SecurityProviderCreationException {
+            baseAuthorizer.initialize(initializationContext);
+        }
+
+        @Override
+        public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+            baseAuthorizer.onConfigured(configurationContext);
+        }
+
+        @Override
+        public void preDestruction() throws SecurityProviderDestructionException {
+            baseAuthorizer.preDestruction();
+        }
+    }
+
+    private static Authorizer installIntegrityChecks(final Authorizer baseAuthorizer) {
+        if (baseAuthorizer instanceof ManagedAuthorizer) {
+            return new ManagedAuthorizerWrapper((ManagedAuthorizer) baseAuthorizer);
+        } else {
+            return new AuthorizerWrapper(baseAuthorizer);
+        }
+    }
+
+    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 or group exists with the same identity.
+     *
+     * @param userGroupProvider the userGroupProvider to use to lookup the tenant
+     * @param identifier identity of the tenant
+     * @param identity identity of the tenant
+     * @return true if another user exists with the same identity, false otherwise
+     */
+    private static boolean tenantExists(final UserGroupProvider userGroupProvider, final String identifier, final String identity) {
+        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;
+    }
+
+    /**
+     * Check that all users in the group exist.
+     *
+     * @param userGroupProvider the userGroupProvider to use to lookup the users
+     * @param group the group whose users will be checked for existence.
+     * @return true if another user exists with the same identity, false otherwise
+     */
+    private static boolean allGroupUsersExist(final UserGroupProvider userGroupProvider, final Group group) {
+        for (String userIdentifier : group.getUsers()) {
+            User user = userGroupProvider.getUser(userIdentifier);
+            if (user == null) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java
new file mode 100644
index 0000000..a479555
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+public class AuthorizerFactoryException extends RuntimeException {
+
+    public AuthorizerFactoryException(String message) {
+        super(message);
+    }
+
+    public AuthorizerFactoryException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public AuthorizerFactoryException(Throwable cause) {
+        super(cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
new file mode 100644
index 0000000..6581e89
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
+import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
+import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.apache.nifi.registry.util.PropertyValue;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+public class CompositeConfigurableUserGroupProvider extends CompositeUserGroupProvider implements ConfigurableUserGroupProvider {
+
+    static final String PROP_CONFIGURABLE_USER_GROUP_PROVIDER = "Configurable User Group Provider";
+
+    private UserGroupProviderLookup userGroupProviderLookup;
+    private ConfigurableUserGroupProvider configurableUserGroupProvider;
+
+    public CompositeConfigurableUserGroupProvider() {
+        super(true);
+    }
+
+    @Override
+    public void initialize(UserGroupProviderInitializationContext initializationContext) throws SecurityProviderCreationException {
+        userGroupProviderLookup = initializationContext.getUserGroupProviderLookup();
+
+        // initialize the CompositeUserGroupProvider
+        super.initialize(initializationContext);
+    }
+
+    @Override
+    public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+        final PropertyValue configurableUserGroupProviderKey = configurationContext.getProperty(PROP_CONFIGURABLE_USER_GROUP_PROVIDER);
+        if (!configurableUserGroupProviderKey.isSet()) {
+            throw new SecurityProviderCreationException("The Configurable User Group Provider must be set.");
+        }
+
+        final UserGroupProvider userGroupProvider = userGroupProviderLookup.getUserGroupProvider(configurableUserGroupProviderKey.getValue());
+
+        if (userGroupProvider == null) {
+            throw new SecurityProviderCreationException(String.format("Unable to locate the Configurable User Group Provider: %s", configurableUserGroupProviderKey));
+        }
+
+        if (!(userGroupProvider instanceof ConfigurableUserGroupProvider)) {
+            throw new SecurityProviderCreationException(String.format("The Configurable User Group Provider is not configurable: %s", configurableUserGroupProviderKey));
+        }
+
+        // Ensure that the ConfigurableUserGroupProvider is not also listed as one of the providers for the CompositeUserGroupProvider
+        for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) {
+            Matcher matcher = USER_GROUP_PROVIDER_PATTERN.matcher(entry.getKey());
+            if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
+                final String userGroupProviderKey = entry.getValue();
+
+                if (userGroupProviderKey.equals(configurableUserGroupProviderKey.getValue())) {
+                    throw new SecurityProviderCreationException(String.format("Duplicate provider in Composite Configurable User Group Provider configuration: %s", userGroupProviderKey));
+                }
+            }
+        }
+
+        configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
+
+        // configure the CompositeUserGroupProvider
+        super.onConfigured(configurationContext);
+    }
+
+    @Override
+    public String getFingerprint() throws AuthorizationAccessException {
+        return configurableUserGroupProvider.getFingerprint();
+    }
+
+    @Override
+    public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
+        configurableUserGroupProvider.inheritFingerprint(fingerprint);
+    }
+
+    @Override
+    public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
+        configurableUserGroupProvider.checkInheritability(proposedFingerprint);
+    }
+
+    @Override
+    public User addUser(User user) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.addUser(user);
+    }
+
+    @Override
+    public boolean isConfigurable(User user) {
+        return configurableUserGroupProvider.isConfigurable(user);
+    }
+
+    @Override
+    public User updateUser(User user) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.updateUser(user);
+    }
+
+    @Override
+    public User deleteUser(User user) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteUser(user);
+    }
+
+    @Override
+    public User deleteUser(String userIdentifier) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteUser(userIdentifier);
+    }
+
+    @Override
+    public Group addGroup(Group group) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.addGroup(group);
+    }
+
+    @Override
+    public boolean isConfigurable(Group group) {
+        return configurableUserGroupProvider.isConfigurable(group);
+    }
+
+    @Override
+    public Group updateGroup(Group group) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.updateGroup(group);
+    }
+
+    @Override
+    public Group deleteGroup(Group group) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteGroup(group);
+    }
+
+    @Override
+    public Group deleteGroup(String groupIdentifier) throws AuthorizationAccessException {
+        return configurableUserGroupProvider.deleteGroup(groupIdentifier);
+    }
+
+    @Override
+    public Set<User> getUsers() throws AuthorizationAccessException {
+        final Set<User> users = new HashSet<>(configurableUserGroupProvider.getUsers());
+        users.addAll(super.getUsers());
+        return users;
+    }
+
+    @Override
+    public User getUser(String identifier) throws AuthorizationAccessException {
+        User user = configurableUserGroupProvider.getUser(identifier);
+
+        if (user == null) {
+            user = super.getUser(identifier);
+        }
+
+        return user;
+    }
+
+    @Override
+    public User getUserByIdentity(String identity) throws AuthorizationAccessException {
+        User user = configurableUserGroupProvider.getUserByIdentity(identity);
+
+        if (user == null) {
+            user = super.getUserByIdentity(identity);
+        }
+
+        return user;
+    }
+
+    @Override
+    public Set<Group> getGroups() throws AuthorizationAccessException {
+        final Set<Group> groups = new HashSet<>(configurableUserGroupProvider.getGroups());
+        groups.addAll(super.getGroups());
+        return groups;
+    }
+
+    @Override
+    public Group getGroup(String identifier) throws AuthorizationAccessException {
+        Group group = configurableUserGroupProvider.getGroup(identifier);
+
+        if (group == null) {
+            group = super.getGroup(identifier);
+        }
+
+        return group;
+    }
+
+    @Override
+    public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
+
+        final CompositeUserAndGroups combinedResult;
+
+        // First, lookup user and groups by identity and combine data from all providers
+        UserAndGroups configurableProviderResult = configurableUserGroupProvider.getUserAndGroups(identity);
+        UserAndGroups compositeProvidersResult = super.getUserAndGroups(identity);
+
+        if (configurableProviderResult.getUser() != null && compositeProvidersResult.getUser() != null) {
+            throw new IllegalStateException("Multiple UserGroupProviders claim to provide user " + identity);
+
+        } else if (configurableProviderResult.getUser() != null) {
+            combinedResult = new CompositeUserAndGroups(configurableProviderResult.getUser(), configurableProviderResult.getGroups());
+            combinedResult.addAllGroups(compositeProvidersResult.getGroups());
+
+        } else if (compositeProvidersResult.getUser() != null) {
+            combinedResult = new CompositeUserAndGroups(compositeProvidersResult.getUser(), compositeProvidersResult.getGroups());
+            combinedResult.addAllGroups(configurableProviderResult.getGroups());
+
+        } else {
+            return UserAndGroups.EMPTY;
+        }
+
+        // Second, lookup groups containing the user identifier
+        String userIdentifier = combinedResult.getUser().getIdentifier();
+        for (final Group group : configurableUserGroupProvider.getGroups()) {
+            if (group.getUsers() != null && group.getUsers().contains(userIdentifier)) {
+                combinedResult.addGroup(group);
+            }
+        }
+        for (final Group group : super.getGroups()) {
+            if (group.getUsers() != null && group.getUsers().contains(userIdentifier)) {
+                combinedResult.addGroup(group);
+            }
+        }
+
+        return combinedResult;
+    }
+
+    @Override
+    public void preDestruction() throws SecurityProviderDestructionException {
+        super.preDestruction();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
new file mode 100644
index 0000000..5665122
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserAndGroups.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class CompositeUserAndGroups implements UserAndGroups {
+
+    private User user;
+    private Set<Group> groups;
+
+    public CompositeUserAndGroups() {
+        this.user = null;
+        this.groups = null;
+    }
+
+    public CompositeUserAndGroups(User user, Set<Group> groups) {
+        this.user = user;
+        setGroups(groups);
+    }
+
+    @Override
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    @Override
+    public Set<Group> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(Set<Group> groups) {
+        // copy the collection so that if we add to this collection it does not modify other references
+        if (groups != null) {
+            this.groups = new HashSet<>(groups);
+        } else {
+            this.groups = null;
+        }
+    }
+
+    public void addAllGroups(Set<Group> groups) {
+        if (groups != null) {
+            if (this.groups == null) {
+                this.groups = new HashSet<>();
+            }
+            this.groups.addAll(groups);
+        }
+    }
+
+    public void addGroup(Group group) {
+        if (group != null) {
+            if (this.groups == null) {
+                this.groups = new HashSet<>();
+            }
+            this.groups.add(group);
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
new file mode 100644
index 0000000..3d84c14
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
+import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CompositeUserGroupProvider implements UserGroupProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(CompositeUserGroupProvider.class);
+
+    static final String PROP_USER_GROUP_PROVIDER_PREFIX = "User Group Provider ";
+    static final Pattern USER_GROUP_PROVIDER_PATTERN = Pattern.compile(PROP_USER_GROUP_PROVIDER_PREFIX + "\\S+");
+
+    private final boolean allowEmptyProviderList;
+
+    private UserGroupProviderLookup userGroupProviderLookup;
+    private List<UserGroupProvider> userGroupProviders = new ArrayList<>(); // order matters
+
+    public CompositeUserGroupProvider() {
+        this(false);
+    }
+
+    public CompositeUserGroupProvider(boolean allowEmptyProviderList) {
+        this.allowEmptyProviderList = allowEmptyProviderList;
+    }
+
+    @Override
+    public void initialize(UserGroupProviderInitializationContext initializationContext) throws SecurityProviderCreationException {
+        userGroupProviderLookup = initializationContext.getUserGroupProviderLookup();
+    }
+
+    @Override
+    public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException {
+        for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) {
+            Matcher matcher = USER_GROUP_PROVIDER_PATTERN.matcher(entry.getKey());
+            if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
+                final String userGroupProviderKey = entry.getValue();
+                final UserGroupProvider userGroupProvider = userGroupProviderLookup.getUserGroupProvider(userGroupProviderKey);
+
+                if (userGroupProvider == null) {
+                    throw new SecurityProviderCreationException(String.format("Unable to locate the configured User Group Provider: %s", userGroupProviderKey));
+                }
+
+                if (userGroupProviders.contains(userGroupProvider)) {
+                    throw new SecurityProviderCreationException(String.format("Duplicate provider in Composite User Group Provider configuration: %s", userGroupProviderKey));
+                }
+
+                userGroupProviders.add(userGroupProvider);
+            }
+        }
+
+        if (!allowEmptyProviderList && userGroupProviders.isEmpty()) {
+            throw new SecurityProviderCreationException("At least one User Group Provider must be configured.");
+        }
+    }
+
+    @Override
+    public Set<User> getUsers() throws AuthorizationAccessException {
+        final Set<User> users = new HashSet<>();
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            users.addAll(userGroupProvider.getUsers());
+        }
+
+        return users;
+    }
+
+    @Override
+    public User getUser(String identifier) throws AuthorizationAccessException {
+        User user = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            user = userGroupProvider.getUser(identifier);
+
+            if (user != null) {
+                break;
+            }
+        }
+
+        return user;
+    }
+
+    @Override
+    public User getUserByIdentity(String identity) throws AuthorizationAccessException {
+        User user = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            user = userGroupProvider.getUserByIdentity(identity);
+
+            if (user != null) {
+                break;
+            }
+        }
+
+        return user;
+    }
+
+    @Override
+    public Set<Group> getGroups() throws AuthorizationAccessException {
+        final Set<Group> groups = new HashSet<>();
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            groups.addAll(userGroupProvider.getGroups());
+        }
+
+        return groups;
+    }
+
+    @Override
+    public Group getGroup(String identifier) throws AuthorizationAccessException {
+        Group group = null;
+
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            group = userGroupProvider.getGroup(identifier);
+
+            if (group != null) {
+                break;
+            }
+        }
+
+        return group;
+    }
+
+    @Override
+    public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
+
+        // This method builds a UserAndGroups response by combining the data from all providers using a two-pass approach
+
+        CompositeUserAndGroups compositeUserAndGroups = new CompositeUserAndGroups();
+
+        // First pass - call getUserAndGroups(identity) on all providers, aggregate the responses, check for multiple
+        // user identity matches, which should not happen as identities should by globally unique.
+        String providerClassForUser = "";
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            UserAndGroups userAndGroups = userGroupProvider.getUserAndGroups(identity);
+
+            if (userAndGroups.getUser() != null) {
+                // is this the first match on the user?
+                if(compositeUserAndGroups.getUser() == null) {
+                    compositeUserAndGroups.setUser(userAndGroups.getUser());
+                    providerClassForUser = userGroupProvider.getClass().getName();
+                } else {
+                    logger.warn("Multiple UserGroupProviders are claiming to provide user '{}': [{} and {}] ",
+                            identity,
+                            userAndGroups.getUser(),
+                            providerClassForUser, userGroupProvider.getClass().getName());
+                    throw new IllegalStateException("Multiple UserGroupProviders are claiming to provide user " + identity);
+                }
+            }
+
+            if (userAndGroups.getGroups() != null) {
+                compositeUserAndGroups.addAllGroups(userAndGroups.getGroups());
+            }
+        }
+
+        if (compositeUserAndGroups.getUser() == null) {
+            logger.debug("No user found for identity {}", identity);
+            return UserAndGroups.EMPTY;
+        }
+
+        // Second pass - Now that we've matched a user, call getGroups() on all providers, and
+        // check all groups to see if they contain the user identifier corresponding to the identity.
+        // This is necessary because a provider might only know about a group<->userIdentifier mapping
+        // without knowing the user identifier.
+        String userIdentifier = compositeUserAndGroups.getUser().getIdentifier();
+        for (final UserGroupProvider userGroupProvider : userGroupProviders) {
+            for (final Group group : userGroupProvider.getGroups()) {
+                if (group.getUsers() != null && group.getUsers().contains(userIdentifier)) {
+                    compositeUserAndGroups.addGroup(group);
+                }
+            }
+        }
+
+        return compositeUserAndGroups;
+    }
+
+    @Override
+    public void preDestruction() throws SecurityProviderDestructionException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
new file mode 100644
index 0000000..18c2a52
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.exception.ResourceNotFoundException;
+import org.apache.nifi.registry.security.authorization.resource.Authorizable;
+import org.apache.nifi.registry.security.authorization.resource.InheritingAuthorizable;
+import org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
+import org.apache.nifi.registry.security.authorization.resource.ResourceType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StandardAuthorizableLookup implements AuthorizableLookup {
+
+    private static final Logger logger = LoggerFactory.getLogger(StandardAuthorizableLookup.class);
+
+    private static final Authorizable TENANTS_AUTHORIZABLE = new Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getTenantsResource();
+        }
+    };
+
+    private static final Authorizable POLICIES_AUTHORIZABLE = new Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getPoliciesResource();
+        }
+    };
+
+    private static final Authorizable BUCKETS_AUTHORIZABLE = new Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getBucketsResource();
+        }
+    };
+
+    private static final Authorizable PROXY_AUTHORIZABLE = new Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getProxyResource();
+        }
+    };
+
+    private static final Authorizable ACTUATOR_AUTHORIZABLE = new Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getActuatorResource();
+        }
+    };
+
+    private static final Authorizable SWAGGER_AUTHORIZABLE = new Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getSwaggerResource();
+        }
+    };
+
+    @Override
+    public Authorizable getActuatorAuthorizable() {
+        return ACTUATOR_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getSwaggerAuthorizable() {
+        return SWAGGER_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getProxyAuthorizable() {
+        return PROXY_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getTenantsAuthorizable() {
+        return TENANTS_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getPoliciesAuthorizable() {
+        return POLICIES_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getBucketsAuthorizable() {
+        return BUCKETS_AUTHORIZABLE;
+    }
+
+    @Override
+    public Authorizable getBucketAuthorizable(String bucketIdentifier) {
+        // Note - this returns a special Authorizable type that inherits permissions from the parent Authorizable
+        return new InheritingAuthorizable() {
+
+            @Override
+            public Authorizable getParentAuthorizable() {
+                return getBucketsAuthorizable();
+            }
+
+            @Override
+            public Resource getResource() {
+                return ResourceFactory.getBucketResource(bucketIdentifier, "Bucket with ID " + bucketIdentifier);
+            }
+        };
+    }
+
+    @Override
+    public Authorizable getAuthorizableByResource(String resource) {
+        ResourceType resourceType = ResourceType.mapFullResourcePathToResourceType(resource);
+
+        if (resourceType == null) {
+            throw new ResourceNotFoundException("Unrecognized resource: " + resource);
+        }
+
+        return getAuthorizableByResource(resourceType, resource);
+    }
+
+    private Authorizable getAuthorizableByResource(final ResourceType resourceType, final String resource) {
+        Authorizable authorizable = null;
+        switch (resourceType) {
+
+            /* Access to these resources are always authorized by the top-level resource */
+            case Policy:
+                authorizable = getPoliciesAuthorizable();
+                break;
+            case Tenant:
+                authorizable = getTenantsAuthorizable();
+                break;
+            case Proxy:
+                authorizable = getProxyAuthorizable();
+                break;
+            case Actuator:
+                authorizable = getActuatorAuthorizable();
+                break;
+            case Swagger:
+                authorizable = getSwaggerAuthorizable();
+                break;
+
+            /* Access to buckets can be authorized by the top-level /buckets resource or an individual /buckets/{id} resource */
+            case Bucket:
+                final String childResourceId = StringUtils.substringAfter(resource, resourceType.getValue());
+                if (childResourceId.startsWith("/")) {
+                    authorizable = getAuthorizableByChildResource(resourceType, childResourceId);
+                } else {
+                    authorizable = getBucketsAuthorizable();
+                }
+        }
+
+        if (authorizable == null) {
+            logger.debug("Could not determine the Authorizable for resource type='{}', path='{}', ", resourceType.getValue(), resource);
+            throw new IllegalArgumentException("This an unexpected type of authorizable resource: " + resourceType.getValue());
+        }
+
+        return authorizable;
+    }
+
+    private Authorizable getAuthorizableByChildResource(final ResourceType baseResourceType, final String childResourceId) {
+        Authorizable authorizable;
+        switch (baseResourceType) {
+            case Bucket:
+                String[] childResourcePathParts = childResourceId.split("/");
+                if (childResourcePathParts.length >= 1) {
+                    final String bucketId = childResourcePathParts[1];
+                    authorizable = getBucketAuthorizable(bucketId);
+                    break;
+                }
+            default:
+                throw new IllegalArgumentException("Unexpected lookup for child resource authorizable for base resource type " + baseResourceType.getValue());
+        }
+
+        return authorizable;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerConfigurationContext.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerConfigurationContext.java
new file mode 100644
index 0000000..9d274f7
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/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.security.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/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.java
new file mode 100644
index 0000000..d643e91
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.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.security.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;
+    }
+}