NIFI-655:
- Refactoring web security to use Spring Security Java Configuration.
- Introducing security in Web UI in order to get JWT.


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

Branch: refs/heads/NIFI-655
Commit: 6104670735325492fd58ca12d123a061818d62cd
Parents: 31fba6b
Author: Matt Gilman <[email protected]>
Authored: Wed Oct 7 13:33:34 2015 -0400
Committer: Matt Gilman <[email protected]>
Committed: Wed Oct 7 13:46:11 2015 -0400

----------------------------------------------------------------------
 .../resources/nifi-administration-context.xml   |   2 +-
 .../org/apache/nifi/web/server/JettyServer.java |   5 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |   6 +
 .../nifi/web/NiFiWebApiConfiguration.java       |  24 +
 .../web/NiFiWebApiSecurityConfiguration.java    | 127 +++++
 .../nifi/web/StandardNiFiServiceFacade.java     |  22 +
 .../nifi/web/api/ApplicationResource.java       |  10 +-
 .../org/apache/nifi/web/api/UserResource.java   |  33 ++
 .../nifi/web/controller/ControllerFacade.java   |  13 +-
 .../src/main/webapp/WEB-INF/web.xml             |  13 +-
 .../nifi/integration/util/NiFiTestUser.java     |  21 +-
 .../org/apache/nifi/web/security/DnUtils.java   |  85 ----
 .../security/NiFiAuthenticationEntryPoint.java  |  69 +++
 .../web/security/NiFiAuthenticationFilter.java  | 144 ++++++
 .../security/NiFiAuthenticationProvider.java    |  62 +++
 .../nifi/web/security/ProxiedEntitiesUtils.java | 168 +++++++
 .../anonymous/NiFiAnonymousUserFilter.java      |  55 +-
 .../NiFiAuthenticationEntryPoint.java           |  69 ---
 .../authorization/NiFiAuthorizationService.java |  33 +-
 .../authorization/NodeAuthorizedUserFilter.java | 128 -----
 .../security/form/FormAuthenticationFilter.java |  72 +++
 .../security/jwt/JwtAuthenticationFilter.java   |  58 +++
 .../security/jwt/JwtAuthenticationProvider.java |  47 ++
 .../nifi/web/security/jwt/JwtService.java       |  51 ++
 .../security/node/NodeAuthorizedUserFilter.java | 127 +++++
 .../NewAccountAuthenticationRequestToken.java   |  41 ++
 .../token/NewAccountAuthenticationToken.java    |  48 ++
 .../token/NiFiAuthenticationRequestToken.java   |  55 ++
 .../security/token/NiFiAuthorizationToken.java  |  51 ++
 .../web/security/user/NewAccountRequest.java    |  31 ++
 .../nifi/web/security/user/NiFiUserDetails.java |   3 +-
 .../nifi/web/security/user/NiFiUserUtils.java   |  21 +
 .../security/x509/X509AuthenticationFilter.java | 257 +---------
 .../x509/X509AuthenticationFilterOld.java       | 317 ++++++++++++
 .../x509/X509AuthenticationProvider.java        |  31 ++
 .../resources/nifi-web-security-context.xml     |  62 +--
 .../NiFiAuthorizationServiceTest.java           | 502 +++++++++----------
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |  22 +-
 .../apache/nifi/web/NiFiWebUiConfiguration.java |  35 ++
 .../web/NiFiWebUiSecurityConfiguration.java     |  69 +++
 .../src/main/webapp/WEB-INF/pages/login.jsp     |  66 +++
 .../WEB-INF/partials/canvas/canvas-header.jsp   |   3 +
 .../nifi-web-ui/src/main/webapp/WEB-INF/web.xml |  44 +-
 pom.xml                                         |   2 +-
 44 files changed, 2204 insertions(+), 900 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
index a36619f..8cb4b97 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml
@@ -23,7 +23,7 @@
     http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd";>
 
     <!-- user authority provider -->
