This is an automated email from the ASF dual-hosted git repository.

kdoran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-registry.git


The following commit(s) were added to refs/heads/master by this push:
     new 4825565  NIFIREG-358 Refactoring proxy authorization to be part of 
Authorizables
4825565 is described below

commit 4825565e48b935e07c3b6039b16e3144f150fa20
Author: Bryan Bende <bbe...@apache.org>
AuthorDate: Fri Feb 7 12:22:37 2020 -0500

    NIFIREG-358 Refactoring proxy authorization to be part of Authorizables
    
    NIFIREG-358 Catching UntrustedProxyException when asking for authorized 
resources since it would be considered unauthorized
    
    This closes #258.
    
    Signed-off-by: Kevin Doran <kdo...@apache.org>
---
 .../security/authorization/AuthorizerFactory.java  |  12 -
 .../authorization/FrameworkAuthorizer.java         | 189 ----------
 .../authorization/FrameworkManagedAuthorizer.java  |  54 ---
 .../authorization/StandardAuthorizableLookup.java  |  78 +++-
 .../authorization/UntrustedProxyException.java     |  29 ++
 .../authorization/resource/Authorizable.java       |  14 -
 .../resource/ProxyChainAuthorizable.java           | 145 ++++++++
 .../resource/PublicCheckingAuthorizable.java       | 107 ++++++
 .../registry/service/AuthorizationService.java     |   3 +-
 .../service/AuthorizationServiceSpec.groovy        |   3 +-
 .../authorization/TestFrameworkAuthorizer.java     | 278 --------------
 .../TestStandardAuthorizableLookup.java            | 404 +++++++++++++++++++++
 .../authorization/AuthorizationRequest.java        |   5 +
 .../web/mapper/UntrustedProxyExceptionMapper.java  |  48 +++
 .../ResourceAuthorizationFilterSpec.groovy         |   4 +-
 15 files changed, 814 insertions(+), 559 deletions(-)

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
index 959e29e..f69ac3c 100644
--- 
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
@@ -248,12 +248,8 @@ public class AuthorizerFactory implements 
UserGroupProviderLookup, AccessPolicyP
                             try (final ExtensionCloseable extClosable = 
ExtensionCloseable.withClassLoader(authorizerClassLoader)) {
                                 
authorizer.onConfigured(authorizerConfigurationContext);
                             }
-
-                            // wrap the integrity checked Authorizer with the 
FrameworkAuthorizer
-                            authorizer = createFrameworkAuthorizer(authorizer);
                         }
 
-
                     } catch (AuthorizerFactoryException e) {
                         throw e;
                     } catch (Exception e) {
@@ -427,14 +423,6 @@ public class AuthorizerFactory implements 
UserGroupProviderLookup, AccessPolicyP
         return instance;
     }
 
