You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/12/01 17:43:41 UTC

[03/51] [abbrv] nifi git commit: NIFI-655: - Refactoring certificate extraction and validation. - Refactoring how expiration is specified in the login identity providers. - Adding unit tests for the access endpoints. - Code clean up.

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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
index ca42059..1b2f28a 100644
--- 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
@@ -16,15 +16,12 @@
  */
 package org.apache.nifi.web.security;
 
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 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;
@@ -42,33 +39,50 @@ public class ProxiedEntitiesUtils {
     private static final Pattern proxyChainPattern = Pattern.compile("<(.*?)>");
 
     /**
-     * @param request http request
-     * @return the X-ProxiedEntitiesChain from the specified request
+     * 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 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;
-            }
+    public static String formatProxyDn(String dn) {
+        return "<" + dn + ">";
+    }
+
+    /**
+     * Tokenizes the specified proxy chain.
+     *
+     * @param rawProxyChain raw chain
+     * @return tokenized proxy chain
+     */
+    public static List<String> tokenizeProxiedEntitiesChain(String rawProxyChain) {
+        final List<String> proxyChain = new ArrayList<>();
+        final Matcher rawProxyChainMatcher = proxyChainPattern.matcher(rawProxyChain);
+        while (rawProxyChainMatcher.find()) {
+            proxyChain.add(rawProxyChainMatcher.group(1));
         }
 
-        return xProxiedEntitiesChain;
+        return proxyChain;
+    }
+
+    /**
+     * Builds the proxy chain for the specified user.
+     *
+     * @param user The current user
+     * @return The proxy chain for that user in String form
+     */
+    public static String buildProxiedEntitiesChainString(final NiFiUser user) {
+        // calculate the dn chain
+        final List<String> proxyChain = buildProxiedEntitiesChain(user);
+        return formatProxyDn(StringUtils.join(proxyChain, "><"));
     }
 
     /**
-     * Builds the dn chain for the specified user.
+     * Builds the proxy chain for the specified user.
      *
      * @param user The current user
-     * @return The dn chain for that user
+     * @return The proxy chain for that user in List form
      */
-    public static List<String> getXProxiedEntitiesChain(final NiFiUser user) {
+    public static List<String> buildProxiedEntitiesChain(final NiFiUser user) {
         // calculate the dn chain
         final List<String> proxyChain = new ArrayList<>();
 
@@ -86,56 +100,25 @@ public class ProxiedEntitiesUtils {
     }
 
     /**
-     * Formats the specified DN to be set as a HTTP header using well known conventions.
+     * Builds the proxy chain from the specified request and user.
      *
-     * @param dn raw dn
-     * @return the dn formatted as an HTTP header
+     * @param request the request
+     * @param username the username
+     * @return the proxy chain in list form
      */
-    public static String formatProxyDn(String dn) {
-        return "<" + dn + ">";
+    public static List<String> buildProxiedEntitiesChain(final HttpServletRequest request, final String username) {
+        final String chain = buildProxiedEntitiesChainString(request, username);
+        return tokenizeProxiedEntitiesChain(chain);
     }
 
-//    /**
-//     * 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) {
+    /**
+     * Builds the dn chain from the specified request and user.
+     *
+     * @param request the request
+     * @param username the username
+     * @return the dn chain in string form
+     */
+    public static String buildProxiedEntitiesChainString(final HttpServletRequest request, final String username) {
         String principal;
         if (username.startsWith("<") && username.endsWith(">")) {
             principal = username;

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/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 7fb8e5c..23d9e61 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
@@ -54,10 +54,8 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
     /**
      * Loads the user details for the specified dn.
      *
-     * Synchronizing because we want each request to be authorized atomically
-     * since each may contain any number of DNs. We wanted an access decision
-     * made for each individual request as a whole (without other request
-     * potentially impacting it).
+     * Synchronizing because we want each request to be authorized atomically since each may contain any number of DNs. We wanted an access decision made for each individual request as a whole
+     * (without other request potentially impacting it).
      *
      * @param request request
      * @return user details
@@ -109,9 +107,6 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
 
                             // attempt to create a new user account for the proxying client
                             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));
                         } catch (AdministrationException ae) {
                             throw new AuthenticationServiceException(String.format("Unable to create an account request for '%s': %s", dn, ae.getMessage()), ae);
                         } catch (IllegalArgumentException iae) {
@@ -122,10 +117,10 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
                             throw new AccountStatusException(message) {
                             };
                         }
-                    } else {
-                        logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), unfe.getMessage()));
-                        throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));
                     }
+
+                    logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), unfe.getMessage()));
+                    throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));
                 } catch (AuthenticationException ae) {
                     logger.warn(String.format("Untrusted proxy '%s' must be authorized with '%s' authority: %s", dn, Authority.ROLE_PROXY.toString(), ae.getMessage()));
                     throw new UntrustedProxyException(String.format("Untrusted proxy '%s' must be authorized with '%s'.", dn, Authority.ROLE_PROXY.toString()));

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.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/jwt/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index 22d9104..5a84e93 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -25,7 +25,6 @@ import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.core.Authentication;
 
 /**
  */
@@ -36,7 +35,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
     private JwtService jwtService;
 
     @Override
-    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
         // only suppport jwt login when running securely
         if (!request.isSecure()) {
             return null;

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.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/jwt/JwtService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
index 2f6c726..25e52d6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtService.java
@@ -82,7 +82,7 @@ public class JwtService {
     /**
      * Generates a signed JWT token from the provided (Spring Security) login authentication token.
      *
-     * @param authenticationToken
+     * @param authenticationToken the authentication token
      * @return a signed JWT containing the user identity and the identity provider, Base64-encoded
      */
     public String generateSignedToken(final LoginAuthenticationToken authenticationToken) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/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/node/NodeAuthorizedUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
index d1207b4..a3e6c3c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/node/NodeAuthorizedUserFilter.java
@@ -26,34 +26,26 @@ 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.authentication.AuthenticationResponse;
 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.security.token.NiFiAuthorizationToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509IdentityProvider;
 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.
+ * 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.
+ * 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 {
 
@@ -61,14 +53,9 @@ public class NodeAuthorizedUserFilter extends GenericFilterBean {
 
     public static final String PROXY_USER_DETAILS = "X-ProxiedEntityUserDetails";
 
-    private final NiFiProperties properties;
-    private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
-    private final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-
-    public NodeAuthorizedUserFilter(NiFiProperties properties) {
-        this.properties = properties;
-    }
+    private NiFiProperties properties;
+    private X509CertificateExtractor certificateExtractor;
+    private X509IdentityProvider certificateIdentityProvider;
 
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
@@ -87,16 +74,15 @@ public class NodeAuthorizedUserFilter extends GenericFilterBean {
             // 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);
+                    // attempt to extract the client certificate
+                    final X509Certificate[] certificate = certificateExtractor.extractClientCertificate(httpServletRequest);
                     if (certificate != null) {
-                        // extract the principal from the certificate
-                        final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-                        final String dn = certificatePrincipal.toString();
+                        // authenticate the certificate
+                        final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificate);
 
-                        // 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)) {
+                        // only consider the pre-authorized user when the request came directly from the NCM according to the DN in the certificate
+                        final String clusterManagerIdentity = flowController.getClusterManagerDN();
+                        if (clusterManagerIdentity != null && clusterManagerIdentity.equals(authenticationResponse.getIdentity())) {
                             // deserialize hex encoded object
                             final Serializable userDetailsObj = WebUtils.deserializeHexToObject(hexEncodedUserDetails);
 
@@ -109,19 +95,33 @@ public class NodeAuthorizedUserFilter extends GenericFilterBean {
                                 logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", user.getIdentity(), 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));
+                                // create the authorized nifi token
+                                final NiFiAuthorizationToken token = new NiFiAuthorizationToken(userDetails);
                                 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);
+                } catch (final IllegalArgumentException iae) {
+                    // unable to authenticate a serialized user from the incoming request
                 }
             }
         }
 
         chain.doFilter(request, response);
     }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.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/spring/LoginIdentityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
index e115b01..92a27ae 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/spring/LoginIdentityProviderFactoryBean.java
@@ -267,13 +267,6 @@ public class LoginIdentityProviderFactoryBean implements FactoryBean, Disposable
             }
 
             @Override
-            public long getExpiration() {
-                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
-                    return baseProvider.getExpiration();
-                }
-            }
-
-            @Override
             public void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException {
                 try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
                     baseProvider.initialize(initializationContext);

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.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/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index f84231f..e626f74 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -16,12 +16,11 @@
  */
 package org.apache.nifi.web.security.x509;
 
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.authentication.AuthenticationResponse;
 import org.apache.nifi.web.security.NiFiAuthenticationFilter;
 import org.apache.nifi.web.security.ProxiedEntitiesUtils;
 import org.apache.nifi.web.security.token.NewAccountAuthenticationRequestToken;
@@ -29,8 +28,7 @@ import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
 import org.apache.nifi.web.security.user.NewAccountRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+import org.springframework.security.authentication.BadCredentialsException;
 
 /**
  * Custom X509 filter that will inspect the HTTP headers for a proxied user before extracting the user details from the client certificate.
@@ -39,54 +37,31 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
 
     private static final Logger logger = LoggerFactory.getLogger(X509AuthenticationFilter.class);
 
-    private X509PrincipalExtractor principalExtractor;
     private X509CertificateExtractor certificateExtractor;
-    private X509CertificateValidator certificateValidator;
+    private X509IdentityProvider certificateIdentityProvider;
 
     @Override
-    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
+    public NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
         // only suppport x509 login when running securely
         if (!request.isSecure()) {
             return null;
         }
 
-        // extract the cert
-        X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-        // ensure the cert was found
-        if (certificate == null) {
+        // look for a client certificate
+        final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(request);
+        if (certificates == null) {
             return null;
         }
 
-        // extract the principal
-        Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-        final String principal = ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
-
+        // attempt to authenticate if certificates were found
+        final AuthenticationResponse authenticationResponse;
         try {
-            certificateValidator.validateClientCertificate(request, certificate);
-        } catch (CertificateExpiredException cee) {
-            final String message = String.format("Client certificate for (%s) is expired.", principal);
-            logger.info(message, cee);
-            if (logger.isDebugEnabled()) {
-                logger.debug("", cee);
-            }
-            return null;
-        } catch (CertificateNotYetValidException cnyve) {
-            final String message = String.format("Client certificate for (%s) is not yet valid.", principal);
-            logger.info(message, cnyve);
-            if (logger.isDebugEnabled()) {
-                logger.debug("", cnyve);
-            }
-            return null;
-        } catch (final Exception e) {
-            logger.info(e.getMessage());
-            if (logger.isDebugEnabled()) {
-                logger.debug("", e);
-            }
-            return null;
+            authenticationResponse = certificateIdentityProvider.authenticate(certificates);
+        } catch (final IllegalArgumentException iae) {
+            throw new BadCredentialsException(iae.getMessage(), iae);
         }
 
-        final List<String> proxyChain = ProxiedEntitiesUtils.buildProxyChain(request, principal);
+        final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(request, authenticationResponse.getIdentity());
         if (isNewAccountRequest(request)) {
             return new NewAccountAuthenticationRequestToken(new NewAccountRequest(proxyChain, getJustification(request)));
         } else {
@@ -95,16 +70,12 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
     }
 
     /* setters */
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
-    }
-
     public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
         this.certificateExtractor = certificateExtractor;
     }
 
+    public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
+        this.certificateIdentityProvider = certificateIdentityProvider;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilterOld.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/x509/X509AuthenticationFilterOld.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilterOld.java
deleted file mode 100644
index 711639b..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilterOld.java
+++ /dev/null
@@ -1,317 +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.x509;
-
-import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-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 javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.admin.service.AdministrationException;
-import org.apache.nifi.admin.service.UserService;
-import org.apache.nifi.web.security.ProxiedEntitiesUtils;
-import org.apache.nifi.web.security.UntrustedProxyException;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.security.authentication.AccountStatusException;
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
-import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
-
-/**
- * Custom X509 filter that will inspect the HTTP headers for a proxied user
- * before extracting the user details from the client certificate.
- */
-public class X509AuthenticationFilterOld extends AbstractPreAuthenticatedProcessingFilter {
-
-    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 final X509CertificateExtractor certificateExtractor = new X509CertificateExtractor();
-    private final X509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
-    private OcspCertificateValidator certificateValidator;
-    private NiFiProperties properties;
-    private UserService userService;
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        final HttpServletResponse httpResponse = (HttpServletResponse) response;
-
-        // determine if this request is attempting to create a new account
-        if (isNewAccountRequest((HttpServletRequest) request)) {
-            // determine if this nifi supports new account requests
-            if (properties.getSupportNewAccountRequests()) {
-                // ensure there is a certificate in the request
-                X509Certificate certificate = certificateExtractor.extractClientCertificate((HttpServletRequest) request);
-                if (certificate != null) {
-                    // extract the principal from the certificate
-                    Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-                    String principal = certificatePrincipal.toString();
-
-                    // log the new user account request
-                    logger.info("Requesting new user account for " + principal);
-
-                    try {
-                        // get the justification
-                        String justification = request.getParameter("justification");
-                        if (justification == null) {
-                            justification = StringUtils.EMPTY;
-                        }
-
-                        // create the pending user account
-                        userService.createPendingUserAccount(principal, justification);
-
-                        // generate a response
-                        httpResponse.setStatus(HttpServletResponse.SC_CREATED);
-                        httpResponse.setContentType("text/plain");
-
-                        // write the response message
-                        PrintWriter out = response.getWriter();
-                        out.println("Not authorized. User account created. Authorization pending.");
-                    } catch (IllegalArgumentException iae) {
-                        handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_BAD_REQUEST, iae.getMessage());
-                    } catch (AdministrationException ae) {
-                        handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ae.getMessage());
-                    }
-                } else {
-                    // can this really happen?
-                    handleMissingCertificate((HttpServletRequest) request, httpResponse);
-                }
-            } else {
-                handleUserServiceError((HttpServletRequest) request, httpResponse, HttpServletResponse.SC_NOT_FOUND, "This NiFi does not support new account requests.");
-            }
-        } else {
-            try {
-                // this not a request to create a user account - try to authorize
-                super.doFilter(request, response, chain);
-            } catch (AuthenticationException ae) {
-                // continue the filter chain since anonymous access should be supported
-                if (!properties.getNeedClientAuth()) {
-                    chain.doFilter(request, response);
-                } else {
-                    // create an appropriate response for the given exception
-                    handleUnsuccessfulAuthentication((HttpServletRequest) request, httpResponse, ae);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
-        String principal;
-
-        // extract the cert
-        X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-        // ensure the cert was found
-        if (certificate == null) {
-            return null;
-        }
-
-        // extract the principal
-        Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
-        principal = ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
-
-        try {
-            // ensure the cert is valid
-            certificate.checkValidity();
-        } catch (CertificateExpiredException cee) {
-            final String message = String.format("Client certificate for (%s) is expired.", principal);
-            logger.info(message, cee);
-            if (logger.isDebugEnabled()) {
-                logger.debug("", cee);
-            }
-            return null;
-        } catch (CertificateNotYetValidException cnyve) {
-            final String message = String.format("Client certificate for (%s) is not yet valid.", principal);
-            logger.info(message, cnyve);
-            if (logger.isDebugEnabled()) {
-                logger.debug("", cnyve);
-            }
-            return null;
-        }
-
-        // validate the certificate in question
-        try {
-            certificateValidator.validate(request);
-        } catch (final Exception e) {
-            logger.info(e.getMessage());
-            if (logger.isDebugEnabled()) {
-                logger.debug("", e);
-            }
-            return null;
-        }
-
-        // look for a proxied user
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            principal = request.getHeader(PROXY_ENTITIES_CHAIN) + principal;
-        }
-
-        // log the request attempt - response details will be logged later
-        logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", principal, request.getMethod(),
-                request.getRequestURL().toString(), request.getRemoteAddr()));
-
-        return principal;
-    }
-
-    @Override
-    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
-        return certificateExtractor.extractClientCertificate(request);
-    }
-
-    @Override
-    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_ACCEPTED, Boolean.TRUE.toString());
-        }
-        super.successfulAuthentication(request, response, authResult);
-    }
-
-    @Override
-    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
-        if (StringUtils.isNotBlank(request.getHeader(PROXY_ENTITIES_CHAIN))) {
-            response.setHeader(PROXY_ENTITIES_DETAILS, failed.getMessage());
-        }
-        super.unsuccessfulAuthentication(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
-     */
-    private 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;
-    }
-
-    /**
-     * Handles requests that were unable to be authorized.
-     *
-     * @param request request
-     * @param response response
-     * @param ae ex
-     * @throws IOException ex
-     */
-    private void handleUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException {
-        // 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 (ae instanceof UsernameNotFoundException) {
-            if (properties.getSupportNewAccountRequests()) {
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-                out.println("Not authorized.");
-            } else {
-                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-                out.println("Access is denied.");
-            }
-        } else if (ae instanceof AccountStatusException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof UntrustedProxyException) {
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println(ae.getMessage());
-        } else if (ae instanceof AuthenticationServiceException) {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            out.println(String.format("Unable to authorize: %s", ae.getMessage()));
-        } else {
-            logger.error(String.format("Unable to authorize: %s", ae.getMessage()), ae);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-            out.println("Access is denied.");
-        }
-
-        // log the failure
-        logger.info(String.format("Rejecting access to web api: %s", ae.getMessage()));
-
-        // optionally log the stack trace
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, ae);
-        }
-    }
-
-    private void handleUserServiceError(HttpServletRequest request, HttpServletResponse response, int responseCode, String message) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(responseCode);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println(message);
-
-        // log the failure
-        logger.info(String.format("Unable to process request because %s", message));
-    }
-
-    /**
-     * Handles requests that failed because they were bad input.
-     *
-     * @param request request
-     * @param response response
-     * @throws IOException ioe
-     */
-    private void handleMissingCertificate(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        // set the response status
-        response.setContentType("text/plain");
-        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-
-        // write the response message
-        PrintWriter out = response.getWriter();
-        out.println("Unable to process request because the user certificate was not specified.");
-
-        // log the failure
-        logger.info("Unable to process request because the user certificate was not specified.");
-    }
-
-    /* setters */
-    public void setProperties(NiFiProperties properties) {
-        this.properties = properties;
-    }
-
-    public void setUserService(UserService userService) {
-        this.userService = userService;
-    }
-
-    public void setCertificateValidator(OcspCertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.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/x509/X509CertificateExtractor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
index b40d5a5..98d0154 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateExtractor.java
@@ -35,11 +35,11 @@ public class X509CertificateExtractor {
      * @param request http request
      * @return cert
      */
-    public X509Certificate extractClientCertificate(HttpServletRequest request) {
+    public X509Certificate[] extractClientCertificate(HttpServletRequest request) {
         X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
 
         if (certs != null && certs.length > 0) {
-            return certs[0];
+            return certs;
         }
 
         if (logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.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/x509/X509CertificateValidator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
index 06b5148..cb56a11 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509CertificateValidator.java
@@ -19,7 +19,6 @@ package org.apache.nifi.web.security.x509;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
-import javax.servlet.http.HttpServletRequest;
 import org.apache.nifi.web.security.x509.ocsp.CertificateStatusException;
 import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
 import org.slf4j.Logger;
@@ -37,20 +36,19 @@ public class X509CertificateValidator {
     /**
      * Extract the client certificate from the specified HttpServletRequest or null if none is specified.
      *
-     * @param request the request
-     * @param certificate the certificate
+     * @param certificates the client certificates
      * @throws java.security.cert.CertificateExpiredException cert is expired
      * @throws java.security.cert.CertificateNotYetValidException cert is not yet valid
      * @throws org.apache.nifi.web.security.x509.ocsp.CertificateStatusException ocsp validation issue
      */
-    public void validateClientCertificate(final HttpServletRequest request, final X509Certificate certificate)
+    public void validateClientCertificate(final X509Certificate[] certificates)
             throws CertificateExpiredException, CertificateNotYetValidException, CertificateStatusException {
 
         // ensure the cert is valid
-        certificate.checkValidity();
+        certificates[0].checkValidity();
 
         // perform ocsp validator if necessary
-        ocspValidator.validate(request);
+        ocspValidator.validate(certificates);
     }
 
     public void setOcspValidator(OcspCertificateValidator ocspValidator) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.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/x509/X509IdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
new file mode 100644
index 0000000..75a94d3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509IdentityProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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.x509;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authentication.AuthenticationResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+
+/**
+ * Identity provider for extract the authenticating a ServletRequest with a X509Certificate.
+ */
+public class X509IdentityProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(X509IdentityProvider.class);
+
+    private X509CertificateValidator certificateValidator;
+    private X509PrincipalExtractor principalExtractor;
+
+    /**
+     * Authenticates the specified request by checking certificate validity.
+     *
+     * @param certificates the client certificates
+     * @return an authentication response
+     * @throws IllegalArgumentException the request did not contain a valid certificate (or no certificate)
+     */
+    public AuthenticationResponse authenticate(final X509Certificate[] certificates) throws IllegalArgumentException {
+        // ensure the cert was found
+        if (certificates == null || certificates.length == 0) {
+            throw new IllegalArgumentException("The specified request does not contain a client certificate.");
+        }
+
+        // extract the principal
+        final Object certificatePrincipal = principalExtractor.extractPrincipal(certificates[0]);
+        final String principal = certificatePrincipal.toString();
+
+        try {
+            certificateValidator.validateClientCertificate(certificates);
+        } catch (CertificateExpiredException cee) {
+            final String message = String.format("Client certificate for (%s) is expired.", principal);
+            logger.info(message, cee);
+            if (logger.isDebugEnabled()) {
+                logger.debug("", cee);
+            }
+            throw new IllegalArgumentException(message, cee);
+        } catch (CertificateNotYetValidException cnyve) {
+            final String message = String.format("Client certificate for (%s) is not yet valid.", principal);
+            logger.info(message, cnyve);
+            if (logger.isDebugEnabled()) {
+                logger.debug("", cnyve);
+            }
+            throw new IllegalArgumentException(message, cnyve);
+        } catch (final Exception e) {
+            logger.info(e.getMessage());
+            if (logger.isDebugEnabled()) {
+                logger.debug("", e);
+            }
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+
+        // build the authentication response
+        return new AuthenticationResponse(principal, principal, TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS));
+    }
+
+    /* setters */
+    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
+        this.certificateValidator = certificateValidator;
+    }
+
+    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
+        this.principalExtractor = principalExtractor;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.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/x509/ocsp/OcspCertificateValidator.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
index 81e4bd6..832a63c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
@@ -42,7 +42,6 @@ import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 import javax.security.auth.x500.X500Principal;
-import javax.servlet.http.HttpServletRequest;
 import org.apache.nifi.framework.security.util.SslContextFactory;
 import org.apache.nifi.web.security.x509.ocsp.OcspStatus.ValidationStatus;
 import org.apache.nifi.web.security.x509.ocsp.OcspStatus.VerificationStatus;
@@ -158,8 +157,7 @@ public class OcspCertificateValidator {
     }
 
     /**
-     * Loads the trusted certificate authorities according to the specified
-     * properties.
+     * Loads the trusted certificate authorities according to the specified properties.
      *
      * @param properties properties
      * @return map of certificate authorities
@@ -208,12 +206,10 @@ public class OcspCertificateValidator {
     /**
      * Validates the specified certificate using OCSP if configured.
      *
-     * @param request http request
+     * @param certificates the client certificates
      * @throws CertificateStatusException ex
      */
-    public void validate(final HttpServletRequest request) throws CertificateStatusException {
-        final X509Certificate[] certificates = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
-
+    public void validate(final X509Certificate[] certificates) throws CertificateStatusException {
         // only validate if configured to do so
         if (client != null && certificates != null && certificates.length > 0) {
             final X509Certificate subjectCertificate = getSubjectCertificate(certificates);
@@ -395,13 +391,9 @@ public class OcspCertificateValidator {
     }
 
     /**
-     * Gets the trusted responder certificate. The response contains the
-     * responder certificate, however we cannot blindly trust it. Instead, we
-     * use a configured trusted CA. If the responder certificate is a trusted
-     * CA, then we can use it. If the responder certificate is not directly
-     * trusted, we still may be able to trust it if it was issued by the same CA
-     * that issued the subject certificate. Other various checks may be required
-     * (this portion is currently not implemented).
+     * Gets the trusted responder certificate. The response contains the responder certificate, however we cannot blindly trust it. Instead, we use a configured trusted CA. If the responder
+     * certificate is a trusted CA, then we can use it. If the responder certificate is not directly trusted, we still may be able to trust it if it was issued by the same CA that issued the subject
+     * certificate. Other various checks may be required (this portion is currently not implemented).
      *
      * @param responderCertificate cert
      * @param issuerCertificate cert

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
index c88d303..40f678c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
@@ -33,6 +33,12 @@
         <property name="ocspValidator" ref="ocspValidator"/> 
     </bean>
     
+    <!-- x509 identity provider -->
+    <bean id="certificateIdentityProvider" class="org.apache.nifi.web.security.x509.X509IdentityProvider">
+        <property name="principalExtractor" ref="principalExtractor"/>
+        <property name="certificateValidator" ref="certificateValidator"/>
+    </bean>
+    
     <!-- user details service -->
     <bean id="userDetailsService" class="org.apache.nifi.web.security.authorization.NiFiAuthorizationService">
         <property name="userService" ref="userService"/>

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
index 5a1e859..5456552 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
@@ -187,7 +187,7 @@ public class NiFiAuthorizationServiceTest {
      *
      * @throws Exception ex
      */
-    @Test(expected = UsernameNotFoundException.class)
+    @Test(expected = UntrustedProxyException.class)
     public void testProxyNotFound() throws Exception {
         try {
             authorizationService.loadUserDetails(createRequestAuthentication(USER, PROXY_NOT_FOUND));

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
index b61988f..89db6ce 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
@@ -27,8 +27,6 @@ nf.Login = (function () {
 
     var config = {
         urls: {
-            registrationStatus: '../nifi-api/registration/status',
-            registration: '../nifi-api/registration',
             identity: '../nifi-api/controller/identity',
             users: '../nifi-api/controller/users',
             token: '../nifi-api/access/token',

http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
index 501eb58..64c48bf 100644
--- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
+++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/AbstractLdapProvider.java
@@ -84,9 +84,9 @@ public abstract class AbstractLdapProvider implements LoginIdentityProvider {
             // attempt to get the ldap user details to get the DN
             if (authentication.getPrincipal() instanceof LdapUserDetails) {
                 final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
-                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername());
+                return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration);
             } else {
-                return new AuthenticationResponse(authentication.getName(), credentials.getUsername());
+                return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration);
             }
         } catch (final CommunicationException | AuthenticationServiceException e) {
             logger.error(e.getMessage());
@@ -100,11 +100,6 @@ public abstract class AbstractLdapProvider implements LoginIdentityProvider {
     }
 
     @Override
-    public long getExpiration() {
-        return expiration;
-    }
-
-    @Override
     public final void preDestruction() throws ProviderDestructionException {
     }