-    <bean id="authorityProvider" 
class="org.apache.nifi.authorization.AuthorityProviderFactoryBean" 
depends-on="clusterManager flowController">
+    <bean id="authorityProvider" 
class="org.apache.nifi.authorization.AuthorityProviderFactoryBean" 
depends-on="clusterManager">
         <property name="properties" ref="nifiProperties"/>
     </bean>
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 99c11a8..b2b3013 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -614,8 +614,9 @@ public class JettyServer implements NiFiServer {
     private SslContextFactory createSslContextFactory() {
         final SslContextFactory contextFactory = new SslContextFactory();
 
-        // need client auth
-        contextFactory.setNeedClientAuth(props.getNeedClientAuth());
+        // client auth
+        contextFactory.setWantClientAuth(true);
+        contextFactory.setNeedClientAuth(false);
 
         /* below code sets JSSE system properties when values are provided */
         // keystore properties

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index c98b1e4..7395cfc 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -1236,6 +1236,12 @@ public interface NiFiServiceFacade {
     Collection<UserDTO> getUsers(Boolean grouped);
 
     /**
+     * Creates a new account request.
+     * @return user
+     */
+    UserDTO createUser();
+    
+    /**
      * Updates the specified user accordingly.
      *
      * @param user The user to update

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
new file mode 100644
index 0000000..04264e7
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
@@ -0,0 +1,24 @@
+package org.apache.nifi.web;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.ImportResource;
+
+/**
+ *
+ */
+@Configuration
+@Import({ NiFiWebApiSecurityConfiguration.class})
+@ImportResource( {"classpath:nifi-context.xml",
+    "classpath:nifi-administration-context.xml",
+    "classpath:nifi-cluster-manager-context.xml",
+    "classpath:nifi-cluster-protocol-context.xml",
+    "classpath:nifi-web-security-context.xml",
+    "classpath:nifi-web-api-context.xml"} )
+public class NiFiWebApiConfiguration {
+
+    public NiFiWebApiConfiguration() {
+        super();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
new file mode 100644
index 0000000..305aaf6
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
@@ -0,0 +1,127 @@
+/*
+ * 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.web;
+
+import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.NiFiAuthenticationProvider;
+import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
+import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
+import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
+import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
+import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
+import org.apache.nifi.web.security.x509.X509AuthenticationProvider;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+import 
org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import 
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import 
org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import 
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import 
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import 
org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+/**
+ * NiFi Web Api Spring security
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class NiFiWebApiSecurityConfiguration extends 
WebSecurityConfigurerAdapter {
+
+    private NiFiProperties properties;
+    private UserService userService;
+    private AuthenticationUserDetailsService userDetailsService;
+
+    public NiFiWebApiSecurityConfiguration() {
+        super(true); // disable defaults
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .rememberMe().disable()
+                .exceptionHandling()
+                    .authenticationEntryPoint(new 
NiFiAuthenticationEntryPoint())
+                    .and()
+                .authorizeRequests()
+                    .anyRequest().fullyAuthenticated()
+                    .and()
+                .sessionManagement()
+                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+
+        // cluster - authorized user
+        final NodeAuthorizedUserFilter authorizedUserFilter = new 
NodeAuthorizedUserFilter(properties);
+        http.addFilterBefore(authorizedUserFilter, 
AnonymousAuthenticationFilter.class);
+
+        // x509
+        http.addFilterBefore(buildX509Filter(), 
AnonymousAuthenticationFilter.class);
+
+        // anonymous
+        final NiFiAnonymousUserFilter anonymousFilter = new 
NiFiAnonymousUserFilter();
+        anonymousFilter.setProperties(properties);
+        anonymousFilter.setUserService(userService);
+        http.anonymous().authenticationFilter(anonymousFilter);
+    }
+
+    @Bean 
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        // override xxxBean method so the authentication manager is available 
in app context (necessary for the method level security)
+        return super.authenticationManagerBean();
+    }
+    
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws 
Exception {
+        // x509
+        final AuthenticationProvider x509AuthenticationProvider = new 
NiFiAuthenticationProvider(new X509AuthenticationProvider(), 
userDetailsService);
+        
+        auth
+                .authenticationProvider(x509AuthenticationProvider);
+    }
+    
+    private X509AuthenticationFilter buildX509Filter() throws Exception {
+        final X509AuthenticationFilter x509Filter = new 
X509AuthenticationFilter();
+        x509Filter.setPrincipalExtractor(new 
SubjectDnX509PrincipalExtractor());
+        x509Filter.setCertificateExtractor(new X509CertificateExtractor());
+        x509Filter.setCertificateValidator(new 
OcspCertificateValidator(properties));
+        x509Filter.setAuthenticationManager(authenticationManager());
+        return x509Filter;
+    }
+    
+    @Autowired
+    public void setUserDetailsService(AuthenticationUserDetailsService 
userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+    @Autowired
+    public void setUserService(UserService userService) {
+        this.userService = userService;
+    }
+    
+    @Autowired
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 2286213..e47b339 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.web;
 
+import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -32,6 +33,8 @@ import java.util.Set;
 import java.util.TimeZone;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import javax.ws.rs.WebApplicationException;
 
@@ -152,6 +155,7 @@ import org.apache.nifi.web.util.SnippetUtils;
 
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.controller.ReportingTaskNode;
@@ -168,6 +172,7 @@ import 
org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.NodeProcessGroupStatusDTO;
 import org.apache.nifi.web.dao.ControllerServiceDAO;
 import org.apache.nifi.web.dao.ReportingTaskDAO;
+import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.access.AccessDeniedException;
@@ -1808,6 +1813,23 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
     }
 
     @Override
+    public UserDTO createUser() {
+        NewAccountRequest newAccountRequest = 
NiFiUserUtils.getNewAccountRequest();
+        
+        // log the new user account request
+        logger.info("Requesting new user account for " + 
newAccountRequest.getUsername());
+
+        // get the justification
+        String justification = newAccountRequest.getJustification();
+        if (justification == null) {
+            justification = StringUtils.EMPTY;
+        }
+
+        // create the pending user account
+        return 
dtoFactory.createUserDTO(userService.createPendingUserAccount(newAccountRequest.getUsername(),
 justification));
+    }
+    
+    @Override
     public UserDTO updateUser(UserDTO userDto) {
         NiFiUser user;
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index aa51925..7ad0504 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -23,7 +23,6 @@ import 
com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider;
 import java.io.Serializable;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.security.cert.X509Certificate;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -44,9 +43,8 @@ import org.apache.nifi.action.Operation;
 import org.apache.nifi.cluster.context.ClusterContext;
 import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
 import org.apache.nifi.cluster.manager.impl.WebClusterManager;
-import org.apache.nifi.web.security.DnUtils;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.api.entity.Entity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
@@ -361,12 +359,10 @@ public abstract class ApplicationResource {
             result.put(PROXY_SCHEME_HTTP_HEADER, 
httpServletRequest.getScheme());
         }
 
-        // if this is a secure request, add the custom headers for proxying 
user requests
-        final X509Certificate cert = new 
X509CertificateExtractor().extractClientCertificate(httpServletRequest);
-        if (cert != null) {
+        if (httpServletRequest.isSecure()) {
 
             // add the certificate DN to the proxy chain
-            final String xProxiedEntitiesChain = 
DnUtils.getXProxiedEntitiesChain(httpServletRequest);
+            final String xProxiedEntitiesChain = 
ProxiedEntitiesUtils.getXProxiedEntitiesChain(httpServletRequest);
             if (StringUtils.isNotBlank(xProxiedEntitiesChain)) {
                 result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, 
xProxiedEntitiesChain);
             }

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
index 4a61ef4..c7a84c3 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
@@ -16,12 +16,15 @@
  */
 package org.apache.nifi.web.api;
 
+import com.sun.jersey.api.Responses;
 import com.wordnik.swagger.annotations.Api;
 import com.wordnik.swagger.annotations.ApiOperation;
 import com.wordnik.swagger.annotations.ApiParam;
 import com.wordnik.swagger.annotations.ApiResponse;
 import com.wordnik.swagger.annotations.ApiResponses;
 import com.wordnik.swagger.annotations.Authorization;
+import java.io.PrintWriter;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -33,12 +36,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.HttpMethod;
+import static javax.ws.rs.HttpMethod.POST;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -59,9 +65,11 @@ import 
org.apache.nifi.web.api.entity.UserSearchResultsEntity;
 import org.apache.nifi.web.api.entity.UsersEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.web.NiFiServiceFacade;
 import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
 import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 
 /**
@@ -83,6 +91,31 @@ public class UserResource extends ApplicationResource {
     private NiFiProperties properties;
     private NiFiServiceFacade serviceFacade;
 
+    @POST
+    @Consumes(MediaType.WILDCARD)
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("") // necessary due to a bug in swagger
+    @ApiOperation(
+            value = "Creates a user",
+            response = String.class
+    )
+    public Response createUser() {
+        if (!properties.getSupportNewAccountRequests()) {
+            return Responses.notFound().entity("This NiFi does not support new 
account requests.").build();
+        }
+        
+        final NiFiUser nifiUser = NiFiUserUtils.getNiFiUser();
+        if (nifiUser != null) {
+            throw new IllegalArgumentException("User account already created " 
+ nifiUser.getDn());
+        }
+        
+        // create an account request for the current user
+        final UserDTO user = serviceFacade.createUser();
+
+        final String uri = generateResourceUri("controller", "templates", 
user.getId());
+        return generateCreatedResponse(URI.create(uri), "Not authorized. User 
account created. Authorization pending.").build();
+    }
+    
     /**
      * Gets all users that are registered within this Controller.
      *

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 8bf5553..3bc8120 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -113,6 +113,7 @@ import org.apache.nifi.authorization.DownloadAuthorization;
 import org.apache.nifi.processor.DataUnit;
 import org.apache.nifi.reporting.BulletinQuery;
 import org.apache.nifi.reporting.ComponentType;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -822,17 +823,7 @@ public class ControllerFacade {
             final Map<String, String> attributes = event.getAttributes();
 
             // calculate the dn chain
-            final List<String> dnChain = new ArrayList<>();
-
-            // build the dn chain
-            NiFiUser chainedUser = user;
-            do {
-                // add the entry for this user
-                dnChain.add(chainedUser.getDn());
-
-                // go to the next user in the chain
-                chainedUser = chainedUser.getChain();
-            } while (chainedUser != null);
+            final List<String> dnChain = 
ProxiedEntitiesUtils.getXProxiedEntitiesChain(user);
 
             // ensure the users in this chain are allowed to download this 
content
             final DownloadAuthorization downloadAuthorization = 
userService.authorizeDownload(dnChain, attributes);

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
index 4ce319e..b57998d 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/webapp/WEB-INF/web.xml
@@ -16,15 +16,12 @@
 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";>
     <display-name>nifi-api</display-name>
     <context-param>
+        <param-name>contextClass</param-name>
+        
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
+    </context-param>
+    <context-param>
         <param-name>contextConfigLocation</param-name>
-        <param-value>
-            classpath:nifi-context.xml
-            classpath:nifi-web-api-context.xml
-            classpath:nifi-web-security-context.xml
-            classpath:nifi-administration-context.xml
-            classpath:nifi-cluster-manager-context.xml
-            classpath:nifi-cluster-protocol-context.xml
-        </param-value>
+        <param-value>org.apache.nifi.web</param-value>
     </context-param>
     <listener>
         
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
index 52f4522..c0e9246 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java
@@ -22,8 +22,7 @@ import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.core.util.MultivaluedMapImpl;
 import java.util.Map;
 import javax.ws.rs.core.MediaType;
-import org.apache.nifi.web.security.DnUtils;
-import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 
 /**
  *
@@ -37,7 +36,7 @@ public class NiFiTestUser {
 
     public NiFiTestUser(Client client, String dn) {
         this.client = client;
-        this.proxyDn = DnUtils.formatProxyDn(dn);
+        this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(dn);
     }
 
     /**
@@ -70,7 +69,7 @@ public class NiFiTestUser {
         }
 
         // perform the query
-        return 
resource.accept(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn).get(ClientResponse.class);
+        return 
resource.accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn).get(ClientResponse.class);
     }
 
     /**
@@ -94,7 +93,7 @@ public class NiFiTestUser {
      */
     public ClientResponse testPost(String url, Object entity) throws Exception 
{
         // get the resource
-        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn);
+        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn);
 
         // include the request entity
         if (entity != null) {
@@ -115,7 +114,7 @@ public class NiFiTestUser {
      */
     public ClientResponse testPostMultiPart(String url, Object entity) throws 
Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn);
+        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn);
 
         // include the request entity
         if (entity != null) {
@@ -143,7 +142,7 @@ public class NiFiTestUser {
 
         // get the resource
         WebResource.Builder resourceBuilder
-                = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn);
+                = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn);
 
         // add the form data if necessary
         if (!entity.isEmpty()) {
@@ -164,7 +163,7 @@ public class NiFiTestUser {
      */
     public ClientResponse testPut(String url, Object entity) throws Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn);
+        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn);
 
         // include the request entity
         if (entity != null) {
@@ -192,7 +191,7 @@ public class NiFiTestUser {
 
         // get the resource
         WebResource.Builder resourceBuilder
-                = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn);
+                = 
client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn);
 
         // add the form data if necessary
         if (!entity.isEmpty()) {
@@ -224,7 +223,7 @@ public class NiFiTestUser {
      */
     public ClientResponse testDelete(String url, Object entity) throws 
Exception {
         // get the resource
-        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_JSON).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn);
+        WebResource.Builder resourceBuilder = 
client.resource(url).accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn);
 
         // append any query parameters
         if (entity != null) {
@@ -255,7 +254,7 @@ public class NiFiTestUser {
         }
 
         // perform the request
-        return 
resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(X509AuthenticationFilter.PROXY_ENTITIES_CHAIN,
 proxyDn).delete(ClientResponse.class);
+        return 
resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN,
 proxyDn).delete(ClientResponse.class);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
deleted file mode 100644
index f3bd11e..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/DnUtils.java
+++ /dev/null
@@ -1,85 +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.web.security;
-
-import java.security.cert.X509Certificate;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- *
- */
-public class DnUtils {
-
-    private static final Pattern proxyChainPattern = 
Pattern.compile("<(.*?)>");
-
-    /**
-     * @param request http request
-     * @return the X-ProxiedEntitiesChain from the specified request
-     */
-    public static String getXProxiedEntitiesChain(final HttpServletRequest 
request) {
-        String xProxiedEntitiesChain = 
request.getHeader("X-ProxiedEntitiesChain");
-        final X509Certificate cert = new 
X509CertificateExtractor().extractClientCertificate(request);
-        if (cert != null) {
-            final SubjectDnX509PrincipalExtractor principalExtractor = new 
SubjectDnX509PrincipalExtractor();
-            final String extractedPrincipal = 
principalExtractor.extractPrincipal(cert).toString();
-            final String formattedPrincipal = 
formatProxyDn(extractedPrincipal);
-            if (StringUtils.isBlank(xProxiedEntitiesChain)) {
-                xProxiedEntitiesChain = formattedPrincipal;
-            } else {
-                xProxiedEntitiesChain += formattedPrincipal;
-            }
-        }
-
-        return xProxiedEntitiesChain;
-    }
-
-    /**
-     * Formats the specified DN to be set as a HTTP header using well known
-     * conventions.
-     *
-     * @param dn raw dn
-     * @return the dn formatted as an HTTP header
-     */
-    public static String formatProxyDn(String dn) {
-        return "<" + dn + ">";
-    }
-
-    /**
-     * Tokenizes the specified proxy chain.
-     *
-     * @param rawProxyChain raw chain
-     * @return tokenized proxy chain
-     */
-    public static Deque<String> tokenizeProxyChain(String rawProxyChain) {
-        final Deque<String> dnList = new ArrayDeque<>();
-
-        // parse the proxy chain
-        final Matcher rawProxyChainMatcher = 
proxyChainPattern.matcher(rawProxyChain);
-        while (rawProxyChainMatcher.find()) {
-            dnList.push(rawProxyChainMatcher.group(1));
-        }
-
-        return dnList;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
new file mode 100644
index 0000000..6cae1f0
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationEntryPoint.java
@@ -0,0 +1,69 @@
+/*
+ * 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.web.security;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.WebAttributes;
+
+/**
+ * This is our own implementation of
+ * org.springframework.security.web.AuthenticationEntryPoint that allows us to
+ * send the response to the client exactly how we want to and log the results.
+ */
+public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
+
+    /**
+     * Always returns a 403 error code to the client.
+     *
+     * @param request request
+     * @param response response
+     * @param ae ae
+     * @throws java.io.IOException ex
+     * @throws javax.servlet.ServletException ex
+     */
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse 
response, AuthenticationException ae) throws IOException, ServletException {
+        // get the last exception - the exception that is being passed in is a 
generic no credentials found
+        // exception because the authentication could not be found in the 
security context. the actual cause
+        // of the problem is stored in the session as the 
authentication_exception
+        Object authenticationException = 
request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
+
+        // log request result
+        if (authenticationException instanceof AuthenticationException) {
+            ae = (AuthenticationException) authenticationException;
+            logger.info(String.format("Rejecting access to web api: %s", 
ae.getMessage()));
+        }
+
+        // set the response status
+        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        response.setContentType("text/plain");
+
+        // write the response message
+        PrintWriter out = response.getWriter();
+        out.println("Access is denied.");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
new file mode 100644
index 0000000..52ac2f1
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -0,0 +1,144 @@
+/*
+ * 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.web.security;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ *
+ */
+public abstract class NiFiAuthenticationFilter implements Filter {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(NiFiAuthenticationFilter.class);
+
+    private AuthenticationManager authenticationManager;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        throw new UnsupportedOperationException("Not supported yet."); //To 
change body of generated methods, choose Tools | Templates.
+    }
+
+    /**
+     *
+     * @param request
+     * @param response
+     * @param chain
+     * @throws java.io.IOException
+     * @throws javax.servlet.ServletException
+     */
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Checking secure context token: " + 
SecurityContextHolder.getContext().getAuthentication());
+        }
+
+        if (requiresAuthentication((HttpServletRequest) request)) {
+            authenticate((HttpServletRequest) request, (HttpServletResponse) 
response);
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    private boolean requiresAuthentication(final HttpServletRequest request) {
+        return NiFiUserUtils.getNiFiUser() == null && 
NiFiUserUtils.getNewAccountRequest() == null;
+    }
+
+    private void authenticate(final HttpServletRequest request, final 
HttpServletResponse response) {
+        try {
+            final Authentication authenticated = 
attemptAuthentication(request, response);
+            if (authenticated != null) {
+                final Authentication authorized = 
authenticationManager.authenticate(authenticated);
+                successfulAuthorization(request, response, authorized);
+            }
+        } catch (final AuthenticationException ae) {
+            unsuccessfulAuthorization(request, response, ae);
+        }
+    }
+
+    public abstract Authentication attemptAuthentication(HttpServletRequest 
request, HttpServletResponse response);
+
+    protected void successfulAuthorization(HttpServletRequest request, 
HttpServletResponse response, Authentication authResult) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Authentication success: " + authResult);
+        }
+
+        SecurityContextHolder.getContext().setAuthentication(authResult);
+        ProxiedEntitiesUtils.successfulAuthorization(request, response, 
authResult);
+    }
+
+    protected void unsuccessfulAuthorization(HttpServletRequest request, 
HttpServletResponse response, AuthenticationException failed) {
+        ProxiedEntitiesUtils.unsuccessfulAuthorization(request, response, 
failed);
+    }
+
+    /**
+     * Determines if the specified request is attempting to register a new 
user account.
+     *
+     * @param request http request
+     * @return true if new user
+     */
+    protected final boolean isNewAccountRequest(HttpServletRequest request) {
+        if ("POST".equalsIgnoreCase(request.getMethod())) {
+            String path = request.getPathInfo();
+            if (StringUtils.isNotBlank(path)) {
+                if ("/controller/users".equals(path)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Extracts the justification from the specified request.
+     *
+     * @param request The request
+     * @return The justification
+     */
+    protected final String getJustification(HttpServletRequest request) {
+        // get the justification
+        String justification = request.getParameter("justification");
+        if (justification == null) {
+            justification = StringUtils.EMPTY;
+        }
+        return justification;
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    public void setAuthenticationManager(AuthenticationManager 
authenticationManager) {
+        this.authenticationManager = authenticationManager;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
new file mode 100644
index 0000000..5f15b76
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
@@ -0,0 +1,62 @@
+package org.apache.nifi.web.security;
+
+import org.apache.nifi.web.security.token.NewAccountAuthenticationToken;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import 
org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+/**
+ *
+ */
+public class NiFiAuthenticationProvider implements AuthenticationProvider {
+
+    private final AuthenticationProvider provider;
+    private final 
AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> 
userDetailsService;
+    
+    public NiFiAuthenticationProvider(final AuthenticationProvider provider, 
final AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> 
userDetailsService) {
+        this.provider = provider;
+        this.userDetailsService = userDetailsService;
+    }
+    
+    @Override
+    public Authentication authenticate(Authentication authentication) throws 
AuthenticationException {
+        final NiFiAuthenticationRequestToken request = 
(NiFiAuthenticationRequestToken) authentication;
+
+        // ensure the base provider could authenticate
+        final Authentication result = provider.authenticate(request);
+        if (result == null) {
+            return null;
+        }
+        
+        try {
+            // defer to the nifi user details service to authorize the user
+            final UserDetails userDetails = 
userDetailsService.loadUserDetails(request);
+
+            // build an authentication for accesing nifi
+            return new NiFiAuthorizationToken(userDetails);
+        } catch (final UsernameNotFoundException unfe) {
+            // if the result was an authenticated new account request and it 
could not be authorized because the user was not found,
+            // return the token so the new account could be created
+            if (isNewAccountAuthenticationToken(result)) {
+                return result;
+            } else {
+                throw unfe;
+            }
+        }
+    }
+    
+    private boolean isNewAccountAuthenticationToken(final Authentication 
authentication) {
+        return 
NewAccountAuthenticationToken.class.isAssignableFrom(authentication.getClass());
+    }
+
+    @Override
+    public boolean supports(Class<?> authentication) {
+        return provider.supports(authentication) && 
NiFiAuthenticationRequestToken.class.isAssignableFrom(authentication);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
new file mode 100644
index 0000000..e0d810c
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/ProxiedEntitiesUtils.java
@@ -0,0 +1,168 @@
+/*
+ * 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.web.security;
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ *
+ */
+public class ProxiedEntitiesUtils {
+
+    public static final String PROXY_ENTITIES_CHAIN = "X-ProxiedEntitiesChain";
+    public static final String PROXY_ENTITIES_ACCEPTED = 
"X-ProxiedEntitiesAccepted";
+    public static final String PROXY_ENTITIES_DETAILS = 
"X-ProxiedEntitiesDetails";
+    
+    private static final Pattern proxyChainPattern = 
Pattern.compile("<(.*?)>");
+
+    /**
+     * @param request http request
+     * @return the X-ProxiedEntitiesChain from the specified request
+     */
+    public static String getXProxiedEntitiesChain(final HttpServletRequest 
request) {
+        String xProxiedEntitiesChain = 
request.getHeader("X-ProxiedEntitiesChain");
+        final X509Certificate cert = new 
X509CertificateExtractor().extractClientCertificate(request);
+        if (cert != null) {
+            final SubjectDnX509PrincipalExtractor principalExtractor = new 
SubjectDnX509PrincipalExtractor();
+            final String extractedPrincipal = 
principalExtractor.extractPrincipal(cert).toString();
+            final String formattedPrincipal = 
formatProxyDn(extractedPrincipal);
+            if (StringUtils.isBlank(xProxiedEntitiesChain)) {
+                xProxiedEntitiesChain = formattedPrincipal;
+            } else {
+                xProxiedEntitiesChain += formattedPrincipal;
+            }
+        }
+
+        return xProxiedEntitiesChain;
+    }
+    
+    /**
+     * Builds the dn chain for the specified user.
+     * 
+     * @param user  The current user
+     * @return      The dn chain for that user
+     */
+    public static List<String> getXProxiedEntitiesChain(final NiFiUser user) {
+        // calculate the dn chain
+        final List<String> dnChain = new ArrayList<>();
+
+        // build the dn chain
+        NiFiUser chainedUser = user;
+        do {
+            // add the entry for this user
+            dnChain.add(chainedUser.getDn());
+
+            // go to the next user in the chain
+            chainedUser = chainedUser.getChain();
+        } while (chainedUser != null);
+        
+        return dnChain;
+    }
+
+    /**
+     * Formats the specified DN to be set as a HTTP header using well known
+     * conventions.
+     *
+     * @param dn raw dn
+     * @return the dn formatted as an HTTP header
+     */
+    public static String formatProxyDn(String dn) {
+        return "<" + dn + ">";
+    }
+
+//    /**
+//     * Tokenizes the specified proxy chain.
+//     *
+//     * @param rawProxyChain raw chain
+//     * @return tokenized proxy chain
+//     */
+//    public static Deque<String> tokenizeProxyChain(String rawProxyChain) {
+//        final Deque<String> dnList = new ArrayDeque<>();
+//
+//        // parse the proxy chain
+//        final Matcher rawProxyChainMatcher = 
proxyChainPattern.matcher(rawProxyChain);
+//        while (rawProxyChainMatcher.find()) {
+//            dnList.push(rawProxyChainMatcher.group(1));
+//        }
+//
+//        return dnList;
+//    }
+    
+    public static List<String> buildProxyChain(final HttpServletRequest 
request, final String username) {
+        String principal;
+        if (username.startsWith("<") && username.endsWith(">")) {
+            principal = username;
+        } else {
+            principal = formatProxyDn(username);
+        }
+        
+        // look for a proxied user
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
+        }
+        
+        // parse the proxy chain
+        final List<String> proxyChain = new ArrayList<>();
+        final Matcher rawProxyChainMatcher = 
proxyChainPattern.matcher(principal);
+        while (rawProxyChainMatcher.find()) {
+            proxyChain.add(rawProxyChainMatcher.group(1));
+        }
+
+        return proxyChain;
+    }
+    
+    public static String extractProxiedEntitiesChain(final HttpServletRequest 
request, final String username) {
+        String principal;
+        if (username.startsWith("<") && username.endsWith(">")) {
+            principal = username;
+        } else {
+            principal = formatProxyDn(username);
+        }
+        
+        // look for a proxied user
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
+        }
+        return principal;
+    }
+    
+    public static void successfulAuthorization(HttpServletRequest request, 
HttpServletResponse response, Authentication authResult) {
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            response.setHeader(PROXY_ENTITIES_ACCEPTED, 
Boolean.TRUE.toString());
+        }
+    }
+
+    public static void unsuccessfulAuthorization(HttpServletRequest request, 
HttpServletResponse response, AuthenticationException failed) {
+        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
+            response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
index 295f09c..7026124 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
@@ -16,8 +16,6 @@
  */
 package org.apache.nifi.web.security.anonymous;
 
-import java.util.ArrayList;
-import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AdministrationException;
@@ -25,11 +23,10 @@ import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import 
org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
 import 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
 
 /**
@@ -51,45 +48,31 @@ public class NiFiAnonymousUserFilter extends 
AnonymousAuthenticationFilter {
 
     @Override
     protected Authentication createAuthentication(HttpServletRequest request) {
-        Authentication authentication;
-        try {
-            // load the anonymous user from the database
-            NiFiUser user = 
userService.getUserByDn(NiFiUser.ANONYMOUS_USER_DN);
-            NiFiUserDetails userDetails = new NiFiUserDetails(user);
+        Authentication authentication = null;
 
-            // get the granted authorities
-            List<GrantedAuthority> authorities = new 
ArrayList<>(userDetails.getAuthorities());
-            authentication = new AnonymousAuthenticationToken(ANONYMOUS_KEY, 
userDetails, authorities);
-        } catch (AdministrationException ase) {
-            // record the issue
-            anonymousUserFilterLogger.warn("Unable to load anonymous user from 
accounts database: " + ase.getMessage());
-            if (anonymousUserFilterLogger.isDebugEnabled()) {
-                anonymousUserFilterLogger.warn(StringUtils.EMPTY, ase);
-            }
+        // only support anonymous when the request is non-secure or one way ssl
+//        if (!request.isSecure() || !properties.getNeedClientAuth()) {
+        if (true) {
+            try {
+                // load the anonymous user from the database
+                NiFiUser user = 
userService.getUserByDn(NiFiUser.ANONYMOUS_USER_DN);
+                NiFiUserDetails userDetails = new NiFiUserDetails(user);
 
-            // defer to the base implementation
-            authentication = super.createAuthentication(request);
+                // get the granted authorities
+                authentication = new NiFiAuthorizationToken(userDetails);
+            } catch (AdministrationException ase) {
+                // record the issue
+                anonymousUserFilterLogger.warn("Unable to load anonymous user 
from accounts database: " + ase.getMessage());
+                if (anonymousUserFilterLogger.isDebugEnabled()) {
+                    anonymousUserFilterLogger.warn(StringUtils.EMPTY, ase);
+                }
+            }
         }
         return authentication;
     }
 
-    /**
-     * Only supports anonymous users for non-secure requests or one way ssl.
-     *
-     * @param request request
-     * @return true if allowed
-     */
-    @Override
-    protected boolean applyAnonymousForThisRequest(HttpServletRequest request) 
{
-        // anonymous for non secure requests
-        if ("http".equalsIgnoreCase(request.getScheme())) {
-            return true;
-        }
-
-        return !properties.getNeedClientAuth();
-    }
-
     /* setters */
+
     public void setUserService(UserService userService) {
         this.userService = userService;
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
deleted file mode 100644
index cd5f1ac..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authentication/NiFiAuthenticationEntryPoint.java
+++ /dev/null
@@ -1,69 +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.web.security.authentication;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.WebAttributes;
-
-/**
- * This is our own implementation of
- * org.springframework.security.web.AuthenticationEntryPoint that allows us to
- * send the response to the client exactly how we want to and log the results.
- */
-public class NiFiAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
-    private static final Logger logger = 
LoggerFactory.getLogger(NiFiAuthenticationEntryPoint.class);
-
-    /**
-     * Always returns a 403 error code to the client.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ae
-     * @throws java.io.IOException ex
-     * @throws javax.servlet.ServletException ex
-     */
-    @Override
-    public void commence(HttpServletRequest request, HttpServletResponse 
response, AuthenticationException ae) throws IOException, ServletException {
-        // get the last exception - the exception that is being passed in is a 
generic no credentials found
-        // exception because the authentication could not be found in the 
security context. the actual cause
-        // of the problem is stored in the session as the 
authentication_exception
-        Object authenticationException = 
request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
-
-        // log request result
-        if (authenticationException instanceof AuthenticationException) {
-            ae = (AuthenticationException) authenticationException;
-            logger.info(String.format("Rejecting access to web api: %s", 
ae.getMessage()));
-        }
-
-        // set the response status
-        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-        response.setContentType("text/plain");
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println("Access is denied.");
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
index 95b4669..c3a5e43 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
@@ -16,34 +16,35 @@
  */
 package org.apache.nifi.web.security.authorization;
 
-import java.util.Deque;
-import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AccountDisabledException;
 import org.apache.nifi.admin.service.AccountNotFoundException;
 import org.apache.nifi.admin.service.AccountPendingException;
 import org.apache.nifi.admin.service.AdministrationException;
 import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.authorization.Authority;
-import org.apache.nifi.web.security.DnUtils;
 import org.apache.nifi.user.NiFiUser;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.UntrustedProxyException;
 import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.dao.DataAccessException;
 import org.springframework.security.authentication.AccountStatusException;
 import 
org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.core.AuthenticationException;
+import 
org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
 import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
 /**
  * UserDetailsService that will verify user identity and grant user 
authorities.
  */
-public class NiFiAuthorizationService implements UserDetailsService {
+public class NiFiAuthorizationService implements 
AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> {
 
     private static final Logger logger = 
LoggerFactory.getLogger(NiFiAuthorizationService.class);
 
@@ -58,30 +59,30 @@ public class NiFiAuthorizationService implements 
UserDetailsService {
      * made for each individual request as a whole (without other request
      * potentially impacting it).
      *
-     * @param rawProxyChain proxy chain
+     * @param request request
      * @return user details
      * @throws UsernameNotFoundException ex
      * @throws org.springframework.dao.DataAccessException ex
      */
     @Override
-    public synchronized UserDetails loadUserByUsername(String rawProxyChain) 
throws UsernameNotFoundException, DataAccessException {
+    public synchronized UserDetails 
loadUserDetails(NiFiAuthenticationRequestToken request) throws 
UsernameNotFoundException, DataAccessException {
         NiFiUserDetails userDetails = null;
-        final Deque<String> dnList = DnUtils.tokenizeProxyChain(rawProxyChain);
+        final List<String> chain = new ArrayList<>(request.getChain());
 
         // ensure valid input
-        if (dnList.size() == 0) {
-            logger.warn("Malformed proxy chain: " + rawProxyChain);
+        if (chain.isEmpty()) {
+            logger.warn("Malformed proxy chain: " + 
StringUtils.join(request.getChain()));
             throw new UntrustedProxyException("Malformed proxy chain.");
         }
 
         NiFiUser proxy = null;
 
         // process each part of the proxy chain
-        for (final Iterator<String> dnIter = dnList.iterator(); 
dnIter.hasNext();) {
-            final String dn = dnIter.next();
+        for (final ListIterator<String> chainIter = 
request.getChain().listIterator(chain.size()); chainIter.hasPrevious();) {
+            final String dn = chainIter.previous();
 
             // if there is another dn after this one, this dn is a proxy for 
the request
-            if (dnIter.hasNext()) {
+            if (chainIter.hasPrevious()) {
                 try {
                     // get the user details for the proxy
                     final NiFiUserDetails proxyDetails = 
getNiFiUserDetails(dn);
@@ -110,7 +111,7 @@ public class NiFiAuthorizationService implements 
UserDetailsService {
                             userService.createPendingUserAccount(dn, 
"Automatic account request generated for unknown proxy.");
 
                             // propagate the exception to return the 
appropriate response
-                            throw new 
UsernameNotFoundException(String.format("An account request was generated for 
the proxy '%s'.", dn));
+                            throw new 
UntrustedProxyException(String.format("An account request was generated for the 
proxy '%s'.", dn));
                         } catch (AdministrationException ae) {
                             throw new 
AuthenticationServiceException(String.format("Unable to create an account 
request for '%s': %s", dn, ae.getMessage()), ae);
                         } catch (IllegalArgumentException iae) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
deleted file mode 100644
index 80feed7..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NodeAuthorizedUserFilter.java
+++ /dev/null
@@ -1,128 +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.web.security.authorization;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.security.cert.X509Certificate;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.nifi.controller.FlowController;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.x509.SubjectDnX509PrincipalExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.nifi.user.NiFiUser;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.util.WebUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
-import org.springframework.security.authentication.AuthenticationDetailsSource;
-import org.springframework.security.core.context.SecurityContextHolder;
-import 
org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import 
org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
-import 
org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
-import org.springframework.web.context.support.WebApplicationContextUtils;
-import org.springframework.web.filter.GenericFilterBean;
-
-/**
- * Custom filter to extract a user's authorities from the request where the 
user
- * was authenticated by the cluster manager and populate the threadlocal with
- * the authorized user. If the request contains the appropriate header with
- * authorities and the application instance is a node connected to the cluster,
- * then the authentication/authorization steps remaining in the filter chain 
are
- * skipped.
- *
- * Checking if the application instance is a connected node is important 
because
- * it prevents external clients from faking the request headers and bypassing
- * the authentication processing chain.
- */
-public class NodeAuthorizedUserFilter extends GenericFilterBean {
-
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(NodeAuthorizedUserFilter.class);
-
-    public static final String PROXY_USER_DETAILS = 
"X-ProxiedEntityUserDetails";
-
-    private NiFiProperties properties;
-    private final AuthenticationDetailsSource authenticationDetailsSource = 
new WebAuthenticationDetailsSource();
-    private final X509CertificateExtractor certificateExtractor = new 
X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new 
SubjectDnX509PrincipalExtractor();
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
-        final HttpServletRequest httpServletRequest = (HttpServletRequest) 
request;
-
-        // get the proxied user's authorities
-        final String hexEncodedUserDetails = 
httpServletRequest.getHeader(PROXY_USER_DETAILS);
-
-        // check if the request has the necessary header information and this 
instance is configured as a node
-        if (StringUtils.isNotBlank(hexEncodedUserDetails) && 
properties.isNode()) {
-
-            // get the flow controller from the Spring context
-            final ApplicationContext ctx = 
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
-            final FlowController flowController = 
ctx.getBean("flowController", FlowController.class);
-
-            // check that we are connected to the cluster
-            if (flowController.getNodeId() != null) {
-                try {
-                    // get the DN from the cert in the request
-                    final X509Certificate certificate = 
certificateExtractor.extractClientCertificate((HttpServletRequest) request);
-                    if (certificate != null) {
-                        // extract the principal from the certificate
-                        final Object certificatePrincipal = 
principalExtractor.extractPrincipal(certificate);
-                        final String dn = certificatePrincipal.toString();
-
-                        // only consider the pre-authorized user when the 
request came from the NCM according to the DN in the certificate
-                        final String clusterManagerDN = 
flowController.getClusterManagerDN();
-                        if (clusterManagerDN != null && 
clusterManagerDN.equals(dn)) {
-                            // deserialize hex encoded object
-                            final Serializable userDetailsObj = 
WebUtils.deserializeHexToObject(hexEncodedUserDetails);
-
-                            // if we have a valid object, set the 
authentication token and bypass the remaining authentication processing chain
-                            if (userDetailsObj instanceof NiFiUserDetails) {
-                                final NiFiUserDetails userDetails = 
(NiFiUserDetails) userDetailsObj;
-                                final NiFiUser user = 
userDetails.getNiFiUser();
-
-                                // log the request attempt - response details 
will be logged later
-                                logger.info(String.format("Attempting request 
for (%s) %s %s (source ip: %s)", user.getDn(), httpServletRequest.getMethod(),
-                                        
httpServletRequest.getRequestURL().toString(), request.getRemoteAddr()));
-
-                                // we do not create the authentication token 
with the X509 certificate because the certificate is from the sending system, 
not the proxied user
-                                final PreAuthenticatedAuthenticationToken 
token = new PreAuthenticatedAuthenticationToken(userDetails, null, 
userDetails.getAuthorities());
-                                
token.setDetails(authenticationDetailsSource.buildDetails(request));
-                                
SecurityContextHolder.getContext().setAuthentication(token);
-                            }
-                        }
-                    }
-                } catch (final ClassNotFoundException cnfe) {
-                    LOGGER.warn("Classpath issue detected because failed to 
deserialize authorized user in request header due to: " + cnfe, cnfe);
-                }
-            }
-        }
-
-        chain.doFilter(request, response);
-    }
-
-    /* setters */
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/61046707/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
new file mode 100644
index 0000000..5367ba2
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/FormAuthenticationFilter.java
@@ -0,0 +1,72 @@
+/*
+ * 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.web.security.form;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import 
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import 
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+
+/**
+ */
+public class FormAuthenticationFilter extends 
AbstractAuthenticationProcessingFilter {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(FormAuthenticationFilter.class);
+
+    private JwtService jwtService;
+    
+    public FormAuthenticationFilter(final String defaultFilterProcessesUrl) {
+        super(defaultFilterProcessesUrl);
+    }
+
+    @Override
+    public Authentication attemptAuthentication(final HttpServletRequest 
request, final HttpServletResponse response) throws AuthenticationException, 
IOException, ServletException {
+        final String username = request.getParameter("username");
+        final String password = request.getParameter("password");
+        return getAuthenticationManager().authenticate(new 
UsernamePasswordAuthenticationToken(username, password));
+    }
+
+    @Override
+    protected void successfulAuthentication(final HttpServletRequest request, 
final HttpServletResponse response, final FilterChain chain, final 
Authentication authentication) throws IOException, ServletException {
+        // generate JWT for response
+        jwtService.addToken(response, authentication);
+    }
+
+    @Override
+    protected void unsuccessfulAuthentication(final HttpServletRequest 
request, final HttpServletResponse response, final AuthenticationException 
failed) throws IOException, ServletException {
+        // set the response status
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        response.setContentType("text/plain");
+
+        final PrintWriter out = response.getWriter();
+        out.println("Invalid username/password");
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+}

Reply via email to