-    private Authorizer createFrameworkAuthorizer(final Authorizer 
baseAuthorizer) {
-        if (baseAuthorizer instanceof ManagedAuthorizer) {
-            return new FrameworkManagedAuthorizer((ManagedAuthorizer) 
baseAuthorizer, registryService);
-        } else {
-            return new FrameworkAuthorizer(baseAuthorizer, registryService);
-        }
-    }
-
     private void performMethodInjection(final Object instance, final Class 
authorizerClass) throws IllegalAccessException, IllegalArgumentException, 
InvocationTargetException {
         for (final Method method : authorizerClass.getMethods()) {
             if (method.isAnnotationPresent(AuthorizerContext.class)) {
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkAuthorizer.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkAuthorizer.java
deleted file mode 100644
index 08fb8f0..0000000
--- 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkAuthorizer.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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.bucket.Bucket;
-import org.apache.nifi.registry.exception.ResourceNotFoundException;
-import 
org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
-import 
org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import 
org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
-import org.apache.nifi.registry.security.authorization.resource.ResourceType;
-import org.apache.nifi.registry.security.authorization.user.NiFiUser;
-import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser;
-import 
org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
-import 
org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
-import org.apache.nifi.registry.service.RegistryService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Wraps an Authorizer and adds framework level logic for authorizing proxies, 
public resources, and anything else
- * that needs to be done on top of the regular Authorizer.
- */
-public class FrameworkAuthorizer implements Authorizer {
-
-    public static Logger LOGGER = 
LoggerFactory.getLogger(FrameworkAuthorizer.class);
-
-    private static final Authorizable PROXY_AUTHORIZABLE = new Authorizable() {
-        @Override
-        public Authorizable getParentAuthorizable() {
-            return null;
-        }
-
-        @Override
-        public Resource getResource() {
-            return ResourceFactory.getProxyResource();
-        }
-    };
-
-    private final Authorizer wrappedAuthorizer;
-    private final RegistryService registryService;
-
-    public FrameworkAuthorizer(final Authorizer wrappedAuthorizer, final 
RegistryService registryService) {
-        this.wrappedAuthorizer = Objects.requireNonNull(wrappedAuthorizer);
-        this.registryService = Objects.requireNonNull(registryService);
-    }
-
-    @Override
-    public void initialize(final AuthorizerInitializationContext 
initializationContext) throws SecurityProviderCreationException {
-        wrappedAuthorizer.initialize(initializationContext);
-    }
-
-    @Override
-    public void onConfigured(final AuthorizerConfigurationContext 
configurationContext) throws SecurityProviderCreationException {
-        wrappedAuthorizer.onConfigured(configurationContext);
-    }
-
-    @Override
-    public AuthorizationResult authorize(final AuthorizationRequest request) 
throws AuthorizationAccessException {
-        final Resource resource = request.getResource();
-        final RequestAction requestAction = request.getAction();
-
-        /**
-         * If the request is for a resource that has been made public and 
action is READ, then it should automatically be authorized.
-         *
-         * This needs to be checked before the proxy authorizations b/c access 
to a public resource should always be allowed.
-         */
-
-        final boolean allowPublicAccess = isPublicAccessAllowed(resource, 
requestAction);
-        if (allowPublicAccess) {
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("Authorizing access to public resource '{}'", new 
Object[]{resource.getIdentifier()});
-            }
-            return AuthorizationResult.approved();
-        }
-
-        /**
-         * Deny an anonymous user access to anything else, they should only 
have access to publicly readable resources checked above
-         */
-
-        if (request.isAnonymous()) {
-            return AuthorizationResult.denied("Anonymous access is not 
authorized");
-        }
-
-        /*
-        * If the request has a proxy chain, ensure each identity in the chain 
is an authorized proxy for the given action.
-        *
-        * The action comes from the original request. For example, if user1 is 
proxied by proxy1, and it is a WRITE request
-        * to /buckets/12345, then we need to determine if proxy1 is authorized 
to proxy WRITE requests.
-        */
-
-        final List<String> proxyChainIdentities = request.getProxyIdentities();
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Found {} proxy identities", new 
Object[]{proxyChainIdentities.size()});
-        }
-
-        for (final String proxyIdentity : proxyChainIdentities) {
-            final NiFiUser proxyNiFiUser = createProxyNiFiUser(proxyIdentity);
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("Authorizing proxy [{}] for {}", new 
Object[]{proxyIdentity, requestAction});
-            }
-
-            try {
-                PROXY_AUTHORIZABLE.authorize(wrappedAuthorizer, requestAction, 
proxyNiFiUser);
-            } catch (final AccessDeniedException e) {
-                final String actionString = requestAction.toString();
-                return AuthorizationResult.denied(String.format("Untrusted 
proxy [%s] for %s operation.", proxyIdentity, actionString));
-            }
-        }
-
-        /**
-         * All other authorization decisions need to be delegated to the 
original wrapped Authorizer.
-         */
-
-        return wrappedAuthorizer.authorize(request);
-    }
-
-    /**
-     * Determines if the given Resource is considered public for the action 
being performed.
-     *
-     * @param resource a Resource being authorized
-     * @param action the action being performed
-     * @return true if the resource is public for the given action, false 
otherwise
-     */
-    private boolean isPublicAccessAllowed(final Resource resource, final 
RequestAction action) {
-        if (resource == null || action == null) {
-            return false;
-        }
-
-        final String resourceIdentifier = resource.getIdentifier();
-        if (resourceIdentifier == null || 
!resourceIdentifier.startsWith(ResourceType.Bucket.getValue() + "/")) {
-            return false;
-        }
-
-        final int lastSlashIndex = resourceIdentifier.lastIndexOf("/");
-        if (lastSlashIndex < 0 || lastSlashIndex >= 
resourceIdentifier.length() - 1) {
-            return false;
-        }
-
-        final String bucketId = resourceIdentifier.substring(lastSlashIndex + 
1);
-        try {
-            final Bucket bucket = registryService.getBucket(bucketId);
-            return bucket.isAllowPublicRead() && action == RequestAction.READ;
-        } catch (ResourceNotFoundException rnfe) {
-            // if not found then we can't determine public access, so return 
false to delegate to regular authorizer
-            LOGGER.debug("Cannot determine public access, bucket not found 
with id [{}]", new Object[]{bucketId});
-            return false;
-        } catch (Exception e) {
-            LOGGER.error("Error checking public access to bucket with id 
[{}]", new Object[]{bucketId}, e);
-            return false;
-        }
-    }
-
-    /**
-     * Creates a NiFiUser for the given proxy identity.
-     *
-     * This is only intended to be used for authorizing the given proxy 
identity against the /proxy resource, so we
-     * don't need to populate the rest of the info on this user.
-     *
-     * @param proxyIdentity the proxy identity
-     * @return the NiFiUser
-     */
-    private NiFiUser createProxyNiFiUser(final String proxyIdentity) {
-        return new StandardNiFiUser.Builder().identity(proxyIdentity).build();
-    }
-
-    @Override
-    public void preDestruction() throws SecurityProviderDestructionException {
-        wrappedAuthorizer.preDestruction();
-    }
-
-}
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkManagedAuthorizer.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkManagedAuthorizer.java
deleted file mode 100644
index 478482e..0000000
--- 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/FrameworkManagedAuthorizer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.security.authorization.exception.AuthorizationAccessException;
-import 
org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException;
-import org.apache.nifi.registry.service.RegistryService;
-
-/**
- * Similar to FrameworkAuthorizer, but specifically for wrapping a 
ManagedAuthorizer.
- */
-public class FrameworkManagedAuthorizer extends FrameworkAuthorizer implements 
ManagedAuthorizer {
-
-    private final ManagedAuthorizer wrappedManagedAuthorizer;
-
-    public FrameworkManagedAuthorizer(final ManagedAuthorizer 
wrappedManagedAuthorizer, final RegistryService registryService) {
-        super(wrappedManagedAuthorizer, registryService);
-        this.wrappedManagedAuthorizer = wrappedManagedAuthorizer;
-    }
-
-    @Override
-    public String getFingerprint() throws AuthorizationAccessException {
-        return wrappedManagedAuthorizer.getFingerprint();
-    }
-
-    @Override
-    public void inheritFingerprint(final String fingerprint) throws 
AuthorizationAccessException {
-        wrappedManagedAuthorizer.inheritFingerprint(fingerprint);
-    }
-
-    @Override
-    public void checkInheritability(final String proposedFingerprint) throws 
AuthorizationAccessException, UninheritableAuthorizationsException {
-        wrappedManagedAuthorizer.checkInheritability(proposedFingerprint);
-    }
-
-    @Override
-    public AccessPolicyProvider getAccessPolicyProvider() {
-        return wrappedManagedAuthorizer.getAccessPolicyProvider();
-    }
-}
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
index 18c2a52..6f68ebe 100644
--- 
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
@@ -17,15 +17,22 @@
 package org.apache.nifi.registry.security.authorization;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.bucket.Bucket;
 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.ProxyChainAuthorizable;
+import 
org.apache.nifi.registry.security.authorization.resource.PublicCheckingAuthorizable;
 import 
org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
 import org.apache.nifi.registry.security.authorization.resource.ResourceType;
+import org.apache.nifi.registry.service.RegistryService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.util.Objects;
+
 @Component
 public class StandardAuthorizableLookup implements AuthorizableLookup {
 
@@ -103,14 +110,21 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
         }
     };
 
+    private final RegistryService registryService;
+
+    @Autowired
+    public StandardAuthorizableLookup(final RegistryService registryService) {
+        this.registryService = Objects.requireNonNull(registryService);
+    }
+
     @Override
     public Authorizable getActuatorAuthorizable() {
-        return ACTUATOR_AUTHORIZABLE;
+        return new ProxyChainAuthorizable(ACTUATOR_AUTHORIZABLE, 
PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
     }
 
     @Override
     public Authorizable getSwaggerAuthorizable() {
-        return SWAGGER_AUTHORIZABLE;
+        return new ProxyChainAuthorizable(SWAGGER_AUTHORIZABLE, 
PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
     }
 
     @Override
@@ -120,34 +134,42 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
 
     @Override
     public Authorizable getTenantsAuthorizable() {
-        return TENANTS_AUTHORIZABLE;
+        return new ProxyChainAuthorizable(TENANTS_AUTHORIZABLE, 
PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
     }
 
     @Override
     public Authorizable getPoliciesAuthorizable() {
-        return POLICIES_AUTHORIZABLE;
+        return new ProxyChainAuthorizable(POLICIES_AUTHORIZABLE, 
PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
     }
 
     @Override
     public Authorizable getBucketsAuthorizable() {
-        return BUCKETS_AUTHORIZABLE;
+        return new ProxyChainAuthorizable(BUCKETS_AUTHORIZABLE, 
PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
     }
 
     @Override
     public Authorizable getBucketAuthorizable(String bucketIdentifier) {
-        // Note - this returns a special Authorizable type that inherits 
permissions from the parent Authorizable
-        return new InheritingAuthorizable() {
+        // Note - this creates a special Authorizable type that inherits 
permissions from the parent Authorizable
+        final Authorizable inheritingAuthorizable = new 
InheritingAuthorizable() {
 
             @Override
             public Authorizable getParentAuthorizable() {
-                return getBucketsAuthorizable();
+                // Use the unwrapped buckets authorizable here so that we 
don't reauthorize the proxy chain
+                return BUCKETS_AUTHORIZABLE;
             }
 
             @Override
             public Resource getResource() {
                 return ResourceFactory.getBucketResource(bucketIdentifier, 
"Bucket with ID " + bucketIdentifier);
             }
+
         };
+
+        // Wrap the inheriting Authorizable with logic that first checks if 
public access is allowed, if not then delegates to the inheriting Authorizable
+        final Authorizable publicCheckingAuthorizable = new 
PublicCheckingAuthorizable(inheritingAuthorizable, this::isPublicAccessAllowed);
+
+        // Return ProxyChainAuthorizable -> public checking Authorizable -> 
inheriting Authorizable
+        return new ProxyChainAuthorizable(publicCheckingAuthorizable, 
PROXY_AUTHORIZABLE, this::isPublicAccessAllowed);
     }
 
     @Override
@@ -217,4 +239,44 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
         return authorizable;
     }
 
+    /**
+     * Determines if the given Resource is considered public for the action 
being performed.
+     *
+     * @param resource a Resource being authorized
+     * @param action the action being performed
+     * @return true if the resource is public for the given action, false 
otherwise
+     */
+    private boolean isPublicAccessAllowed(final Resource resource, final 
RequestAction action) {
+        if (resource == null || action == null) {
+            return false;
+        }
+
+        if (action != RequestAction.READ) {
+            return false;
+        }
+
+        final String resourceIdentifier = resource.getIdentifier();
+        if (resourceIdentifier == null || 
!resourceIdentifier.startsWith(ResourceType.Bucket.getValue() + "/")) {
+            return false;
+        }
+
+        final int lastSlashIndex = resourceIdentifier.lastIndexOf("/");
+        if (lastSlashIndex < 0 || lastSlashIndex >= 
resourceIdentifier.length() - 1) {
+            return false;
+        }
+
+        final String bucketId = resourceIdentifier.substring(lastSlashIndex + 
1);
+        try {
+            final Bucket bucket = registryService.getBucket(bucketId);
+            return bucket.isAllowPublicRead();
+        } catch (ResourceNotFoundException rnfe) {
+            // if not found then we can't determine public access, so return 
false to delegate to regular authorizer
+            logger.debug("Cannot determine public access, bucket not found 
with id [{}]", new Object[]{bucketId});
+            return false;
+        } catch (Exception e) {
+            logger.error("Error checking public access to bucket with id 
[{}]", new Object[]{bucketId}, e);
+            return false;
+        }
+    }
+
 }
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UntrustedProxyException.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UntrustedProxyException.java
new file mode 100644
index 0000000..fbf1580
--- /dev/null
+++ 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UntrustedProxyException.java
@@ -0,0 +1,29 @@
+/*
+ * 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 UntrustedProxyException extends RuntimeException {
+
+    public UntrustedProxyException(String message) {
+        super(message);
+    }
+
+    public UntrustedProxyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
index 04cb469..c461965 100644
--- 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
+++ 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java
@@ -27,9 +27,7 @@ import 
org.apache.nifi.registry.security.authorization.UserContextKeys;
 import 
org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
 import org.apache.nifi.registry.security.authorization.user.NiFiUser;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 public interface Authorizable {
@@ -95,10 +93,6 @@ public interface Authorizable {
             userContext = null;
         }
 
-        // Note: We don't include the proxy identities here since this is not 
a direct attempt to access the resource and
-        // we just want to determine if the end user is authorized. The proxy 
identities will be authorized when calling
-        // Authorizable.authorize() during a direct access attempt for a 
resource.
-
         final Resource resource = getResource();
         final Resource requestedResource = getRequestedResource();
         final AuthorizationRequest request = new AuthorizationRequest.Builder()
@@ -211,18 +205,10 @@ public interface Authorizable {
             userContext = null;
         }
 
-        final List<String> proxyChain = new ArrayList<>();
-        NiFiUser proxyUser = user.getChain();
-        while (proxyUser  != null) {
-            proxyChain.add(proxyUser.getIdentity());
-            proxyUser = proxyUser.getChain();
-        }
-
         final Resource resource = getResource();
         final Resource requestedResource = getRequestedResource();
         final AuthorizationRequest request = new AuthorizationRequest.Builder()
                 .identity(user.getIdentity())
-                .proxyIdentities(proxyChain)
                 .groups(user.getGroups())
                 .anonymous(user.isAnonymous())
                 .accessAttempt(true)
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ProxyChainAuthorizable.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ProxyChainAuthorizable.java
new file mode 100644
index 0000000..aec8d76
--- /dev/null
+++ 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ProxyChainAuthorizable.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.security.authorization.resource;
+
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.Resource;
+import org.apache.nifi.registry.security.authorization.UntrustedProxyException;
+import 
org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+/**
+ * Authorizable that wraps another Authorizable and applies logic for 
authorizing the proxy chain, unless the resource
+ * allows public access, which then skips authorizing the proxy chain.
+ */
+public class ProxyChainAuthorizable implements Authorizable {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ProxyChainAuthorizable.class);
+
+    private final Authorizable wrappedAuthorizable;
+    private final Authorizable proxyAuthorizable;
+    private final BiFunction<Resource,RequestAction,Boolean> 
publicResourceCheck;
+
+    public ProxyChainAuthorizable(final Authorizable wrappedAuthorizable,
+                                  final Authorizable proxyAuthorizable,
+                                  final 
BiFunction<Resource,RequestAction,Boolean> publicResourceCheck) {
+        this.wrappedAuthorizable = Objects.requireNonNull(wrappedAuthorizable);
+        this.proxyAuthorizable = Objects.requireNonNull(proxyAuthorizable);
+        this.publicResourceCheck = Objects.requireNonNull(publicResourceCheck);
+    }
+
+    @Override
+    public Authorizable getParentAuthorizable() {
+        if (wrappedAuthorizable.getParentAuthorizable() == null) {
+            return null;
+        } else {
+            final Authorizable parentAuthorizable = 
wrappedAuthorizable.getParentAuthorizable();
+            return new ProxyChainAuthorizable(parentAuthorizable, 
proxyAuthorizable, publicResourceCheck);
+        }
+    }
+
+    @Override
+    public Resource getResource() {
+        return wrappedAuthorizable.getResource();
+    }
+
+    @Override
+    public AuthorizationResult checkAuthorization(final Authorizer authorizer, 
final RequestAction action, final NiFiUser user,
+                                                  final Map<String, String> 
resourceContext) {
+        final Resource requestResource = 
wrappedAuthorizable.getRequestedResource();
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Requested resource is {}", new 
Object[]{requestResource.getIdentifier()});
+        }
+
+        // if public access is allowed then we want to skip proxy 
authorization so just return
+        final Boolean isPublicAccessAllowed = 
publicResourceCheck.apply(requestResource, action);
+        if (isPublicAccessAllowed) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Proxy chain will not be checked, public access 
is allowed for {} on {}",
+                        new Object[]{action.toString(), 
requestResource.getIdentifier()});
+            }
+            return AuthorizationResult.approved();
+        }
+
+        // otherwise public access is not allowed so check the proxy chain for 
the given action
+        NiFiUser proxyUser = user.getChain();
+        while (proxyUser  != null) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Checking proxy [{}] for {}", new 
Object[]{proxyUser, action});
+            }
+
+            // if the proxy is denied then break out of the loop and return a 
denied result
+            final AuthorizationResult proxyAuthorizationResult = 
proxyAuthorizable.checkAuthorization(authorizer, action, proxyUser);
+            if (proxyAuthorizationResult.getResult() == 
AuthorizationResult.Result.Denied) {
+                final String deniedMessage = String.format("Untrusted proxy 
[%s] for %s operation.", proxyUser.getIdentity(), action.toString());
+                return AuthorizationResult.denied(deniedMessage);
+            }
+
+            proxyUser = proxyUser.getChain();
+        }
+
+        // at this point the proxy chain was approved so continue to check the 
original Authorizable
+        return wrappedAuthorizable.checkAuthorization(authorizer, action, 
user, resourceContext);
+    }
+
+    @Override
+    public void authorize(final Authorizer authorizer, final RequestAction 
action, final NiFiUser user,
+                          final Map<String, String> resourceContext) throws 
AccessDeniedException {
+        final Resource requestResource = 
wrappedAuthorizable.getRequestedResource();
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Requested resource is {}", new 
Object[]{requestResource.getIdentifier()});
+        }
+
+        // if public access is allowed then we want to skip proxy 
authorization so just return
+        final Boolean isPublicAccessAllowed = 
publicResourceCheck.apply(requestResource, action);
+        if (isPublicAccessAllowed) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Proxy chain will not be authorized, public 
access is allowed for {} on {}",
+                        new Object[]{action.toString(), 
requestResource.getIdentifier()});
+            }
+            return;
+        }
+
+        // otherwise public access is not allowed so authorize proxy chain for 
the given action
+        NiFiUser proxyUser = user.getChain();
+        while (proxyUser  != null) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Authorizing proxy [{}] for {}", new 
Object[]{proxyUser, action});
+            }
+
+            try {
+                proxyAuthorizable.authorize(authorizer, action, proxyUser);
+            } catch (final AccessDeniedException e) {
+                final String actionString = action.toString();
+                throw new UntrustedProxyException(String.format("Untrusted 
proxy [%s] for %s operation.", proxyUser.getIdentity(), actionString));
+            }
+            proxyUser = proxyUser.getChain();
+        }
+
+        // at this point the proxy chain was authorized so continue to 
authorize the original Authorizable
+        wrappedAuthorizable.authorize(authorizer, action, user, 
resourceContext);
+    }
+
+}
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/PublicCheckingAuthorizable.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/PublicCheckingAuthorizable.java
new file mode 100644
index 0000000..7cacb59
--- /dev/null
+++ 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/PublicCheckingAuthorizable.java
@@ -0,0 +1,107 @@
+/*
+ * 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.resource;
+
+import org.apache.nifi.registry.security.authorization.AuthorizationResult;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.Resource;
+import 
org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+/**
+ * Authorizable that first checks if public access is allowed for the resource 
and action. If it is then it short-circuits
+ * and returns approved, otherwise it continues and delegates to the wrapped 
Authorizable.
+ */
+public class PublicCheckingAuthorizable implements Authorizable {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(PublicCheckingAuthorizable.class);
+
+    private final Authorizable wrappedAuthorizable;
+    private final BiFunction<Resource, RequestAction,Boolean> 
publicResourceCheck;
+
+    public PublicCheckingAuthorizable(final Authorizable wrappedAuthorizable,
+                                      final 
BiFunction<Resource,RequestAction,Boolean> publicResourceCheck) {
+        this.wrappedAuthorizable = Objects.requireNonNull(wrappedAuthorizable);
+        this.publicResourceCheck = Objects.requireNonNull(publicResourceCheck);
+    }
+
+    @Override
+    public Authorizable getParentAuthorizable() {
+        return wrappedAuthorizable.getParentAuthorizable();
+    }
+
+    @Override
+    public Resource getResource() {
+        return wrappedAuthorizable.getResource();
+    }
+
+    @Override
+    public AuthorizationResult checkAuthorization(final Authorizer authorizer, 
final RequestAction action, final NiFiUser user,
+                                                  final Map<String, String> 
resourceContext) {
+        final Resource resource = getResource();
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Requested resource is {}", new 
Object[]{resource.getIdentifier()});
+        }
+
+        // if public access is allowed then return approved
+        final Boolean isPublicAccessAllowed = 
publicResourceCheck.apply(resource, action);
+        if(isPublicAccessAllowed) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Public access is allowed for {}", new 
Object[]{resource.getIdentifier()});
+            }
+            return AuthorizationResult.approved();
+        }
+
+        // otherwise delegate to the original inheriting authorizable
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Delegating to inheriting authorizable for {}", new 
Object[]{resource.getIdentifier()});
+        }
+        return wrappedAuthorizable.checkAuthorization(authorizer, action, 
user, resourceContext);
+    }
+
+    @Override
+    public void authorize(final Authorizer authorizer, final RequestAction 
action, final NiFiUser user,
+                          final Map<String, String> resourceContext) throws 
AccessDeniedException {
+        final Resource resource = getResource();
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Requested resource is {}", new 
Object[]{resource.getIdentifier()});
+        }
+
+        // if public access is allowed then skip authorization and return
+        final Boolean isPublicAccessAllowed = 
publicResourceCheck.apply(resource, action);
+        if(isPublicAccessAllowed) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Public access is allowed for {}", new 
Object[]{resource.getIdentifier()});
+            }
+            return;
+        }
+
+        // otherwise delegate to the original authorizable
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Delegating to inheriting authorizable for {}", new 
Object[]{resource.getIdentifier()});
+        }
+
+        wrappedAuthorizable.authorize(authorizer, action, user, 
resourceContext);
+    }
+}
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index 12f39b6..503f27c 100644
--- 
a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ 
b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -39,6 +39,7 @@ import 
org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProv
 import org.apache.nifi.registry.security.authorization.Group;
 import org.apache.nifi.registry.security.authorization.ManagedAuthorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.UntrustedProxyException;
 import org.apache.nifi.registry.security.authorization.UserAndGroups;
 import org.apache.nifi.registry.security.authorization.UserGroupProvider;
 import 
