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;
+ }
+
+}