Repository: nifi-registry
Updated Branches:
  refs/heads/master c6aaca673 -> 1755d8400


NIFIREG-134 Enable SpringBoot Actuator endpoints

- Configures Jersey as a filter (previously was a servlet) that
  forwards requests to /actuator/* so they can be handled by Actuator
- Adds a ResourceAuthorizationFilter that performs authorization in
  the filter chain, and configures it to gate access to /actuator/*
- Adds test cases for ResourceAuthorizationFilter

This closes #97.

Signed-off-by: Bryan Bende <[email protected]>


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

Branch: refs/heads/master
Commit: 1755d840052645c916916438649566fc04eeaf2e
Parents: c6aaca6
Author: Kevin Doran <[email protected]>
Authored: Thu Jan 25 09:14:53 2018 -0500
Committer: Bryan Bende <[email protected]>
Committed: Mon Mar 12 11:19:22 2018 -0400

----------------------------------------------------------------------
 .../authorization/AuthorizableLookup.java       |   9 +
 .../security/authorization/AuthorizeAccess.java |  21 --
 .../StandardAuthorizableLookup.java             |  65 ++++--
 .../file/FileAccessPolicyProvider.java          |   3 +
 .../authorization/resource/ResourceFactory.java |  71 +++---
 .../authorization/resource/ResourceType.java    |  37 +++-
 .../registry/service/AuthorizationService.java  |  16 +-
 .../service/AuthorizationServiceSpec.groovy     |  16 +-
 nifi-registry-web-api/pom.xml                   |  18 ++
 .../registry/NiFiRegistryApiApplication.java    |  21 +-
 .../web/NiFiRegistryResourceConfig.java         |   4 +
 .../registry/web/api/AccessPolicyResource.java  |  12 +-
 .../api/AuthorizableApplicationResource.java    |  14 +-
 .../registry/web/api/BucketFlowResource.java    |   6 +-
 .../nifi/registry/web/api/BucketResource.java   |  13 +-
 .../nifi/registry/web/api/FlowResource.java     |  10 +-
 .../nifi/registry/web/api/ItemResource.java     |   6 +-
 .../nifi/registry/web/api/TenantResource.java   |  16 +-
 .../security/NiFiRegistrySecurityConfig.java    |  29 ++-
 .../IdentityAuthenticationFilter.java           | 148 -------------
 .../security/authentication/IdentityFilter.java |   4 +-
 .../HttpMethodAuthorizationRules.java           |  48 ++++
 .../ResourceAuthorizationFilter.java            | 218 +++++++++++++++++++
 .../StandardHttpMethodAuthorizationRules.java   |  40 ++++
 .../ResourceAuthorizationFilterSpec.groovy      | 170 +++++++++++++++
 .../nifi/registry/web/api/SecureFileIT.java     |   1 +
 26 files changed, 713 insertions(+), 303 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
index 2ba7227..1842199 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java
@@ -21,6 +21,13 @@ import 
org.apache.nifi.registry.security.authorization.resource.Authorizable;
 public interface AuthorizableLookup {
 
     /**
+     * Get the authorizable for /actuator.
+     *
+     * @return authorizable
+     */
+    Authorizable getActuatorAuthorizable();
+
+    /**
      * Get the authorizable for /proxy.
      *
      * @return authorizable
@@ -59,6 +66,8 @@ public interface AuthorizableLookup {
 
     /**
      * Get the authorizable of the specified resource.
+     * If the resource is authorized by its base/top-level
+     * resource type, the authorizable for the base type will be returned.
      *
      * @param resource resource
      * @return authorizable

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java
deleted file mode 100644
index 94dc3be..0000000
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java
+++ /dev/null
@@ -1,21 +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;
-
-public interface AuthorizeAccess {
-    void authorize(AuthorizableLookup lookup);
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
index 00d318a..50b7185 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java
@@ -22,11 +22,15 @@ 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() {
@@ -75,6 +79,23 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
         }
     };
 
+    private static final Authorizable ACTUATOR_AUTHORIZABLE = new 
Authorizable() {
+        @Override
+        public Authorizable getParentAuthorizable() {
+            return null;
+        }
+
+        @Override
+        public Resource getResource() {
+            return ResourceFactory.getActuatorResource();
+        }
+    };
+
+    @Override
+    public Authorizable getActuatorAuthorizable() {
+        return ACTUATOR_AUTHORIZABLE;
+    }
+
     @Override
     public Authorizable getProxyAuthorizable() {
         return PROXY_AUTHORIZABLE;
@@ -114,12 +135,7 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
 
     @Override
     public Authorizable getAuthorizableByResource(String resource) {
-        ResourceType resourceType = null;
-        for (ResourceType type : ResourceType.values()) {
-            if (resource.equals(type.getValue()) || 
resource.startsWith(type.getValue() + "/")) {
-                resourceType = type;
-            }
-        }
+        ResourceType resourceType = 
ResourceType.mapFullResourcePathToResourceType(resource);
 
         if (resourceType == null) {
             throw new ResourceNotFoundException("Unrecognized resource: " + 
resource);
@@ -129,21 +145,10 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
     }
 
     private Authorizable getAuthorizableByResource(final ResourceType 
resourceType, final String resource) {
-        final String childResourceId = StringUtils.substringAfter(resource, 
resourceType.getValue());
-        if (childResourceId.startsWith("/")) {
-            return getAuthorizableByChildResource(resourceType, 
childResourceId.substring(1));
-        } else {
-            return getAuthorizableByResource(resourceType);
-        }
-    }
-
-    private Authorizable getAuthorizableByResource(final ResourceType 
resourceType) {
         Authorizable authorizable = null;
         switch (resourceType) {
 
-            case Bucket:
-                authorizable = getBucketsAuthorizable();
-                break;
+            /* Access to these resources are always authorized by the 
top-level resource */
             case Policy:
                 authorizable = getPoliciesAuthorizable();
                 break;
@@ -152,10 +157,24 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
                 break;
             case Proxy:
                 authorizable = getProxyAuthorizable();
+                break;
+            case Actuator:
+                authorizable = getActuatorAuthorizable();
+                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) {
-            throw new IllegalArgumentException("An unexpected type of resource 
in this policy " + resourceType.getValue());
+            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;
@@ -165,8 +184,12 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
         Authorizable authorizable;
         switch (baseResourceType) {
             case Bucket:
-                authorizable = getBucketAuthorizable(childResourceId);
-                break;
+                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());
         }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java
index e4a03f3..83ceb50 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java
@@ -125,6 +125,9 @@ public class FileAccessPolicyProvider implements 
ConfigurableAccessPolicyProvide
             new ResourceActionPair("/buckets", READ_CODE),
             new ResourceActionPair("/buckets", WRITE_CODE),
             new ResourceActionPair("/buckets", DELETE_CODE),
+            new ResourceActionPair("/actuator", READ_CODE),
+            new ResourceActionPair("/actuator", WRITE_CODE),
+            new ResourceActionPair("/actuator", DELETE_CODE),
             new ResourceActionPair("/proxy", WRITE_CODE)
     };
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
index b81b873..845e719 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java
@@ -39,77 +39,85 @@ public final class ResourceFactory {
         }
     };
 