org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext;
@@ -487,7 +488,7 @@ public class AuthorizationService {
                                         
.getAuthorizableByResource(resource.getIdentifier())
                                         .authorize(authorizer, actionType, 
NiFiUserUtils.getNiFiUser());
                                 return true;
-                            } catch (AccessDeniedException e) {
+                            } catch (AccessDeniedException | 
UntrustedProxyException e) {
                                 return false;
                             }
                         })
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
 
b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
index 33b9f40..8035bd8 100644
--- 
a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
+++ 
b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
@@ -41,8 +41,7 @@ class AuthorizationServiceSpec extends Specification {
     def setup() {
         accessPolicyProvider.getUserGroupProvider() >> userGroupProvider
         def standardAuthorizer = new 
StandardManagedAuthorizer(accessPolicyProvider, userGroupProvider)
-        def frameworkAuthorizer = new 
FrameworkManagedAuthorizer(standardAuthorizer, registryService)
-        authorizationService = new AuthorizationService(authorizableLookup, 
frameworkAuthorizer, registryService)
+        authorizationService = new AuthorizationService(authorizableLookup, 
standardAuthorizer, registryService)
     }
 
     // ----- User tests -------------------------------------------------------
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestFrameworkAuthorizer.java
 
b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestFrameworkAuthorizer.java
deleted file mode 100644
index 2cc03f8..0000000
--- 
a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestFrameworkAuthorizer.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * 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.bucket.Bucket;
-import 
org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
-import org.apache.nifi.registry.service.RegistryService;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentMatcher;
-
-import java.util.Arrays;
-import java.util.UUID;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class TestFrameworkAuthorizer {
-
-    private Authorizer frameworkAuthorizer;
-    private Authorizer wrappedAuthorizer;
-    private RegistryService registryService;
-
-    private Bucket bucketPublic;
-    private Bucket bucketNotPublic;
-
-    @Before
-    public void setup() {
-        wrappedAuthorizer = mock(Authorizer.class);
-        registryService = mock(RegistryService.class);
-        frameworkAuthorizer = new FrameworkAuthorizer(wrappedAuthorizer, 
registryService);
-
-        bucketPublic = new Bucket();
-        bucketPublic.setIdentifier(UUID.randomUUID().toString());
-        bucketPublic.setName("Public Bucket");
-        bucketPublic.setAllowPublicRead(true);
-
-        bucketNotPublic = new Bucket();
-        bucketNotPublic.setIdentifier(UUID.randomUUID().toString());
-        bucketNotPublic.setName("Non Public Bucket");
-        bucketNotPublic.setAllowPublicRead(false);
-
-        
when(registryService.getBucket(bucketPublic.getIdentifier())).thenReturn(bucketPublic);
-        
when(registryService.getBucket(bucketNotPublic.getIdentifier())).thenReturn(bucketNotPublic);
-    }
-
-    @Test
-    public void testReadPublicBucketWhenAnonymous() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketPublic.getIdentifier(), 
bucketPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.READ)
-                .accessAttempt(true)
-                .identity("anonymous")
-                .anonymous(true)
-                .build();
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Approved, result.getResult());
-
-        // should never make it to wrapped authorizer
-        verify(wrappedAuthorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
-    }
-
-    @Test
-    public void testReadNonPublicBucketWhenAnonymous() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), 
bucketNotPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.READ)
-                .accessAttempt(true)
-                .identity("anonymous")
-                .anonymous(true)
-                .build();
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
-        // should be denied before making it to the wrapped authorizer since 
the user is anonymous
-        verify(wrappedAuthorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
-    }
-
-    @Test
-    public void testWritePublicBucketWhenAnonymous() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketPublic.getIdentifier(), 
bucketPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.WRITE)
-                .accessAttempt(true)
-                .identity("anonymous")
-                .anonymous(true)
-                .build();
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
-        // should be denied before making it to wrapped authorizer since 
request is anonymous
-        verify(wrappedAuthorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
-    }
-
-    @Test
-    public void testReadPublicBucketWhenNotAnonymous() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketPublic.getIdentifier(), 
bucketPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.READ)
-                .accessAttempt(true)
-                .identity("user1")
-                .anonymous(false)
-                .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
-                .build();
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Approved, result.getResult());
-
-        // should never make it to wrapped authorizer
-        verify(wrappedAuthorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
-    }
-
-    @Test
-    public void testReadNonPublicBucketWhenNotAnonymousAndAuthorizedProxies() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), 
bucketNotPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.READ)
-                .accessAttempt(true)
-                .identity("user1")
-                .anonymous(false)
-                .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
-                .build();
-
-        // since the bucket is not public it will fall through to the wrapped 
authorizer
-        when(wrappedAuthorizer.authorize(any(AuthorizationRequest.class)))
-                .thenReturn(AuthorizationResult.approved());
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Approved, result.getResult());
-
-        // should make 3 calls to the wrapped authorizer to authorize user1, 
proxy1, proxy2
-        verify(wrappedAuthorizer, 
times(3)).authorize(any(AuthorizationRequest.class));
-    }
-
-    @Test
-    public void testReadNonPublicBucketWhenNotAnonymousAndUnauthorizedProxy() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), 
bucketNotPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.READ)
-                .accessAttempt(true)
-                .identity("user1")
-                .anonymous(false)
-                .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
-                .build();
-
-        // since the bucket is not public and the user is not anonymous, it 
will continue to proxy authorization
-
-        // simulate the first proxy being authorized for READ actions
-        final AuthorizationRequestMatcher proxy1Matcher = new 
AuthorizationRequestMatcher(
-                "proxy1", ResourceFactory.getProxyResource(), 
request.getAction());
-        
when(wrappedAuthorizer.authorize(argThat(proxy1Matcher))).thenReturn(AuthorizationResult.approved());
-
-        // simulate the second proxy being unauthorized for READ actions
-        final AuthorizationRequestMatcher proxy2Matcher = new 
AuthorizationRequestMatcher(
-                "proxy2", ResourceFactory.getProxyResource(), 
request.getAction());
-        
when(wrappedAuthorizer.authorize(argThat(proxy2Matcher))).thenReturn(AuthorizationResult.denied("denied"));
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
-        // should make 2 calls to the wrapped authorizer for the two proxies
-        verify(wrappedAuthorizer, 
times(2)).authorize(any(AuthorizationRequest.class));
-    }
-
-    @Test
-    public void 
testReadNonPublicBucketWhenNotAnonymousAndUnauthorizedEndUser() {
-        final Resource resource = 
ResourceFactory.getBucketResource(bucketNotPublic.getIdentifier(), 
bucketNotPublic.getName());
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .resource(resource)
-                .requestedResource(resource)
-                .action(RequestAction.READ)
-                .accessAttempt(true)
-                .identity("user1")
-                .anonymous(false)
-                .proxyIdentities(Arrays.asList("proxy1", "proxy2"))
-                .build();
-
-        // since the bucket is not public and the user is not anonymous, it 
will continue to proxy authorization
-
-        // simulate the first proxy being authorized for READ actions
-        final AuthorizationRequestMatcher proxy1Matcher = new 
AuthorizationRequestMatcher(
-                "proxy1", ResourceFactory.getProxyResource(), 
request.getAction());
-        
when(wrappedAuthorizer.authorize(argThat(proxy1Matcher))).thenReturn(AuthorizationResult.approved());
-
-        // simulate the second proxy being authorized for READ actions
-        final AuthorizationRequestMatcher proxy2Matcher = new 
AuthorizationRequestMatcher(
-                "proxy2", ResourceFactory.getProxyResource(), 
request.getAction());
-        
when(wrappedAuthorizer.authorize(argThat(proxy2Matcher))).thenReturn(AuthorizationResult.approved());
-
-        // simulate the end user being unauthorized for READ actions
-        final AuthorizationRequestMatcher user1Matcher = new 
AuthorizationRequestMatcher(
-                "user1", resource, request.getAction());
-        
when(wrappedAuthorizer.authorize(argThat(user1Matcher))).thenReturn(AuthorizationResult.denied("denied"));
-
-        final AuthorizationResult result = 
frameworkAuthorizer.authorize(request);
-        assertNotNull(result);
-        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
-
-        // should make 3 calls to the wrapped authorizer for the two proxies 
and end user
-        verify(wrappedAuthorizer, 
times(3)).authorize(any(AuthorizationRequest.class));
-    }
-
-
-    /**
-     * Matcher for matching Authorization requests.
-     */
-    private static class AuthorizationRequestMatcher implements 
ArgumentMatcher<AuthorizationRequest> {
-
-        private final String identity;
-        private final Resource resource;
-        private final RequestAction action;
-
-        public AuthorizationRequestMatcher(final String identity, final 
Resource resource, final RequestAction action) {
-            this.identity = identity;
-            this.resource = resource;
-            this.action = action;
-        }
-
-        @Override
-        public boolean matches(final AuthorizationRequest request) {
-            if (request == null) {
-                return false;
-            }
-
-            return identity.equals(request.getIdentity())
-                    && 
resource.getIdentifier().equals(request.getResource().getIdentifier())
-                    && action == request.getAction();
-        }
-    }
-}
diff --git 
a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestStandardAuthorizableLookup.java
 
