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/11/09 16:52:29 UTC

nifi git commit: NIFI-655: - Updating packages for log in filters. - Handling new registration exceptions. - Code clean up.

Repository: nifi
Updated Branches:
  refs/heads/NIFI-655 1350483d3 -> efa1939fc


NIFI-655:
- Updating packages for log in filters.
- Handling new registration exceptions.
- Code clean up.

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

Branch: refs/heads/NIFI-655
Commit: efa1939fc551f4d02de61718e72e1bc896320350
Parents: 1350483
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Nov 9 10:52:18 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Nov 9 10:52:18 2015 -0500

----------------------------------------------------------------------
 .../nifi/authorized/users/AuthorizedUsers.java  |   8 +-
 .../web/NiFiWebApiSecurityConfiguration.java    |   4 +-
 .../form/LoginAuthenticationFilter.java         | 236 -------------------
 .../web/security/form/RegistrationFilter.java   | 158 -------------
 .../login/LoginAuthenticationFilter.java        | 236 +++++++++++++++++++
 .../web/security/login/RegistrationFilter.java  | 163 +++++++++++++
 6 files changed, 405 insertions(+), 400 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/efa1939f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
index 98922e7..abdd48e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorized-users/src/main/java/org/apache/nifi/authorized/users/AuthorizedUsers.java
@@ -130,7 +130,7 @@ public final class AuthorizedUsers {
      * @return The user identity
      */
     public String getUserIdentity(final NiFiUser user) {
-        if (User.class.isAssignableFrom(user.getClass())) {
+        if (user instanceof User) {
             return ((User) user).getDn();
         } else {
             return ((LoginUser) user).getUsername();
@@ -233,7 +233,7 @@ public final class AuthorizedUsers {
 
         // create the user
         final NiFiUser newUser = creator.createUser();
-        if (User.class.isAssignableFrom(newUser.getClass())) {
+        if (newUser instanceof User) {
             users.getUser().add((User) newUser);
         } else {
             users.getLoginUser().add((LoginUser) newUser);
@@ -323,7 +323,7 @@ public final class AuthorizedUsers {
 
         // find the desired user
         final NiFiUser user = finder.findUser(nifiUsers);
-        if (User.class.isAssignableFrom(user.getClass())) {
+        if (user instanceof User) {
             users.getUser().remove((User) user);
         } else {
             users.getLoginUser().remove((LoginUser) user);
@@ -350,7 +350,7 @@ public final class AuthorizedUsers {
         // find the desired user
         final List<NiFiUser> usersToRemove = finder.findUsers(nifiUsers);
         for (final NiFiUser user : usersToRemove) {
-            if (User.class.isAssignableFrom(user.getClass())) {
+            if (user instanceof User) {
                 users.getUser().remove((User) user);
             } else {
                 users.getLoginUser().remove((LoginUser) user);

http://git-wip-us.apache.org/repos/asf/nifi/blob/efa1939f/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
index bdc6ebc..4fb3501 100644
--- 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
@@ -24,8 +24,8 @@ 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.RegistrationStatusFilter;
-import org.apache.nifi.web.security.form.LoginAuthenticationFilter;
-import org.apache.nifi.web.security.form.RegistrationFilter;
+import org.apache.nifi.web.security.login.LoginAuthenticationFilter;
+import org.apache.nifi.web.security.login.RegistrationFilter;
 import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
 import org.apache.nifi.web.security.jwt.JwtService;
 import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;

http://git-wip-us.apache.org/repos/asf/nifi/blob/efa1939f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.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/LoginAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java
deleted file mode 100644
index 4848801..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/LoginAuthenticationFilter.java
+++ /dev/null
@@ -1,236 +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.form;
-
-import org.apache.nifi.web.security.token.LoginAuthenticationToken;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.util.List;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.nifi.authentication.LoginCredentials;
-import org.apache.nifi.authentication.LoginIdentityProvider;
-import org.apache.nifi.authentication.exception.IdentityAccessException;
-import org.apache.nifi.util.StringUtils;
-import org.apache.nifi.web.security.ProxiedEntitiesUtils;
-import org.apache.nifi.web.security.jwt.JwtService;
-import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
-import org.apache.nifi.web.security.x509.X509CertificateExtractor;
-import org.apache.nifi.web.security.x509.X509CertificateValidator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.authentication.BadCredentialsException;
-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.UsernameNotFoundException;
-import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
-import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
-
-/**
- * Exchanges a successful login with the configured provider for a ID token for accessing the API.
- */
-public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
-
-    private static final Logger logger = LoggerFactory.getLogger(LoginAuthenticationFilter.class);
-
-    private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
-
-    private X509CertificateValidator certificateValidator;
-    private X509CertificateExtractor certificateExtractor;
-    private X509PrincipalExtractor principalExtractor;
-
-    private LoginIdentityProvider loginIdentityProvider;
-    private JwtService jwtService;
-
-    public LoginAuthenticationFilter(final String defaultFilterProcessesUrl) {
-        super(defaultFilterProcessesUrl);
-
-        // do not continue filter chain... simply exchanging authentication for token
-        setContinueChainBeforeSuccessfulAuthentication(false);
-    }
-
-    @Override
-    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
-        // only suppport login when running securely
-        if (!request.isSecure()) {
-            return null;
-        }
-
-        // look for the credentials in the request
-        final LoginCredentials credentials = getLoginCredentials(request);
-
-        // if the credentials were not part of the request, attempt to log in with the certificate in the request
-        if (credentials == null) {
-            // look for a certificate
-            final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
-
-            // if there is no certificate, look for an existing token
-            if (certificate == null) {
-                // if not configured for login, don't consider existing tokens
-                if (loginIdentityProvider == null) {
-                    throw new BadCredentialsException("Login not supported.");
-                }
-
-                final String principal = jwtService.getAuthentication(request);
-
-                if (principal == null) {
-                    throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request.");
-                }
-
-                final LoginCredentials tokenCredentials = new LoginCredentials(principal, null);
-                return new LoginAuthenticationToken(tokenCredentials);
-            } else {
-                // extract the principal
-                final String principal = principalExtractor.extractPrincipal(certificate).toString();
-
-                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;
-                }
-
-                // authorize the proxy if necessary
-                authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, principal));
-
-                final LoginCredentials preAuthenticatedCredentials = new LoginCredentials(principal, null);
-                return new LoginAuthenticationToken(preAuthenticatedCredentials);
-            }
-        } else {
-            // if not configuration for login, don't consider credentials
-            if (loginIdentityProvider == null) {
-                throw new BadCredentialsException("Login not supported.");
-            }
-
-            try {
-                if (loginIdentityProvider.authenticate(credentials)) {
-                    return new LoginAuthenticationToken(credentials);
-                } else {
-                    throw new BadCredentialsException("The supplied username and password are not valid.");
-                }
-            } catch (final IdentityAccessException iae) {
-                throw new AuthenticationServiceException(iae.getMessage(), iae);
-            }
-        }
-    }
-
-    /**
-     * Ensures the proxyChain is authorized before allowing the user to be authenticated.
-     *
-     * @param proxyChain the proxy chain
-     * @throws AuthenticationException if the proxy chain is not authorized
-     */
-    private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
-        if (proxyChain.size() > 1) {
-            try {
-                userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
-            } catch (final UsernameNotFoundException unfe) {
-                // if a username not found exception was thrown, the proxies were authorized and now
-                // we can issue a new ID token to the end user
-            } catch (final Exception e) {
-                // any other issue we're going to treat as an authentication exception which will return 401
-                throw new AuthenticationException(e.getMessage(), e) {
-                };
-            }
-        }
-    }
-
-    private LoginCredentials getLoginCredentials(HttpServletRequest request) {
-        final String username = request.getParameter("username");
-        final String password = request.getParameter("password");
-
-        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
-            return null;
-        } else {
-            return new LoginCredentials(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 {
-        response.setContentType("text/plain");
-
-        final PrintWriter out = response.getWriter();
-        out.println(failed.getMessage());
-
-        if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) {
-            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-        } else if (failed instanceof AuthenticationServiceException) {
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        } else {
-            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-        }
-    }
-
-    public void setJwtService(JwtService jwtService) {
-        this.jwtService = jwtService;
-    }
-
-    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
-        this.loginIdentityProvider = loginIdentityProvider;
-    }
-
-    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
-        this.certificateValidator = certificateValidator;
-    }
-
-    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
-        this.certificateExtractor = certificateExtractor;
-    }
-
-    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
-        this.principalExtractor = principalExtractor;
-    }
-
-    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
-        this.userDetailsService = userDetailsService;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/efa1939f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/RegistrationFilter.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/RegistrationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/RegistrationFilter.java
deleted file mode 100644
index ea54127..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/form/RegistrationFilter.java
+++ /dev/null
@@ -1,158 +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.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.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.authentication.LoginCredentials;
-import org.apache.nifi.authentication.LoginIdentityProvider;
-import org.apache.nifi.authentication.exception.IdentityAccessException;
-import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
-import org.apache.nifi.util.StringUtils;
-import org.apache.nifi.web.security.jwt.JwtService;
-import org.apache.nifi.web.security.token.LoginAuthenticationToken;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-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.AbstractAuthenticationProcessingFilter;
-
-/**
- * Exchanges a successful login with the configured provider for a ID token for accessing the API.
- */
-public class RegistrationFilter extends AbstractAuthenticationProcessingFilter {
-
-    private static final Logger logger = LoggerFactory.getLogger(RegistrationFilter.class);
-
-    private LoginIdentityProvider loginIdentityProvider;
-    private JwtService jwtService;
-    private UserService userService;
-
-    public RegistrationFilter(final String defaultFilterProcessesUrl) {
-        super(defaultFilterProcessesUrl);
-
-        // do not continue filter chain... simply exchanging authentication for token
-        setContinueChainBeforeSuccessfulAuthentication(false);
-    }
-
-    @Override
-    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
-        // only suppport registration when running securely
-        if (!request.isSecure()) {
-            return null;
-        }
-
-        // look for the credentials in the request
-        final LoginCredentials credentials = getLoginCredentials(request);
-
-        // if the credentials were not part of the request, attempt to log in with the certificate in the request
-        if (credentials == null) {
-            throw new UsernameNotFoundException("User login credentials not found in request.");
-        } else {
-            try {
-                // attempt to register the user
-                loginIdentityProvider.register(credentials);
-            } catch (final IdentityAlreadyExistsException iaee) {
-                // if the identity already exists, try to create the nifi account request
-            } catch (final IdentityAccessException iae) {
-                throw new AuthenticationServiceException(iae.getMessage(), iae);
-            }
-
-            try {
-                // see if the account already exists so we're able to return the current status
-                userService.checkAuthorization(credentials.getUsername());
-
-                // account exists and is valid
-                throw new AccountStatusException(String.format("An account for %s already exists.", credentials.getUsername())) {
-                };
-            } catch (AdministrationException ase) {
-                throw new AuthenticationServiceException(ase.getMessage(), ase);
-            } catch (AccountDisabledException | AccountPendingException e) {
-                throw new AccountStatusException(e.getMessage(), e) {
-                };
-            } catch (AccountNotFoundException anfe) {
-                // create the pending user account
-                userService.createPendingUserAccount(credentials.getUsername(), request.getParameter("justification"));
-
-                // create the login token
-                return new LoginAuthenticationToken(credentials);
-            }
-        }
-    }
-
-    private LoginCredentials getLoginCredentials(HttpServletRequest request) {
-        final String username = request.getParameter("username");
-        final String password = request.getParameter("password");
-
-        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
-            return null;
-        } else {
-            return new LoginCredentials(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 {
-        response.setContentType("text/plain");
-
-        final PrintWriter out = response.getWriter();
-        out.println(failed.getMessage());
-
-        // set the appropriate response status
-        if (failed instanceof UsernameNotFoundException) {
-            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-        } else if (failed instanceof AccountStatusException) {
-            // account exists (maybe valid, pending, revoked)
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-        } else {
-            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        }
-    }
-
-    public void setJwtService(JwtService jwtService) {
-        this.jwtService = jwtService;
-    }
-
-    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
-        this.loginIdentityProvider = loginIdentityProvider;
-    }
-
-    public void setUserService(UserService userService) {
-        this.userService = userService;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/efa1939f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.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/login/LoginAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java
new file mode 100644
index 0000000..39f2782
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/LoginAuthenticationFilter.java
@@ -0,0 +1,236 @@
+/*
+ * 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.login;
+
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.nifi.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.util.StringUtils;
+import org.apache.nifi.web.security.ProxiedEntitiesUtils;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
+import org.apache.nifi.web.security.x509.X509CertificateExtractor;
+import org.apache.nifi.web.security.x509.X509CertificateValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+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.UsernameNotFoundException;
+import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
+
+/**
+ * Exchanges a successful login with the configured provider for a ID token for accessing the API.
+ */
+public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(LoginAuthenticationFilter.class);
+
+    private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
+
+    private X509CertificateValidator certificateValidator;
+    private X509CertificateExtractor certificateExtractor;
+    private X509PrincipalExtractor principalExtractor;
+
+    private LoginIdentityProvider loginIdentityProvider;
+    private JwtService jwtService;
+
+    public LoginAuthenticationFilter(final String defaultFilterProcessesUrl) {
+        super(defaultFilterProcessesUrl);
+
+        // do not continue filter chain... simply exchanging authentication for token
+        setContinueChainBeforeSuccessfulAuthentication(false);
+    }
+
+    @Override
+    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
+        // only suppport login when running securely
+        if (!request.isSecure()) {
+            return null;
+        }
+
+        // look for the credentials in the request
+        final LoginCredentials credentials = getLoginCredentials(request);
+
+        // if the credentials were not part of the request, attempt to log in with the certificate in the request
+        if (credentials == null) {
+            // look for a certificate
+            final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
+
+            // if there is no certificate, look for an existing token
+            if (certificate == null) {
+                // if not configured for login, don't consider existing tokens
+                if (loginIdentityProvider == null) {
+                    throw new BadCredentialsException("Login not supported.");
+                }
+
+                final String principal = jwtService.getAuthentication(request);
+
+                if (principal == null) {
+                    throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request.");
+                }
+
+                final LoginCredentials tokenCredentials = new LoginCredentials(principal, null);
+                return new LoginAuthenticationToken(tokenCredentials);
+            } else {
+                // extract the principal
+                final String principal = principalExtractor.extractPrincipal(certificate).toString();
+
+                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;
+                }
+
+                // authorize the proxy if necessary
+                authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, principal));
+
+                final LoginCredentials preAuthenticatedCredentials = new LoginCredentials(principal, null);
+                return new LoginAuthenticationToken(preAuthenticatedCredentials);
+            }
+        } else {
+            // if not configuration for login, don't consider credentials
+            if (loginIdentityProvider == null) {
+                throw new BadCredentialsException("Login not supported.");
+            }
+
+            try {
+                if (loginIdentityProvider.authenticate(credentials)) {
+                    return new LoginAuthenticationToken(credentials);
+                } else {
+                    throw new BadCredentialsException("The supplied username and password are not valid.");
+                }
+            } catch (final IdentityAccessException iae) {
+                throw new AuthenticationServiceException(iae.getMessage(), iae);
+            }
+        }
+    }
+
+    /**
+     * Ensures the proxyChain is authorized before allowing the user to be authenticated.
+     *
+     * @param proxyChain the proxy chain
+     * @throws AuthenticationException if the proxy chain is not authorized
+     */
+    private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
+        if (proxyChain.size() > 1) {
+            try {
+                userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
+            } catch (final UsernameNotFoundException unfe) {
+                // if a username not found exception was thrown, the proxies were authorized and now
+                // we can issue a new ID token to the end user
+            } catch (final Exception e) {
+                // any other issue we're going to treat as an authentication exception which will return 401
+                throw new AuthenticationException(e.getMessage(), e) {
+                };
+            }
+        }
+    }
+
+    private LoginCredentials getLoginCredentials(HttpServletRequest request) {
+        final String username = request.getParameter("username");
+        final String password = request.getParameter("password");
+
+        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+            return null;
+        } else {
+            return new LoginCredentials(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 {
+        response.setContentType("text/plain");
+
+        final PrintWriter out = response.getWriter();
+        out.println(failed.getMessage());
+
+        if (failed instanceof BadCredentialsException || failed instanceof AuthenticationCredentialsNotFoundException) {
+            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        } else if (failed instanceof AuthenticationServiceException) {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        } else {
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        }
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
+    public void setCertificateValidator(X509CertificateValidator certificateValidator) {
+        this.certificateValidator = certificateValidator;
+    }
+
+    public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
+        this.certificateExtractor = certificateExtractor;
+    }
+
+    public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {
+        this.principalExtractor = principalExtractor;
+    }
+
+    public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/efa1939f/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/RegistrationFilter.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/login/RegistrationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/RegistrationFilter.java
new file mode 100644
index 0000000..8a3f02e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/login/RegistrationFilter.java
@@ -0,0 +1,163 @@
+/*
+ * 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.login;
+
+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.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.authentication.LoginCredentials;
+import org.apache.nifi.authentication.LoginIdentityProvider;
+import org.apache.nifi.authentication.exception.IdentityAccessException;
+import org.apache.nifi.authentication.exception.IdentityRegistrationException;
+import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
+import org.apache.nifi.util.StringUtils;
+import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.token.LoginAuthenticationToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AccountStatusException;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+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.AbstractAuthenticationProcessingFilter;
+
+/**
+ * Exchanges a successful login with the configured provider for a ID token for accessing the API.
+ */
+public class RegistrationFilter extends AbstractAuthenticationProcessingFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(RegistrationFilter.class);
+
+    private LoginIdentityProvider loginIdentityProvider;
+    private JwtService jwtService;
+    private UserService userService;
+
+    public RegistrationFilter(final String defaultFilterProcessesUrl) {
+        super(defaultFilterProcessesUrl);
+
+        // do not continue filter chain... simply exchanging authentication for token
+        setContinueChainBeforeSuccessfulAuthentication(false);
+    }
+
+    @Override
+    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
+        // only suppport registration when running securely
+        if (!request.isSecure()) {
+            return null;
+        }
+
+        // look for the credentials in the request
+        final LoginCredentials credentials = getLoginCredentials(request);
+
+        // if the credentials were not part of the request, attempt to log in with the certificate in the request
+        if (credentials == null) {
+            throw new UsernameNotFoundException("User login credentials not found in request.");
+        } else {
+            try {
+                // attempt to register the user
+                loginIdentityProvider.register(credentials);
+            } catch (final IdentityAlreadyExistsException iaee) {
+                // if the identity already exists, try to create the nifi account request
+            } catch (final IdentityRegistrationException ire) {
+                // the credentials are not acceptable for some reason
+                throw new BadCredentialsException(ire.getMessage(), ire);
+            } catch (final IdentityAccessException iae) {
+                throw new AuthenticationServiceException(iae.getMessage(), iae);
+            }
+
+            try {
+                // see if the account already exists so we're able to return the current status
+                userService.checkAuthorization(credentials.getUsername());
+
+                // account exists and is valid
+                throw new AccountStatusException(String.format("An account for %s already exists.", credentials.getUsername())) {
+                };
+            } catch (AdministrationException ase) {
+                throw new AuthenticationServiceException(ase.getMessage(), ase);
+            } catch (AccountDisabledException | AccountPendingException e) {
+                throw new AccountStatusException(e.getMessage(), e) {
+                };
+            } catch (AccountNotFoundException anfe) {
+                // create the pending user account
+                userService.createPendingUserAccount(credentials.getUsername(), request.getParameter("justification"));
+
+                // create the login token
+                return new LoginAuthenticationToken(credentials);
+            }
+        }
+    }
+
+    private LoginCredentials getLoginCredentials(HttpServletRequest request) {
+        final String username = request.getParameter("username");
+        final String password = request.getParameter("password");
+
+        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+            return null;
+        } else {
+            return new LoginCredentials(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 {
+        response.setContentType("text/plain");
+
+        final PrintWriter out = response.getWriter();
+        out.println(failed.getMessage());
+
+        // set the appropriate response status
+        if (failed instanceof UsernameNotFoundException || failed instanceof BadCredentialsException) {
+            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+        } else if (failed instanceof AccountStatusException) {
+            // account exists (maybe valid, pending, revoked)
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        } else {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    public void setJwtService(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
+        this.loginIdentityProvider = loginIdentityProvider;
+    }
+
+    public void setUserService(UserService userService) {
+        this.userService = userService;
+    }
+
+}