-
-    private final static Resource POLICY_RESOURCE = new Resource() {
+    private final static Resource PROXY_RESOURCE = new Resource() {
         @Override
         public String getIdentifier() {
-            return ResourceType.Policy.getValue();
+            return ResourceType.Proxy.getValue();
         }
 
         @Override
         public String getName() {
-            return "Policies for ";
+            return "Proxy User Requests";
         }
 
         @Override
         public String getSafeDescription() {
-            return "the policies for ";
+            return "proxy requests on behalf of users";
         }
     };
 
-    private final static Resource PROXY_RESOURCE = new Resource() {
+    private final static Resource TENANTS_RESOURCE = new Resource() {
         @Override
         public String getIdentifier() {
-            return ResourceType.Proxy.getValue();
+            return ResourceType.Tenant.getValue();
         }
 
         @Override
         public String getName() {
-            return "Proxy User Requests";
+            return "Tenants";
         }
 
         @Override
         public String getSafeDescription() {
-            return "proxy requests on behalf of users";
+            return "users/user groups";
         }
     };
 
-    private final static Resource TENANTS_RESOURCE = new Resource() {
+    private final static Resource POLICIES_RESOURCE = new Resource() {
+
         @Override
         public String getIdentifier() {
-            return ResourceType.Tenant.getValue();
+            return ResourceType.Policy.getValue();
         }
 
         @Override
         public String getName() {
-            return "Tenants";
+            return "Access Policies";
         }
 
         @Override
         public String getSafeDescription() {
-            return "users/user groups";
+            return "policies";
         }
     };
 
-    private final static Resource POLICIES_RESOURCE = new Resource() {
-
+    private final static Resource ACTUATOR_RESOURCE = new Resource() {
         @Override
         public String getIdentifier() {
-            return "/policies";
+            return ResourceType.Actuator.getValue();
         }
 
         @Override
         public String getName() {
-            return "Access Policies";
+            return "Actuator";
         }
 
         @Override
         public String getSafeDescription() {
-            return "policies";
+            return "actuator";
         }
     };
 
     /**
+     * Gets the Resource for actuator system management endpoints.
+     *
+     * @return  The resource for actuator system management endpoints.
+     */
+    public static Resource getActuatorResource() {
+        return ACTUATOR_RESOURCE;
+    }
+
+    /**
      * Gets the Resource for proxying a user request.
      *
      * @return  The resource for proxying a user request
@@ -152,33 +160,6 @@ public final class ResourceFactory {
     }
 
     /**
-     * Gets a Resource for accessing a resources's policies.
-     *
-     * @param resource      The resource being accessed
-     * @return              The resource
-     */
-    public static Resource getPolicyResource(final Resource resource) {
-        Objects.requireNonNull(resource, "The resource type must be 
specified.");
-
-        return new Resource() {
-            @Override
-            public String getIdentifier() {
-                return String.format("%s%s", POLICY_RESOURCE.getIdentifier(), 
resource.getIdentifier());
-            }
-
-            @Override
-            public String getName() {
-                return POLICY_RESOURCE.getName() + resource.getName();
-            }
-
-            @Override
-            public String getSafeDescription() {
-                return POLICY_RESOURCE.getSafeDescription() + 
resource.getSafeDescription();
-            }
-        };
-    }
-
-    /**
      * Get a Resource object for any object that has a base type and an 
identifier, ie:
      * /buckets/{uuid}
      *

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
index 7274b56..00358ff 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java
@@ -20,7 +20,8 @@ public enum ResourceType {
     Bucket("/buckets"),
     Policy("/policies"),
     Proxy("/proxy"),
-    Tenant("/tenants");
+    Tenant("/tenants"),
+    Actuator("/actuator");
 
     final String value;
 
@@ -48,4 +49,38 @@ public enum ResourceType {
 
         return type;
     }
+
+    /**
+     * Map an arbitrary resource path to its base resource type. The base 
resource type is
+     * what the resource path starts with.
+     *
+     * The resourcePath arg is expected to be a string of the format:
+     *
+     * {ResourceTypeValue}/arbitrary/sub-resource/path
+     *
+     * For example:
+     *   /buckets -> ResourceType.Bucket
+     *   /buckets/bucketId -> ResourceType.Bucket
+     *   /policies/read/buckets -> ResourceType.Policy
+     *
+     * @param resourcePath the path component of a URI (not including the 
context path)
+     * @return the base resource type
+     */
+    public static ResourceType mapFullResourcePathToResourceType(final String 
resourcePath) {
+        if (resourcePath == null) {
+            throw new IllegalArgumentException("Resource path must not be 
null");
+        }
+
+        ResourceType type = null;
+
+        for (final ResourceType rt : values()) {
+            final String rtValue = rt.getValue();
+            if(resourcePath.equals(rtValue) || resourcePath.startsWith(rtValue 
+ "/"))  {
+                type = rt;
+                break;
+            }
+        }
+
+        return type;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index 219885c..12f6f26 100644
--- 
a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ 
b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -29,7 +29,6 @@ import org.apache.nifi.registry.bucket.Bucket;
 import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
 import 
org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext;
 import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
-import org.apache.nifi.registry.security.authorization.AuthorizeAccess;
 import org.apache.nifi.registry.security.authorization.Authorizer;
 import 
org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
 import 
org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext;
@@ -100,8 +99,16 @@ public class AuthorizationService {
 
     // ---------------------- Authorization methods 
-------------------------------------
 
-    public void authorizeAccess(final AuthorizeAccess authorizeAccess) {
-        authorizeAccess.authorize(authorizableLookup);
+    public AuthorizableLookup getAuthorizableLookup() {
+        return authorizableLookup;
+    }
+
+    public Authorizer getAuthorizer() {
+        return authorizer;
+    }
+
+    public void authorize(Authorizable authorizable, RequestAction action) 
throws AccessDeniedException {
+        authorizable.authorize(authorizer, action, 
NiFiUserUtils.getNiFiUser());
     }
 
     // ---------------------- Permissions methods 
---------------------------------------
@@ -520,6 +527,9 @@ public class AuthorizationService {
         if (includeFilter == null || includeFilter.equals(ResourceType.Proxy)) 
{
             resources.add(ResourceFactory.getProxyResource());
         }
+        if (includeFilter == null || 
includeFilter.equals(ResourceType.Actuator)) {
+            resources.add(ResourceFactory.getActuatorResource());
+        }
         if (includeFilter == null || 
includeFilter.equals(ResourceType.Bucket)) {
             resources.add(ResourceFactory.getBucketsResource());
             // add all buckets

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
----------------------------------------------------------------------
diff --git 
a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
 
b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
index ead72d4..8bde97a 100644
--- 
a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
+++ 
b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
@@ -536,14 +536,15 @@ class AuthorizationServiceSpec extends Specification {
 
         then:
         resources != null
-        resources.size() == 6
+        resources.size() == 7
         def sortedResources = resources.sort{it.identifier}
-        sortedResources[0].identifier == "/buckets"
-        sortedResources[1].identifier == "/buckets/b1"
-        sortedResources[2].identifier == "/buckets/b2"
-        sortedResources[3].identifier == "/policies"
-        sortedResources[4].identifier == "/proxy"
-        sortedResources[5].identifier == "/tenants"
+        sortedResources[0].identifier == "/actuator"
+        sortedResources[1].identifier == "/buckets"
+        sortedResources[2].identifier == "/buckets/b1"
+        sortedResources[3].identifier == "/buckets/b2"
+        sortedResources[4].identifier == "/policies"
+        sortedResources[5].identifier == "/proxy"
+        sortedResources[6].identifier == "/tenants"
 
     }
 
@@ -575,6 +576,7 @@ class AuthorizationServiceSpec extends Specification {
         def denied = Mock(Authorizable)
         denied.authorize(_, _, _) >> { throw new AccessDeniedException("") }
 
+        authorizableLookup.getAuthorizableByResource("/actuator")   >> denied
         authorizableLookup.getAuthorizableByResource("/buckets")    >> 
authorized
         authorizableLookup.getAuthorizableByResource("/buckets/b1") >> 
authorized
         authorizableLookup.getAuthorizableByResource("/buckets/b2") >> denied

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/pom.xml b/nifi-registry-web-api/pom.xml
index 797f31e..e991836 100644
--- a/nifi-registry-web-api/pom.xml
+++ b/nifi-registry-web-api/pom.xml
@@ -240,5 +240,23 @@
             <version>3.2.1</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-core</artifactId>
+            <version>1.0-groovy-2.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-all</artifactId>
+            <version>2.4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>2.2.2</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java
index 29c6480..256efb4 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java
@@ -18,9 +18,11 @@ package org.apache.nifi.registry;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import 
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
 import 
org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 
+import java.util.Properties;
+
 /**
  * Main class for starting the NiFi Registry Web API as a Spring Boot 
application.
  *
@@ -31,12 +33,27 @@ import 
org.springframework.boot.web.servlet.support.SpringBootServletInitializer
  *
  * WebMvcAutoConfiguration is excluded because our web app is using Jersey in 
place of SpringMVC
  */
-@SpringBootApplication(exclude = WebMvcAutoConfiguration.class)
+@SpringBootApplication
 public class NiFiRegistryApiApplication extends SpringBootServletInitializer {
 
     public static final String NIFI_REGISTRY_PROPERTIES_ATTRIBUTE = 
"nifi-registry.properties";
     public static final String NIFI_REGISTRY_MASTER_KEY_ATTRIBUTE = 
"nifi-registry.key";
 
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder 
application) {
+        final Properties defaultProperties = new Properties();
+
+        // Enable Actuator Endpoints
+        defaultProperties.setProperty("management.endpoints.web.expose", "*");
+
+        // Run Jersey as a filter instead of a servlet so that requests can be 
forwarded to other handlers (e.g., actuator)
+        defaultProperties.setProperty("spring.jersey.type", "filter");
+
+        return application
+                .sources(NiFiRegistryApiApplication.class)
+                .properties(defaultProperties);
+    }
+
     public static void main(String[] args) {
         SpringApplication.run(NiFiRegistryApiApplication.class, args);
     }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java
index 878ec90..097fc47 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java
@@ -26,6 +26,7 @@ import org.apache.nifi.registry.web.api.TenantResource;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.server.ServerProperties;
 import org.glassfish.jersey.server.filter.HttpMethodOverrideFilter;
+import org.glassfish.jersey.servlet.ServletProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.Configuration;
@@ -67,6 +68,9 @@ public class NiFiRegistryResourceConfig extends 
ResourceConfig {
         // if this value needs to be changed, kerberos authentication needs to 
move to filter chain
         // so it can directly set the HttpServletResponse instead of 
indirectly through a JAX-RS Response
         property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
+
+        // configure jersey to ignore resource paths for actuator
+        property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "/actuator.*");
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
index 9950bb3..ac84b2f 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
@@ -32,7 +32,6 @@ import 
org.apache.nifi.registry.security.authorization.Authorizer;
 import 
org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -69,11 +68,14 @@ public class AccessPolicyResource extends 
AuthorizableApplicationResource {
 
     private static final Logger logger = 
LoggerFactory.getLogger(AccessPolicyResource.class);
 
+    private Authorizer authorizer;
+
     @Autowired
     public AccessPolicyResource(
             Authorizer authorizer,
             AuthorizationService authorizationService) {
-        super(authorizer, authorizationService);
+        super(authorizationService);
+        this.authorizer = authorizer;
     }
 
     /**
@@ -392,10 +394,8 @@ public class AccessPolicyResource extends 
AuthorizableApplicationResource {
     }
 
     private void authorizeAccess(RequestAction actionType) {
-        authorizationService.authorizeAccess(lookup -> {
-            final Authorizable policiesAuthorizable = 
lookup.getPoliciesAuthorizable();
-            policiesAuthorizable.authorize(authorizer, actionType, 
NiFiUserUtils.getNiFiUser());
-        });
+        final Authorizable policiesAuthorizable = 
authorizableLookup.getPoliciesAuthorizable();
+        authorizationService.authorize(policiesAuthorizable, actionType);
     }
 
     private String generateAccessPolicyUri(final AccessPolicySummary 
accessPolicy) {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
index d0be42c..8f983cf 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
@@ -18,11 +18,10 @@ package org.apache.nifi.registry.web.api;
 
 import org.apache.nifi.registry.authorization.Resource;
 import org.apache.nifi.registry.bucket.BucketItem;
-import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
 import org.apache.nifi.registry.security.authorization.resource.ResourceType;
-import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,20 +35,17 @@ public class AuthorizableApplicationResource extends 
ApplicationResource {
     private static final Logger logger = 
LoggerFactory.getLogger(AuthorizableApplicationResource.class);
 
     protected final AuthorizationService authorizationService;
-    protected final Authorizer authorizer;
+    protected final AuthorizableLookup authorizableLookup;
 
     protected AuthorizableApplicationResource(
-            Authorizer authorizer,
             AuthorizationService authorizationService) {
-        this.authorizer = authorizer;
         this.authorizationService = authorizationService;
+        this.authorizableLookup = authorizationService.getAuthorizableLookup();
     }
 
     protected void authorizeBucketAccess(RequestAction actionType, String 
bucketIdentifier) {
-        authorizationService.authorizeAccess(lookup -> {
-            final Authorizable bucketAccessPolicy = 
lookup.getBucketAuthorizable(bucketIdentifier);
-            bucketAccessPolicy.authorize(authorizer, actionType, 
NiFiUserUtils.getNiFiUser());
-        });
+        final Authorizable bucketAuthorizable = 
authorizableLookup.getBucketAuthorizable(bucketIdentifier);
+        authorizationService.authorize(bucketAuthorizable, actionType);
     }
 
     protected void authorizeBucketItemAccess(RequestAction actionType, 
BucketItem bucketItem) {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
index 788f318..a53d6e2 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
@@ -31,7 +31,6 @@ import 
org.apache.nifi.registry.exception.ResourceNotFoundException;
 import org.apache.nifi.registry.flow.VersionedFlow;
 import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
 import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
-import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
 import org.apache.nifi.registry.service.AuthorizationService;
@@ -78,9 +77,8 @@ public class BucketFlowResource extends 
AuthorizableApplicationResource {
             final RegistryService registryService,
             final LinkService linkService,
             final PermissionsService permissionsService,
-            final AuthorizationService authorizationService,
-            final Authorizer authorizer) {
-        super(authorizer, authorizationService);
+            final AuthorizationService authorizationService) {
+        super(authorizationService);
         this.registryService = registryService;
         this.linkService = linkService;
         this.permissionsService =permissionsService;

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
index f94117d..5f75e80 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
@@ -28,11 +28,9 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.registry.bucket.Bucket;
 import org.apache.nifi.registry.bucket.BucketItem;
 import org.apache.nifi.registry.field.Fields;
-import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 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.user.NiFiUserUtils;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.apache.nifi.registry.service.RegistryService;
 import org.apache.nifi.registry.web.link.LinkService;
@@ -85,9 +83,8 @@ public class BucketResource extends 
AuthorizableApplicationResource {
             final RegistryService registryService,
             final LinkService linkService,
             final PermissionsService permissionsService,
-            final AuthorizationService authorizationService,
-            final Authorizer authorizer) {
-        super(authorizer, authorizationService);
+            final AuthorizationService authorizationService) {
+        super(authorizationService);
         this.registryService = registryService;
         this.linkService = linkService;
         this.permissionsService = permissionsService;
@@ -278,10 +275,8 @@ public class BucketResource extends 
AuthorizableApplicationResource {
     }
 
     private void authorizeAccess(RequestAction actionType) throws 
AccessDeniedException {
-        authorizationService.authorizeAccess(lookup -> {
-            final Authorizable bucketsAuthorizable = 
lookup.getBucketsAuthorizable();
-            bucketsAuthorizable.authorize(authorizer, actionType, 
NiFiUserUtils.getNiFiUser());
-        });
+        final Authorizable bucketsAuthorizable = 
authorizableLookup.getBucketsAuthorizable();
+        authorizationService.authorize(bucketsAuthorizable, actionType);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
index 4ea059e..c9deb11 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
@@ -20,8 +20,6 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.Authorization;
 import org.apache.nifi.registry.field.Fields;
-import org.apache.nifi.registry.security.authorization.Authorizer;
-import org.apache.nifi.registry.service.AuthorizationService;
 import org.apache.nifi.registry.service.RegistryService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -41,16 +39,12 @@ import java.util.Set;
         description = "Gets metadata about flows.",
         authorizations = { @Authorization("Authorization") }
 )
-public class FlowResource extends AuthorizableApplicationResource {
+public class FlowResource extends ApplicationResource {
 
     private final RegistryService registryService;
 
     @Autowired
-    public FlowResource(
-            final RegistryService registryService,
-            final AuthorizationService authorizationService,
-            final Authorizer authorizer) {
-        super(authorizer, authorizationService);
+    public FlowResource(final RegistryService registryService) {
         this.registryService = registryService;
     }
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
index 645f34d..e7ae6be 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
@@ -26,7 +26,6 @@ import io.swagger.annotations.Extension;
 import io.swagger.annotations.ExtensionProperty;
 import org.apache.nifi.registry.bucket.BucketItem;
 import org.apache.nifi.registry.field.Fields;
-import org.apache.nifi.registry.security.authorization.Authorizer;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.apache.nifi.registry.service.RegistryService;
@@ -74,9 +73,8 @@ public class ItemResource extends 
AuthorizableApplicationResource {
             final RegistryService registryService,
             final LinkService linkService,
             final PermissionsService permissionsService,
-            final AuthorizationService authorizationService,
-            final Authorizer authorizer) {
-        super(authorizer, authorizationService);
+            final AuthorizationService authorizationService) {
+        super(authorizationService);
         this.registryService = registryService;
         this.linkService = linkService;
         this.permissionsService = permissionsService;

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
index 2fc2e97..d00038d 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
@@ -32,7 +32,6 @@ import 
org.apache.nifi.registry.security.authorization.Authorizer;
 import 
org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
 import org.apache.nifi.registry.security.authorization.RequestAction;
 import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
 import org.apache.nifi.registry.service.AuthorizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -68,11 +67,12 @@ public class TenantResource extends 
AuthorizableApplicationResource {
 
     private static final Logger logger = 
LoggerFactory.getLogger(TenantResource.class);
 
+    private Authorizer authorizer;
+
     @Autowired
-    public TenantResource(
-            Authorizer authorizer,
-            AuthorizationService authorizationService) {
-        super(authorizer, authorizationService);
+    public TenantResource(AuthorizationService authorizationService) {
+        super(authorizationService);
+        authorizer = authorizationService.getAuthorizer();
     }
 
 
@@ -563,10 +563,8 @@ public class TenantResource extends 
AuthorizableApplicationResource {
     }
 
     private void authorizeAccess(RequestAction actionType) {
-        authorizationService.authorizeAccess(lookup -> {
-            final Authorizable tenantsAuthorizable = 
lookup.getTenantsAuthorizable();
-            tenantsAuthorizable.authorize(authorizer, actionType, 
NiFiUserUtils.getNiFiUser());
-        });
+        final Authorizable tenantsAuthorizable = 
authorizableLookup.getTenantsAuthorizable();
+        authorizationService.authorize(tenantsAuthorizable, actionType);
     }
 
     private String generateUserUri(final User user) {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
index 9a5d18b..1d484c1 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
@@ -17,7 +17,8 @@
 package org.apache.nifi.registry.web.security;
 
 import org.apache.nifi.registry.properties.NiFiRegistryProperties;
-import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.authorization.resource.ResourceType;
+import org.apache.nifi.registry.service.AuthorizationService;
 import 
org.apache.nifi.registry.web.security.authentication.AnonymousIdentityFilter;
 import 
org.apache.nifi.registry.web.security.authentication.IdentityAuthenticationProvider;
 import org.apache.nifi.registry.web.security.authentication.IdentityFilter;
@@ -25,6 +26,7 @@ import 
org.apache.nifi.registry.web.security.authentication.exception.UntrustedP
 import 
org.apache.nifi.registry.web.security.authentication.jwt.JwtIdentityProvider;
 import 
org.apache.nifi.registry.web.security.authentication.x509.X509IdentityAuthenticationProvider;
 import 
org.apache.nifi.registry.web.security.authentication.x509.X509IdentityProvider;
+import 
org.apache.nifi.registry.web.security.authorization.ResourceAuthorizationFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -38,6 +40,7 @@ import 
org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.AuthenticationEntryPoint;
+import 
org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
 
 import javax.servlet.ServletException;
@@ -59,7 +62,7 @@ public class NiFiRegistrySecurityConfig extends 
WebSecurityConfigurerAdapter {
     private NiFiRegistryProperties properties;
 
     @Autowired
-    private Authorizer authorizer;
+    private AuthorizationService authorizationService;
 
     private AnonymousIdentityFilter anonymousAuthenticationFilter = new 
AnonymousIdentityFilter();
 
@@ -73,6 +76,8 @@ public class NiFiRegistrySecurityConfig extends 
WebSecurityConfigurerAdapter {
     private IdentityFilter jwtAuthenticationFilter;
     private IdentityAuthenticationProvider jwtAuthenticationProvider;
 
+    private ResourceAuthorizationFilter resourceAuthorizationFilter;
+
     public NiFiRegistrySecurityConfig() {
         super(true); // disable defaults
     }
@@ -112,6 +117,12 @@ public class NiFiRegistrySecurityConfig extends 
WebSecurityConfigurerAdapter {
             // is detected earlier in the Spring filter chain.
             
http.anonymous().authenticationFilter(anonymousAuthenticationFilter);
         }
+
+        // After Spring Security filter chain is complete (so authentication 
is done),
+        // but before the Jersey application endpoints get the request,
+        // insert the ResourceAuthorizationFilter to do its authorization 
checks
+        http.addFilterAfter(resourceAuthorizationFilter(), 
FilterSecurityInterceptor.class);
+
     }
 
     @Override
@@ -130,7 +141,7 @@ public class NiFiRegistrySecurityConfig extends 
WebSecurityConfigurerAdapter {
 
     private IdentityAuthenticationProvider x509AuthenticationProvider() {
         if (x509AuthenticationProvider == null) {
-            x509AuthenticationProvider = new 
X509IdentityAuthenticationProvider(properties, authorizer, 
x509IdentityProvider);
+            x509AuthenticationProvider = new 
X509IdentityAuthenticationProvider(properties, 
authorizationService.getAuthorizer(), x509IdentityProvider);
         }
         return x509AuthenticationProvider;
     }
@@ -144,11 +155,21 @@ public class NiFiRegistrySecurityConfig extends 
WebSecurityConfigurerAdapter {
 
     private IdentityAuthenticationProvider jwtAuthenticationProvider() {
         if (jwtAuthenticationProvider == null) {
-            jwtAuthenticationProvider = new 
IdentityAuthenticationProvider(properties, authorizer, jwtIdentityProvider);
+            jwtAuthenticationProvider = new 
IdentityAuthenticationProvider(properties, 
authorizationService.getAuthorizer(), jwtIdentityProvider);
         }
         return jwtAuthenticationProvider;
     }
 
+    private ResourceAuthorizationFilter resourceAuthorizationFilter() {
+        if (resourceAuthorizationFilter == null) {
+            resourceAuthorizationFilter = ResourceAuthorizationFilter.builder()
+                    .setAuthorizationService(authorizationService)
+                    .addResourceType(ResourceType.Actuator)
+                    .build();
+        }
+        return resourceAuthorizationFilter;
+    }
+
     private AuthenticationEntryPoint http401AuthenticationEntryPoint() {
         // This gets used for both secured and unsecured configurations. It 
will be called by Spring Security if a request makes it through the filter 
chain without being authenticated.
         // For unsecured, this should never be reached because the custom 
AnonymousAuthenticationFilter should always populate a fully-authenticated 
anonymous user

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationFilter.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationFilter.java
deleted file mode 100644
index 8367eec..0000000
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationFilter.java
+++ /dev/null
@@ -1,148 +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.web.security.authentication;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.registry.security.authentication.AuthenticationRequest;
-import org.apache.nifi.registry.security.authentication.IdentityProvider;
-import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.security.util.ProxiedEntitiesUtils;
-import 
org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException;
-import 
org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException;
-import org.springframework.security.authentication.AuthenticationManager;
-import 
org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.context.SecurityContextHolder;
-import 
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.security.web.util.matcher.RequestMatcher;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-/**
- * Note: This class is deprecated and is being considered for complete removal 
in favor of using {@link IdentityFilter}.
- *       It is remaining in place for the time being until the pattern of 
authentication implemented by {@link IdentityFilter}
- *       has been more thoroughly vetted in real use.
- */
-@Deprecated
-public class IdentityAuthenticationFilter extends 
AbstractAuthenticationProcessingFilter {
-
-    private static final RequestMatcher requiresAuthenticationRequestMatcher = 
new RequestMatcher() {
-        @Override
-        public boolean matches(HttpServletRequest httpServletRequest) {
-            return NiFiUserUtils.getNiFiUser() == null;
-        }
-    };
-
-    private final IdentityProvider identityProvider;
-
-    public IdentityAuthenticationFilter(IdentityProvider identityProvider, 
AuthenticationManager authenticationManager, String defaultFilterProcessesUrl) {
-        super(defaultFilterProcessesUrl);
-        super.setRequiresAuthenticationRequestMatcher(new 
AntPathRequestMatcher(defaultFilterProcessesUrl)); // Authentication will only 
be initiated for the request url matching this pattern
-        setAuthenticationManager(authenticationManager);
-        this.identityProvider = identityProvider;
-    }
-
-    public IdentityAuthenticationFilter(IdentityProvider identityProvider, 
AuthenticationManager authenticationManager) {
-        super(requiresAuthenticationRequestMatcher);
-        setAuthenticationManager(authenticationManager);
-        this.identityProvider = identityProvider;
-    }
-
-    @Override
-    public Authentication attemptAuthentication(HttpServletRequest 
httpServletRequest, HttpServletResponse httpServletResponse) throws 
AuthenticationException, IOException, ServletException {
-
-        // Only require authentication from an identity provider if the NiFi 
registry is running securely.
-        if (!httpServletRequest.isSecure()) {
-            throw new InvalidAuthenticationException("Authentication of user 
identity claim is only avaialble when running a securely.");
-        }
-
-        AuthenticationRequest authenticationRequest = 
identityProvider.extractCredentials(httpServletRequest);
-        if (authenticationRequest == null) {
-            throw new InvalidAuthenticationException("User credentials not 
found in httpServletRequest by " + identityProvider.getClass().getSimpleName());
-        }
-        Authentication authentication = new 
AuthenticationRequestToken(authenticationRequest, identityProvider.getClass(), 
httpServletRequest.getRemoteAddr());
-        Authentication authenticationResult = 
getAuthenticationManager().authenticate(authentication); // See 
IdentityAuthenticationProvider for authentication impl.
-        if (authenticationResult == null) {
-            throw new InvalidAuthenticationException("User credentials not 
authenticated by " + identityProvider.getClass().getSimpleName());
-        }
-
-        return authenticationResult;
-        // Super class will invoke successfulAuthentication() or 
unsuccessfulAuthentication() depending on the outcome of the authentication 
attempt
-    }
-
-    @Override
-    protected void successfulAuthentication(HttpServletRequest request, 
HttpServletResponse response, FilterChain chain, Authentication authResult) 
throws IOException, ServletException {
-
-        logger.info("Authentication success for " + authResult);
-
-        SecurityContextHolder.getContext().setAuthentication(authResult);
-        if 
(StringUtils.isNotBlank(request.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN)))
 {
-            response.setHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_ACCEPTED, 
Boolean.TRUE.toString());
-        }
-
-        // continue the filter chain, which now holds a NiFiUser in the 
SecurityContext's authentication
-        chain.doFilter(request, response);
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(HttpServletRequest request, 
HttpServletResponse response, AuthenticationException failed) throws 
IOException, ServletException {
-        this.logger.debug("Authentication request failed: " + 
failed.toString(), failed);
-
-        SecurityContextHolder.clearContext();
-        this.logger.debug("Updated SecurityContextHolder to contain null 
Authentication");
-
-        // populate the response
-        if 
(StringUtils.isNotBlank(request.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN)))
 {
-            response.setHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_DETAILS, 
failed.getMessage());
-        }
-
-        // set the response status
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-
-        // use the type of authentication exception to determine the response 
code
-        if (failed instanceof InvalidAuthenticationException) {
-            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-            out.println(failed.getMessage());
-        } else if (failed instanceof UntrustedProxyException) { // thrown in 
X509IdentityProviderAuthenticationProvider
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(failed.getMessage());
-        } else if (failed instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", 
failed.getMessage()), failed);
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            out.println(String.format("Unable to authorize: %s", 
failed.getMessage()));
-        } else {
-            logger.error(String.format("Unable to authorize: %s", 
failed.getMessage()), failed);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println("Access is denied.");
-        }
-
-        // log the failure
-        logger.warn(String.format("Rejecting access to web api: %s", 
failed.getMessage()));
-        logger.debug(StringUtils.EMPTY, failed);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
index 40c2662..cd5e2bf 100644
--- 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
@@ -64,8 +64,8 @@ public class IdentityFilter extends GenericFilterBean {
         }
 
         if (credentialsAlreadyPresent()) {
-            logger.debug("Credentials already extracted for {}, skipping 
credentials extraction filter for {}",
-                    
SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
+            logger.debug("Credentials already extracted for [{}], skipping 
credentials extraction filter using {}",
+                    
SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString(),
                     identityProvider.getClass().getSimpleName());
             filterChain.doFilter(servletRequest, servletResponse);
             return;

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/HttpMethodAuthorizationRules.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/HttpMethodAuthorizationRules.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/HttpMethodAuthorizationRules.java
new file mode 100644
index 0000000..c940359
--- /dev/null
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/HttpMethodAuthorizationRules.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.security.authorization;
+
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.springframework.http.HttpMethod;
+
+public interface HttpMethodAuthorizationRules {
+
+    default boolean requiresAuthorization(HttpMethod httpMethod) {
+        return true;
+    }
+
+    default RequestAction mapHttpMethodToAction(HttpMethod httpMethod) {
+
+        switch (httpMethod) {
+            case TRACE:
+            case OPTIONS:
+            case HEAD:
+            case GET:
+                return RequestAction.READ;
+            case POST:
+            case PUT:
+            case PATCH:
+                return RequestAction.WRITE;
+            case DELETE:
+                return RequestAction.DELETE;
+            default:
+                throw new IllegalArgumentException("Unknown http method: " + 
httpMethod);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/ResourceAuthorizationFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/ResourceAuthorizationFilter.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/ResourceAuthorizationFilter.java
new file mode 100644
index 0000000..6e551e1
--- /dev/null
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/ResourceAuthorizationFilter.java
@@ -0,0 +1,218 @@
+/*
+ * 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.security.authorization;
+
+import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+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.ResourceType;
+import org.apache.nifi.registry.security.authorization.user.NiFiUser;
+import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
+import org.apache.nifi.registry.service.AuthorizationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpMethod;
+import org.springframework.web.filter.GenericFilterBean;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This filter is designed to perform a resource authorization check in the 
Spring Security filter chain.
+ *
+ * It authorizes the current authenticated user for the {@link RequestAction} 
(based on the HttpMethod) requested
+ * on the {@link ResourceType} (based on the URI path).
+ *
+ * This filter is designed to be place after any authentication and before any 
application endpoints.
+ *
+ * This filter can be used in place of or in addition to authorization checks 
that occur in the application
+ * downstream of this filter.
+ *
+ * To configure this filter, provide an {@link AuthorizationService} that will 
be used to perform the authorization
+ * check, as well as a set of rules that control which resource and HTTP 
methods are handled by this filter.
+ *
+ * Any (ResourceType, HttpMethod) pair that is not configured to require 
authorization by this filter will be
+ * allowed to proceed in the filter chain without an authorization check.
+ *
+ * Any (ResourceType, HttpMethod) pair that is configured to require 
authorization by this filter will map
+ * the HttpMethod to a NiFi Registry RequestAction (configurable when creating 
this filter), and the
+ * (Resource Authorizable, RequestAction) pair will be sent to the 
AuthorizationService, which will use the
+ * configured Authorizer to authorize the current user for the action on the 
requested resource.
+ */
+public class ResourceAuthorizationFilter extends GenericFilterBean {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(ResourceAuthorizationFilter.class);
+
+    private Map<ResourceType, HttpMethodAuthorizationRules> 
resourceTypeAuthorizationRules;
+    private AuthorizationService authorizationService;
+    private AuthorizableLookup authorizableLookup;
+
+    ResourceAuthorizationFilter(Builder builder) {
+        if (builder.getAuthorizationService() == null || 
builder.getResourceTypeAuthorizationRules() == null) {
+            throw new IllegalArgumentException("Builder is missing one or more 
required fields [authorizationService, resourceTypeAuthorizationRules].");
+        }
+        this.resourceTypeAuthorizationRules = 
builder.getResourceTypeAuthorizationRules();
+        this.authorizationService = builder.getAuthorizationService();
+        this.authorizableLookup = 
this.authorizationService.getAuthorizableLookup();
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse 
servletResponse, FilterChain filterChain) throws IOException, ServletException {
+
+        HttpServletRequest httpServletRequest = (HttpServletRequest) 
servletRequest;
+        HttpServletResponse httpServletResponse = (HttpServletResponse) 
servletResponse;
+
+        boolean authorizationCheckIsRequired = false;
+        String resourcePath = null;
+        RequestAction action = null;
+
+        // Only require authorization if the NiFi Registry is running securely.
+        if (servletRequest.isSecure()) {
+
+            // Only require authorization for resources for which this filter 
has been configured
+            resourcePath = httpServletRequest.getServletPath();
+            if (resourcePath != null) {
+                final ResourceType resourceType = 
ResourceType.mapFullResourcePathToResourceType(resourcePath);
+                final HttpMethodAuthorizationRules authorizationRules = 
resourceTypeAuthorizationRules.get(resourceType);
+                if (authorizationRules != null) {
+                    final String httpMethodStr = 
httpServletRequest.getMethod().toUpperCase();
+                    HttpMethod httpMethod = HttpMethod.resolve(httpMethodStr);
+
+                    // Only require authorization for HTTP methods included in 
this resource type's rule set
+                    if (httpMethod != null && 
authorizationRules.requiresAuthorization(httpMethod)) {
+                        authorizationCheckIsRequired = true;
+                        action = 
authorizationRules.mapHttpMethodToAction(httpMethod);
+                    }
+                }
+            }
+        }
+
+        if (!authorizationCheckIsRequired) {
+            forwardRequestWithoutAuthorizationCheck(httpServletRequest, 
httpServletResponse, filterChain);
+            return;
+        }
+
+        // Perform authorization check
+        try {
+            authorizeAccess(resourcePath, action);
+            successfulAuthorization(httpServletRequest, httpServletResponse, 
filterChain);
+        } catch (Exception e) {
+            logger.debug("Exception occurred while performing authorization 
check.", e);
+            failedAuthorization(httpServletRequest, httpServletResponse, 
filterChain, e);
+        }
+    }
+
+    private boolean userIsAuthenticated() {
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+        return (user != null && !user.isAnonymous());
+    }
+
+    private void authorizeAccess(String path, RequestAction action) throws 
AccessDeniedException {
+
+        if (path == null || action == null) {
+            throw new IllegalArgumentException("Authorization is required, but 
a required input [resource, action] is absent.");
+        }
+
+        Authorizable authorizable = 
authorizableLookup.getAuthorizableByResource(path);
+
+        if (authorizable == null) {
+            throw new IllegalStateException("Resource Authorization Filter 
configured for non-authorizable resource: " + path);
+        }
+
+        // throws AccessDeniedException if current user is not authorized to 
perform requested action on resource
+        authorizationService.authorize(authorizable, action);
+    }
+
+    private void forwardRequestWithoutAuthorizationCheck(HttpServletRequest 
req, HttpServletResponse res, FilterChain chain) throws IOException, 
ServletException {
+        logger.debug("Request filter authorization check is not required for 
this HTTP Method on this resource. " +
+                "Allowing request to proceed. An additional authorization 
check might be performed downstream of this filter.");
+        chain.doFilter(req, res);
+    }
+
+    private void successfulAuthorization(HttpServletRequest req, 
HttpServletResponse res, FilterChain chain) throws IOException, 
ServletException {
+        logger.debug("Request filter authorization check passed. Allowing 
request to proceed.");
+        chain.doFilter(req, res);
+    }
+
+    private void failedAuthorization(HttpServletRequest request, 
HttpServletResponse response, FilterChain chain, Exception failure) throws 
IOException, ServletException {
+        logger.debug("Request filter authorization check failed. Blocking 
access.");
+
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+        final String identity = (user != null) ? user.toString() : "<no user 
found>";
+        final int status = !userIsAuthenticated() ? 
HttpServletResponse.SC_UNAUTHORIZED : HttpServletResponse.SC_FORBIDDEN;
+
+        logger.info("{} does not have permission to perform this action on the 
requested resource. {} Returning {} response.", identity, failure.getMessage(), 
status);
+        logger.debug("", failure);
+
+        if (!response.isCommitted()) {
+            response.setStatus(status);
+            response.setContentType("text/plain");
+            response.getWriter().println(String.format("Access is denied due 
to: %s Contact the system administrator.", failure.getLocalizedMessage()));
+        }
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+
+        private AuthorizationService authorizationService;
+        final private Map<ResourceType, HttpMethodAuthorizationRules> 
resourceTypeAuthorizationRules;
+
+        // create via ResourceAuthorizationFilter.builder()
+        private Builder() {
+            this.resourceTypeAuthorizationRules = new HashMap<>();
+        }
+
+        public AuthorizationService getAuthorizationService() {
+            return authorizationService;
+        }
+
+        public Builder setAuthorizationService(AuthorizationService 
authorizationService) {
+            this.authorizationService = authorizationService;
+            return this;
+        }
+
+        public Map<ResourceType, HttpMethodAuthorizationRules> 
getResourceTypeAuthorizationRules() {
+            return resourceTypeAuthorizationRules;
+        }
+
+        public Builder addResourceType(ResourceType resourceType) {
+            this.resourceTypeAuthorizationRules.put(resourceType, new 
HttpMethodAuthorizationRules() {});
+            return this;
+        }
+
+        public Builder addResourceType(ResourceType resourceType, 
HttpMethodAuthorizationRules authorizationRules) {
+            this.resourceTypeAuthorizationRules.put(resourceType, 
authorizationRules);
+            return this;
+        }
+
+        public ResourceAuthorizationFilter build() {
+            return new ResourceAuthorizationFilter(this);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/StandardHttpMethodAuthorizationRules.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/StandardHttpMethodAuthorizationRules.java
 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/StandardHttpMethodAuthorizationRules.java
new file mode 100644
index 0000000..daa5a37
--- /dev/null
+++ 
b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authorization/StandardHttpMethodAuthorizationRules.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.web.security.authorization;
+
+import org.springframework.http.HttpMethod;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+public class StandardHttpMethodAuthorizationRules implements 
HttpMethodAuthorizationRules {
+
+    final private Set<HttpMethod> methodsRequiringAuthorization;
+
+    public StandardHttpMethodAuthorizationRules() {
+        this(EnumSet.allOf(HttpMethod.class));
+    }
+
+    public StandardHttpMethodAuthorizationRules(Set<HttpMethod> 
methodsRequiringAuthorization) {
+        this.methodsRequiringAuthorization = methodsRequiringAuthorization;
+    }
+
+    @Override
+    public boolean requiresAuthorization(HttpMethod httpMethod) {
+        return methodsRequiringAuthorization.contains(httpMethod);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
 
b/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
new file mode 100644
index 0000000..e27dfbe
--- /dev/null
+++ 
b/nifi-registry-web-api/src/test/groovy/org/apache/nifi/registry/security/authorization/ResourceAuthorizationFilterSpec.groovy
@@ -0,0 +1,170 @@
+/*
+ * 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.AccessDeniedException
+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.web.security.authorization.HttpMethodAuthorizationRules
+import 
org.apache.nifi.registry.web.security.authorization.ResourceAuthorizationFilter
+import 
org.apache.nifi.registry.web.security.authorization.StandardHttpMethodAuthorizationRules
+import org.springframework.http.HttpMethod
+import org.springframework.mock.web.MockHttpServletRequest
+import org.springframework.mock.web.MockHttpServletResponse
+import spock.lang.Specification
+
+import javax.servlet.FilterChain
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+class ResourceAuthorizationFilterSpec extends Specification {
+
+    AuthorizableLookup authorizableLookup = new StandardAuthorizableLookup()
+    AuthorizationService mockAuthorizationService = Mock(AuthorizationService)
+    FilterChain mockFilterChain = Mock(FilterChain)
+    ResourceAuthorizationFilter.Builder resourceAuthorizationFilterBuilder
+
+    // runs before every feature method
+    def setup() {
+        mockAuthorizationService.getAuthorizableLookup() >> authorizableLookup
+        resourceAuthorizationFilterBuilder = 
ResourceAuthorizationFilter.builder().setAuthorizationService(mockAuthorizationService)
+    }
+
+    // runs after every feature method
+    def cleanup() {
+        //mockAuthorizationService = null
+        //mockFilterChain = null
+        resourceAuthorizationFilterBuilder = null
+    }
+
+    // runs before the first feature method
+    def setupSpec() {}
+
+    // runs after the last feature method
+    def cleanupSpec() {}
+
+
+    def "unsecured requests are allowed without an authorization check"() {
+
+        setup:
+        def resourceAuthorizationFilter = 
resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator).build()
+        def httpServletRequest = createUnsecuredRequest()
+        def httpServletResponse = createResponse()
+
+        when: "doFilter() is called"
+        resourceAuthorizationFilter.doFilter(httpServletRequest, 
httpServletResponse, mockFilterChain)
+
+        then: "response is forwarded without authorization check"
+        0 * mockAuthorizationService._
+        1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as 
HttpServletResponse)
+
+    }
+
+
+    def "secure requests to an unguarded resource are allowed without an 
authorization check"() {
+
+        setup:
+        def resourceAuthorizationFilter = 
resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator).build()
+        def httpServletRequest = createSecureRequest(HttpMethod.POST, 
ResourceType.Bucket)
+        def httpServletResponse = createResponse()
+
+        when: "doFilter() is called"
+        resourceAuthorizationFilter.doFilter(httpServletRequest, 
httpServletResponse, mockFilterChain)
+
+        then: "response is forwarded without authorization check"
+        0 * mockAuthorizationService._
+        1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as 
HttpServletResponse)
+
+    }
+
+
+    def "secure requests to an unguarded HTTP method are allowed without an 
authorization check"() {
+
+        setup:
+        HttpMethodAuthorizationRules rules = new 
StandardHttpMethodAuthorizationRules(EnumSet.of(HttpMethod.POST, 
HttpMethod.PUT, HttpMethod.DELETE))
+        def resourceAuthorizationFilter = 
resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator, 
rules).build()
+        def httpServletRequest = createSecureRequest(HttpMethod.GET, 
ResourceType.Actuator)
+        def httpServletResponse = createResponse()
+
+        when: "doFilter() is called"
+        resourceAuthorizationFilter.doFilter(httpServletRequest, 
httpServletResponse, mockFilterChain)
+
+        then: "response is forwarded without authorization check"
+        0 * mockAuthorizationService._
+        1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as 
HttpServletResponse)
+
+    }
+
+
+    def "secure requests matching resource configuration rules perform 
authorization check"() {
+
+        setup:
+        // Stubbing setup for mockAuthorizationService is done in the then 
block as we are also verifying interactions with mock
+        def resourceAuthorizationFilter = 
resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator).build()
+        def authorizedRequest = createSecureRequest(HttpMethod.GET, 
ResourceType.Actuator)
+        def unauthorizedRequest = createSecureRequest(HttpMethod.POST, 
ResourceType.Actuator)
+        def httpServletResponse = createResponse()
+
+
+        when: "doFilter() is called with an authorized request"
+        resourceAuthorizationFilter.doFilter(authorizedRequest, 
httpServletResponse, mockFilterChain)
+
+        then: "response is forwarded after authorization check"
+        1 * mockAuthorizationService.authorize(_ as Authorizable, 
RequestAction.READ) >> { allowAccess() }
+        1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as 
HttpServletResponse)
+
+
+        when: "doFilter() is called with an unauthorized request"
+        resourceAuthorizationFilter.doFilter(unauthorizedRequest, 
httpServletResponse, mockFilterChain)
+
+        then: "authorization check is performed and response is not forwarded"
+        1 * mockAuthorizationService.authorize(_ as Authorizable, 
RequestAction.WRITE) >> { denyAccess() }
+        0 * mockFilterChain.doFilter(*_)
+
+    }
+
+    static private HttpServletRequest createUnsecuredRequest() {
+        HttpServletRequest req = new MockHttpServletRequest()
+        req.setScheme("http")
+        req.setSecure(false)
+        return req
+    }
+
+    static private HttpServletRequest createSecureRequest(HttpMethod 
httpMethod, ResourceType resourceType) {
+        HttpServletRequest req = new MockHttpServletRequest()
+        req.setMethod(httpMethod.name())
+        req.setScheme("https")
+        req.setServletPath(resourceType.getValue())
+        req.setSecure(true)
+        return req
+    }
+
+    static private HttpServletResponse createResponse() {
+        HttpServletResponse res = new MockHttpServletResponse()
+        return res
+    }
+
+    static private void allowAccess() {
+        // Do nothing (no thrown exception indicates access is allowed
+    }
+
+    static private void denyAccess() {
+        throw new AccessDeniedException("This is an expected 
AccessDeniedException.")
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/1755d840/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
 
b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
index 0ffdb0d..29cd215 100644
--- 
a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
+++ 
b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
@@ -85,6 +85,7 @@ public class SecureFileIT extends IntegrationTestBase {
 
         // Given: an empty registry returns these resources
         String expected = "[" +
+                "{\"identifier\":\"/actuator\",\"name\":\"Actuator\"}," +
                 "{\"identifier\":\"/policies\",\"name\":\"Access Policies\"}," 
+
                 "{\"identifier\":\"/tenants\",\"name\":\"Tenants\"}," +
                 "{\"identifier\":\"/proxy\",\"name\":\"Proxy User 
Requests\"}," +

Reply via email to