b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestStandardAuthorizableLookup.java
new file mode 100644
index 0000000..2804ac7
--- /dev/null
+++ 
b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/authorization/TestStandardAuthorizableLookup.java
@@ -0,0 +1,404 @@
+/*
+ * 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.bucket.Bucket;
+import 
org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.resource.Authorizable;
+import 
org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser;
+import org.apache.nifi.registry.service.RegistryService;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TestStandardAuthorizableLookup {
+
+    private static final NiFiUser USER_NO_PROXY_CHAIN = new 
StandardNiFiUser.Builder()
+            .identity("user1")
+            .build();
+
+    private static final NiFiUser USER_WITH_PROXY_CHAIN = new 
StandardNiFiUser.Builder()
+            .identity("user1")
+            .chain(new StandardNiFiUser.Builder().identity("CN=localhost, 
OU=NIFI").build())
+            .build();
+
+    private Authorizer authorizer;
+    private RegistryService registryService;
+    private AuthorizableLookup authorizableLookup;
+
+    private Bucket bucketPublic;
+    private Bucket bucketNotPublic;
+
+    @Before
+    public void setup() {
+        authorizer = mock(Authorizer.class);
+        registryService = mock(RegistryService.class);
+        authorizableLookup = new StandardAuthorizableLookup(registryService);
+
+        bucketPublic = new Bucket();
+        bucketPublic.setIdentifier(UUID.randomUUID().toString());
+        bucketPublic.setName("Public Bucket");
+        bucketPublic.setAllowPublicRead(true);
+
+        bucketNotPublic = new Bucket();
+        bucketNotPublic.setIdentifier(UUID.randomUUID().toString());
+        bucketNotPublic.setName("Non Public Bucket");
+        bucketNotPublic.setAllowPublicRead(false);
+
+        
when(registryService.getBucket(bucketPublic.getIdentifier())).thenReturn(bucketPublic);
+        
when(registryService.getBucket(bucketNotPublic.getIdentifier())).thenReturn(bucketNotPublic);
+    }
+
+    // Test check method for Bucket Authorizable
+
+    @Test
+    public void testCheckReadPublicBucketWithNoProxyChain() {
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        final AuthorizationResult result = 
bucketAuthorizable.checkAuthorization(authorizer, RequestAction.READ, 
USER_NO_PROXY_CHAIN);
+        assertNotNull(result);
+        assertEquals(AuthorizationResult.Result.Approved, result.getResult());
+
+        // Should never call authorizer because resource is public
+        verify(authorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void testCheckReadPublicBucketWithProxyChain() {
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        final AuthorizationResult result = 
bucketAuthorizable.checkAuthorization(authorizer, RequestAction.READ, 
USER_WITH_PROXY_CHAIN);
+        assertNotNull(result);
+        assertEquals(AuthorizationResult.Result.Approved, result.getResult());
+
+        // Should never call authorizer because resource is public
+        verify(authorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void 
testCheckWritePublicBucketWithUnauthorizedUserAndNoProxyChain() {
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to the specific bucket
+        final AuthorizationRequest expectedBucketAuthorizationRequest = 
getBucketAuthorizationRequest(
+                bucketPublic.getIdentifier(), action, USER_NO_PROXY_CHAIN);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // second request will go to parent of /buckets
+        final AuthorizationRequest expectedBucketsAuthorizationRequest = 
getBucketsAuthorizationRequest(action, USER_NO_PROXY_CHAIN);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // should reach authorizer and return denied
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        final AuthorizationResult result = 
bucketAuthorizable.checkAuthorization(authorizer, action, USER_NO_PROXY_CHAIN);
+        assertNotNull(result);
+        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
+
+        // Should call authorizer twice for specific bucket and top-level 
/buckets
+        verify(authorizer, 
times(2)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void testCheckWritePublicBucketWithUnauthorizedProxyChain() {
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to authorize the proxy
+        final AuthorizationRequest expectedProxyAuthorizationRequest = 
getProxyAuthorizationRequest(action, USER_WITH_PROXY_CHAIN.getChain());
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // the authorization of the proxy chain should return denied
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        final AuthorizationResult result = 
bucketAuthorizable.checkAuthorization(authorizer, action, 
USER_WITH_PROXY_CHAIN);
+        assertNotNull(result);
+        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
+
+        // Should never call authorizer once for /proxy and then return denied
+        verify(authorizer, 
times(1)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void 
testCheckWritePublicBucketWithUnauthorizedUserAndAuthorizedProxyChain() {
+        final NiFiUser user = USER_WITH_PROXY_CHAIN;
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to authorize the proxy
+        final AuthorizationRequest expectedProxyAuthorizationRequest = 
getProxyAuthorizationRequest(action, user.getChain());
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.approved());
+
+        // second request will be to the specific bucket
+        final AuthorizationRequest expectedBucketAuthorizationRequest = 
getBucketAuthorizationRequest(
+                bucketPublic.getIdentifier(), action, user);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // third request will go to parent of /buckets
+        final AuthorizationRequest expectedBucketsAuthorizationRequest = 
getBucketsAuthorizationRequest(action, user);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // the authorization of the proxy chain should return denied
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        final AuthorizationResult result = 
bucketAuthorizable.checkAuthorization(authorizer, action, user);
+        assertNotNull(result);
+        assertEquals(AuthorizationResult.Result.Denied, result.getResult());
+
+        // Should call authorizer three time for /proxy, /bucket/{id}, and 
/buckets
+        verify(authorizer, 
times(3)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void 
testCheckWritePublicBucketWithAuthorizedUserAndAuthorizedProxyChain() {
+        final NiFiUser user = USER_WITH_PROXY_CHAIN;
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to authorize the proxy
+        final AuthorizationRequest expectedProxyAuthorizationRequest = 
getProxyAuthorizationRequest(action, user.getChain());
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.approved());
+
+        // second request will be to the specific bucket
+        final AuthorizationRequest expectedBucketAuthorizationRequest = 
getBucketAuthorizationRequest(
+                bucketPublic.getIdentifier(), action, user);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.approved());
+
+        // the authorization should all return approved
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        final AuthorizationResult result = 
bucketAuthorizable.checkAuthorization(authorizer, action, user);
+        assertNotNull(result);
+        assertEquals(AuthorizationResult.Result.Approved, result.getResult());
+
+        // Should call authorizer two times for /proxy and /bucket/{id}
+        verify(authorizer, 
times(2)).authorize(any(AuthorizationRequest.class));
+    }
+
+    // Test authorize method for Bucket Authorizable
+
+    @Test
+    public void testAuthorizeReadPublicBucketWithNoProxyChain() {
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        bucketAuthorizable.authorize(authorizer, RequestAction.READ, 
USER_NO_PROXY_CHAIN);
+
+        // Should never call authorizer because resource is public
+        verify(authorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void testAuthorizeReadPublicBucketWithProxyChain() {
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        bucketAuthorizable.authorize(authorizer, RequestAction.READ, 
USER_WITH_PROXY_CHAIN);
+
+        // Should never call authorizer because resource is public
+        verify(authorizer, 
times(0)).authorize(any(AuthorizationRequest.class));
+    }
+
+    @Test
+    public void 
testAuthorizeWritePublicBucketWithUnauthorizedUserAndNoProxyChain() {
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to the specific bucket
+        final AuthorizationRequest expectedBucketAuthorizationRequest = 
getBucketAuthorizationRequest(
+                bucketPublic.getIdentifier(), action, USER_NO_PROXY_CHAIN);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // second request will go to parent of /buckets
+        final AuthorizationRequest expectedBucketsAuthorizationRequest = 
getBucketsAuthorizationRequest(action, USER_NO_PROXY_CHAIN);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // should reach authorizer and throw access denied
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        try {
+            bucketAuthorizable.authorize(authorizer, action, 
USER_NO_PROXY_CHAIN);
+            Assert.fail("Should have thrown exception");
+        } catch (AccessDeniedException e) {
+            // Should never call authorizer twice for specific bucket and 
top-level /buckets
+            verify(authorizer, 
times(2)).authorize(any(AuthorizationRequest.class));
+        }
+    }
+
+    @Test
+    public void testAuthorizeWritePublicBucketWithUnauthorizedProxyChain() {
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to authorize the proxy
+        final AuthorizationRequest expectedProxyAuthorizationRequest = 
getProxyAuthorizationRequest(action, USER_WITH_PROXY_CHAIN.getChain());
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // the authorization of the proxy chain should throw 
UntrustedProxyException
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        try {
+            bucketAuthorizable.authorize(authorizer, action, 
USER_WITH_PROXY_CHAIN);
+            Assert.fail("Should have thrown exception");
+        } catch (UntrustedProxyException e) {
+            // Should call authorizer once for /proxy and then throw exception
+            verify(authorizer, 
times(1)).authorize(any(AuthorizationRequest.class));
+        }
+    }
+
+    @Test
+    public void 
testAuthorizeWritePublicBucketWithUnauthorizedUserAndAuthorizedProxyChain() {
+        final NiFiUser user = USER_WITH_PROXY_CHAIN;
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to authorize the proxy
+        final AuthorizationRequest expectedProxyAuthorizationRequest = 
getProxyAuthorizationRequest(action, user.getChain());
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.approved());
+
+        // second request will be to the specific bucket
+        final AuthorizationRequest expectedBucketAuthorizationRequest = 
getBucketAuthorizationRequest(
+                bucketPublic.getIdentifier(), action, user);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // third request will go to parent of /buckets
+        final AuthorizationRequest expectedBucketsAuthorizationRequest = 
getBucketsAuthorizationRequest(action, user);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketsAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.denied());
+
+        // the authorization of the proxy chain should throw 
UntrustedProxyException
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        try {
+            bucketAuthorizable.authorize(authorizer, action, user);
+            Assert.fail("Should have thrown exception");
+        } catch (AccessDeniedException e) {
+            // Should call authorizer three times for /proxy, /bucket/{id}, 
and /buckets
+            verify(authorizer, 
times(3)).authorize(any(AuthorizationRequest.class));
+        }
+    }
+
+    @Test
+    public void 
testAuthorizeWritePublicBucketWithAuthorizedUserAndAuthorizedProxyChain() {
+        final NiFiUser user = USER_WITH_PROXY_CHAIN;
+        final RequestAction action = RequestAction.WRITE;
+
+        // first request will be to authorize the proxy
+        final AuthorizationRequest expectedProxyAuthorizationRequest = 
getProxyAuthorizationRequest(action, user.getChain());
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedProxyAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.approved());
+
+        // second request will be to the specific bucket
+        final AuthorizationRequest expectedBucketAuthorizationRequest = 
getBucketAuthorizationRequest(
+                bucketPublic.getIdentifier(), action, user);
+
+        when(authorizer.authorize(argThat(new 
AuthorizationRequestMatcher(expectedBucketAuthorizationRequest))))
+                .thenReturn(AuthorizationResult.approved());
+
+        // the authorization should all return approved so no exception
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketPublic.getIdentifier());
+        bucketAuthorizable.authorize(authorizer, action, user);
+
+        // Should call authorizer two times for /proxy and /bucket/{id}
+        verify(authorizer, 
times(2)).authorize(any(AuthorizationRequest.class));
+    }
+
+    private AuthorizationRequest getBucketAuthorizationRequest(final String 
bucketIdentifier, final RequestAction action, final NiFiUser user) {
+        return new AuthorizationRequest.Builder()
+                .resource(ResourceFactory.getBucketResource(bucketIdentifier, 
bucketIdentifier))
+                .action(action)
+                .identity(user.getIdentity())
+                .accessAttempt(true)
+                .anonymous(false)
+                .build();
+    }
+
+    private AuthorizationRequest getBucketsAuthorizationRequest(final 
RequestAction action, final NiFiUser user) {
+        return new AuthorizationRequest.Builder()
+                .resource(ResourceFactory.getBucketsResource())
+                .action(action)
+                .identity(user.getIdentity())
+                .accessAttempt(true)
+                .anonymous(false)
+                .build();
+    }
+
+    private AuthorizationRequest getProxyAuthorizationRequest(final 
RequestAction action, final NiFiUser user) {
+        return new AuthorizationRequest.Builder()
+                .resource(ResourceFactory.getProxyResource())
+                .action(action)
+                .identity(user.getIdentity())
+                .accessAttempt(true)
+                .anonymous(false)
+                .build();
+    }
+
+    /**
+     * ArugmentMatcher for AuthorizationRequest.
+     */
+    private static class AuthorizationRequestMatcher implements 
ArgumentMatcher<AuthorizationRequest> {
+
+        private final AuthorizationRequest expectedAuthorizationRequest;
+
+        public AuthorizationRequestMatcher(final AuthorizationRequest 
expectedAuthorizationRequest) {
+            this.expectedAuthorizationRequest = expectedAuthorizationRequest;
+        }
+
+        @Override
+        public boolean matches(final AuthorizationRequest 
authorizationRequest) {
+            if (authorizationRequest == null) {
+                return false;
+            }
+
+            final String requestResourceId = 
authorizationRequest.getResource().getIdentifier();
+            final String expectedResourceId = 
expectedAuthorizationRequest.getResource().getIdentifier();
+
+            final String requestAction = 
authorizationRequest.getAction().toString();
+            final String expectedAction = 
expectedAuthorizationRequest.getAction().toString();
+
+            final String requestUserIdentity = 
authorizationRequest.getIdentity();
+            final String expectedUserIdentity = 
authorizationRequest.getIdentity();
+
+            return requestResourceId.equals(expectedResourceId)
+                    && requestAction.equals(expectedAction)
+                    && requestUserIdentity.equals(expectedUserIdentity);
+        }
+    }
+}
diff --git 
a/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
 
b/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
index 56b7b45..3e832fa 100644
--- 
a/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
+++ 
b/nifi-registry-core/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java
@@ -109,6 +109,8 @@ public class AuthorizationRequest {
      * The identities in the proxy chain for the request. Will be empty if the 
request was not proxied.
      *
      * @return The identities in the proxy chain
+     *
+     * @deprecated no longer populated
      */
     public List<String> getProxyIdentities() {
         return proxyIdentities;
@@ -210,6 +212,9 @@ public class AuthorizationRequest {
             return this;
         }
 
+        /**
+         * @deprecated no longer populated by the framework
+         */
         public Builder proxyIdentities(final List<String> proxyIdentities) {
             this.proxyIdentities = proxyIdentities;
             return this;
diff --git 
a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UntrustedProxyExceptionMapper.java
 
b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UntrustedProxyExceptionMapper.java
new file mode 100644
index 0000000..453dbd5
--- /dev/null
+++ 
b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UntrustedProxyExceptionMapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.web.mapper;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.security.authorization.UntrustedProxyException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps an UntrustedProxyException to a FORBIDDEN response.
+ */
+@Component
+@Provider
+public class UntrustedProxyExceptionMapper implements 
ExceptionMapper<UntrustedProxyException> {
+
+    private static Logger LOGGER = 
LoggerFactory.getLogger(UntrustedProxyException.class);
+
+    @Override
+    public Response toResponse(final UntrustedProxyException exception) {
+        LOGGER.info("{}. Returning {} response.", exception, 
Response.Status.FORBIDDEN);
+        LOGGER.debug(StringUtils.EMPTY, exception);
+
+        return Response.status(Response.Status.FORBIDDEN)
+                .entity(exception.getMessage())
+                .type("text/plain")
+                .build();
+    }
+}
diff --git 
a/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
 
b/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
index e27dfbe..806f73c 100644
--- 
a/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
+++ 
b/nifi-registry-core/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
@@ -20,6 +20,7 @@ import 
org.apache.nifi.registry.security.authorization.exception.AccessDeniedExc
 import org.apache.nifi.registry.security.authorization.resource.Authorizable
 import org.apache.nifi.registry.security.authorization.resource.ResourceType
 import org.apache.nifi.registry.service.AuthorizationService
+import org.apache.nifi.registry.service.RegistryService
 import 
org.apache.nifi.registry.web.security.authorization.HttpMethodAuthorizationRules
 import 
org.apache.nifi.registry.web.security.authorization.ResourceAuthorizationFilter
 import 
org.apache.nifi.registry.web.security.authorization.StandardHttpMethodAuthorizationRules
@@ -34,7 +35,8 @@ import javax.servlet.http.HttpServletResponse
 
 class ResourceAuthorizationFilterSpec extends Specification {
 
-    AuthorizableLookup authorizableLookup = new StandardAuthorizableLookup()
+    RegistryService registryService = Mock(RegistryService)
+    AuthorizableLookup authorizableLookup = new 
StandardAuthorizableLookup(registryService)
     AuthorizationService mockAuthorizationService = Mock(AuthorizationService)
     FilterChain mockFilterChain = Mock(FilterChain)
     ResourceAuthorizationFilter.Builder resourceAuthorizationFilterBuilder

Reply via email to