From 28757945194ce95b4a8e66a59149bcce472e9a94 Mon Sep 17 00:00:00 2001 From: Clemens Rabe Date: Wed, 2 Oct 2013 19:45:21 +0200 Subject: [PATCH] Added auto-login filter system. --- .../sonia/scm/web/filter/AutoLoginFilter.java | 137 +++++ .../sonia/scm/web/filter/AutoLoginModule.java | 64 +++ .../web/filter/BasicAuthenticationFilter.java | 353 ++++++------ .../main/java/sonia/scm/ScmServletModule.java | 4 +- .../ApiBasicAuthenticationFilter.java | 8 +- .../security/ChainAuthenticatonManager.java | 513 +++++++++--------- 6 files changed, 615 insertions(+), 464 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/web/filter/AutoLoginFilter.java create mode 100644 scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModule.java diff --git a/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginFilter.java new file mode 100644 index 0000000000..412a4db515 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginFilter.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2013, Clemens Rabe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +package sonia.scm.web.filter; + +import java.io.IOException; +import java.util.Set; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.user.User; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +/** + * This filter calls all AutoLoginModule objects to try an auto-login. It can be + * used on its own, usually at the global context ('/*') or as a base class like + * BasicAuthenticationFilter. + * + * @author Clemens Rabe + */ +@Singleton +public class AutoLoginFilter extends HttpFilter { + + /** the logger for AutoLoginFilter */ + private static final Logger logger = LoggerFactory + .getLogger(AutoLoginFilter.class); + + @Deprecated + public AutoLoginFilter() { + } + + /** + * Constructor. + * + * @param autoLoginModules + * - The auto-login modules. + */ + @Inject + public AutoLoginFilter(Set autoLoginModules) { + this.autoLoginModules = autoLoginModules; + } + + @Override + protected void doFilter(HttpServletRequest request, + HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + User user = getAuthenticatedUser(request, response); + + if (user == null) + chain.doFilter(request, response); + else + chain.doFilter( + new SecurityHttpServletRequestWrapper(request, user), + response); + } + + /** + * Check all known AutoLoginModule objects to authenticate the user using + * the current request. + * + * @param request + * - The servlet request. + * @param response + * - The servlet response. + * @return The user or null if no user was found. + */ + protected User getAuthenticatedUser(HttpServletRequest request, + HttpServletResponse response) { + Subject subject = SecurityUtils.getSubject(); + User user = null; + + if (subject.isAuthenticated() || subject.isRemembered()) { + if (logger.isTraceEnabled()) { + logger.trace("user is allready authenticated"); + } + + user = subject.getPrincipals().oneByType(User.class); + } else { + // Try the known filters first + for (AutoLoginModule filter : autoLoginModules) { + user = filter.authenticate(request, response, subject); + + if (user != null) { + if (logger.isTraceEnabled()) { + logger.trace( + "user {} successfully authenticated by authentication filter", + user.getName()); + } + break; + } + } + } + + return user; + } + + /** + * Set of AutoLoginModule objects. + */ + private Set autoLoginModules; + +} diff --git a/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModule.java b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModule.java new file mode 100644 index 0000000000..d5b70a69d9 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModule.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2013, Clemens Rabe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +package sonia.scm.web.filter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.shiro.subject.Subject; + +import sonia.scm.plugin.ExtensionPoint; +import sonia.scm.user.User; + +/** + * Classes implementing this interface are called by the + * BasicAuthenticationFilter before the default basic authentication takes + * place. This allows to implement auto-login methods. + * + * @author Clemens Rabe + */ +@ExtensionPoint +public interface AutoLoginModule { + + /** + * Authenticate a user using the given request object. If the user can not + * be authenticated, e.g., because required headers are not set null must be + * returned. + * + * @param request + * The HTTP request. + * @param response + * The HTTP response. Use only if absolutely necessary. + * @param subject + * The subject object. + * @return Return a User object or null. + */ + public User authenticate(HttpServletRequest request, + HttpServletResponse response, Subject subject); +} diff --git a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java index bb41ff9145..e1953cae96 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java @@ -29,8 +29,6 @@ * */ - - package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- @@ -43,7 +41,6 @@ import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,6 +49,7 @@ import sonia.scm.config.ScmConfiguration; import sonia.scm.user.User; import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; +import sonia.scm.web.security.AuthenticationHandler; import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ @@ -59,6 +57,7 @@ import sonia.scm.web.security.WebSecurityContext; import com.sun.jersey.core.util.Base64; import java.io.IOException; +import java.util.Set; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -66,221 +65,195 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** - * + * * @author Sebastian Sdorra */ @Singleton -public class BasicAuthenticationFilter extends HttpFilter -{ +public class BasicAuthenticationFilter extends AutoLoginFilter { - /** Field description */ - public static final String AUTHORIZATION_BASIC_PREFIX = "BASIC"; + /** Field description */ + public static final String AUTHORIZATION_BASIC_PREFIX = "BASIC"; - /** Field description */ - public static final String CREDENTIAL_SEPARATOR = ":"; + /** Field description */ + public static final String CREDENTIAL_SEPARATOR = ":"; - /** Field description */ - public static final String HEADER_AUTHORIZATION = "Authorization"; + /** Field description */ + public static final String HEADER_AUTHORIZATION = "Authorization"; - /** the logger for BasicAuthenticationFilter */ - private static final Logger logger = - LoggerFactory.getLogger(BasicAuthenticationFilter.class); + /** the logger for BasicAuthenticationFilter */ + private static final Logger logger = LoggerFactory + .getLogger(BasicAuthenticationFilter.class); - //~--- constructors --------------------------------------------------------- + // ~--- constructors + // --------------------------------------------------------- - /** - * Constructs ... - * - * - * @param securityContextProvider - * @deprecated use the constructor with out arguments instead. - */ - @Deprecated - public BasicAuthenticationFilter( - Provider securityContextProvider) {} + /** + * Constructs ... + * + * + * @param securityContextProvider + * @deprecated use the constructor with out arguments instead. + */ + @Deprecated + public BasicAuthenticationFilter( + Provider securityContextProvider) { + } - /** - * Constructs a new basic authenticaton filter - * - * @param configuration scm-manager global configuration - * - * @since 1.21 - */ - @Inject - public BasicAuthenticationFilter(ScmConfiguration configuration) - { - this.configuration = configuration; - } + /** + * Constructs a new basic authenticaton filter + * + * @param configuration + * scm-manager global configuration + * + * @since 1.21 + */ + @Inject + public BasicAuthenticationFilter(ScmConfiguration configuration, + Set autoLoginModules) { + super(autoLoginModules); + this.configuration = configuration; + } - //~--- methods -------------------------------------------------------------- + // ~--- methods + // -------------------------------------------------------------- - /** - * Method description - * - * - * @param request - * @param response - * @param chain - * - * @throws IOException - * @throws ServletException - */ - @Override - protected void doFilter(HttpServletRequest request, - HttpServletResponse response, FilterChain chain) - throws IOException, ServletException - { - Subject subject = SecurityUtils.getSubject(); + /** + * Method description + * + * + * @param request + * @param response + * @param chain + * + * @throws IOException + * @throws ServletException + */ + @Override + protected void doFilter(HttpServletRequest request, + HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + Subject subject = SecurityUtils.getSubject(); + User user = getAuthenticatedUser(request, response); - User user = null; - String authentication = request.getHeader(HEADER_AUTHORIZATION); + // Fallback to basic authentication scheme + if (user == null) { + String authentication = request.getHeader(HEADER_AUTHORIZATION); - if (Util.startWithIgnoreCase(authentication, AUTHORIZATION_BASIC_PREFIX)) - { - if (logger.isTraceEnabled()) - { - logger.trace("found basic authorization header, start authentication"); - } + if (Util.startWithIgnoreCase(authentication, + AUTHORIZATION_BASIC_PREFIX)) { + if (logger.isTraceEnabled()) { + logger.trace("found basic authorization header, start authentication"); + } - user = authenticate(request, response, subject, authentication); + user = authenticate(request, response, subject, authentication); - if (logger.isTraceEnabled()) - { - if (user != null) - { - logger.trace("user {} successfully authenticated", user.getName()); - } - else - { - logger.trace("authentcation failed, user object is null"); - } - } - } - else if (subject.isAuthenticated() || subject.isRemembered()) - { - if (logger.isTraceEnabled()) - { - logger.trace("user is allready authenticated"); - } + if (logger.isTraceEnabled()) { + if (user != null) { + logger.trace("user {} successfully authenticated", + user.getName()); + } else { + logger.trace("authentcation failed, user object is null"); + } + } + } else if ((configuration != null) + && configuration.isAnonymousAccessEnabled()) { + if (logger.isTraceEnabled()) { + logger.trace("anonymous access granted"); + } - user = subject.getPrincipals().oneByType(User.class); - } - else if ((configuration != null) - && configuration.isAnonymousAccessEnabled()) - { - if (logger.isTraceEnabled()) - { - logger.trace("anonymous access granted"); - } + user = SCMContext.ANONYMOUS; + } + } - user = SCMContext.ANONYMOUS; - } + if (user == null) { + if (logger.isTraceEnabled()) { + logger.trace("could not find user send unauthorized"); + } - if (user == null) - { - if (logger.isTraceEnabled()) - { - logger.trace("could not find user send unauthorized"); - } + handleUnauthorized(request, response, chain); + } else { + chain.doFilter( + new SecurityHttpServletRequestWrapper(request, user), + response); + } + } - handleUnauthorized(request, response, chain); - } - else - { - chain.doFilter(new SecurityHttpServletRequestWrapper(request, user), - response); - } - } + /** + * Method description + * + * + * @param request + * @param response + * @param chain + * + * @throws IOException + * @throws ServletException + * + * @since 1.8 + */ + protected void handleUnauthorized(HttpServletRequest request, + HttpServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpUtil.sendUnauthorized(request, response); + } - /** - * Method description - * - * - * @param request - * @param response - * @param chain - * - * @throws IOException - * @throws ServletException - * - * @since 1.8 - */ - protected void handleUnauthorized(HttpServletRequest request, - HttpServletResponse response, FilterChain chain) - throws IOException, ServletException - { - HttpUtil.sendUnauthorized(request, response); - } + /** + * Method description + * + * + * @param request + * @param response + * @param securityContext + * @param subject + * @param authentication + * + * @return + */ + private User authenticate(HttpServletRequest request, + HttpServletResponse response, Subject subject, String authentication) { + String token = authentication.substring(6); - /** - * Method description - * - * - * @param request - * @param response - * @param securityContext - * @param subject - * @param authentication - * - * @return - */ - private User authenticate(HttpServletRequest request, - HttpServletResponse response, Subject subject, String authentication) - { - String token = authentication.substring(6); + token = new String(Base64.decode(token.getBytes())); - token = new String(Base64.decode(token.getBytes())); + int index = token.indexOf(CREDENTIAL_SEPARATOR); + User user = null; - int index = token.indexOf(CREDENTIAL_SEPARATOR); - User user = null; + if ((index > 0) && (index < token.length())) { + String username = token.substring(0, index); + String password = token.substring(index + 1); - if ((index > 0) && (index < token.length())) - { - String username = token.substring(0, index); - String password = token.substring(index + 1); + if (Util.isNotEmpty(username) && Util.isNotEmpty(password)) { + if (logger.isTraceEnabled()) { + logger.trace("try to authenticate user {}", username); + } - if (Util.isNotEmpty(username) && Util.isNotEmpty(password)) - { - if (logger.isTraceEnabled()) - { - logger.trace("try to authenticate user {}", username); - } + try { - try - { + subject.login(new UsernamePasswordToken(username, password, + request.getRemoteAddr())); + user = subject.getPrincipals().oneByType(User.class); + } catch (AuthenticationException ex) { + if (logger.isTraceEnabled()) { + logger.trace("authentication failed for user " + .concat(username), ex); + } else if (logger.isWarnEnabled()) { + logger.warn("authentication failed for user {}", + username); + } + } + } else if (logger.isWarnEnabled()) { + logger.warn("username or password is null/empty"); + } + } else if (logger.isWarnEnabled()) { + logger.warn("failed to read basic auth credentials"); + } - subject.login(new UsernamePasswordToken(username, password, - request.getRemoteAddr())); - user = subject.getPrincipals().oneByType(User.class); - } - catch (AuthenticationException ex) - { - if (logger.isTraceEnabled()) - { - logger.trace("authentication failed for user ".concat(username), - ex); - } - else if (logger.isWarnEnabled()) - { - logger.warn("authentication failed for user {}", username); - } - } - } - else if (logger.isWarnEnabled()) - { - logger.warn("username or password is null/empty"); - } - } - else if (logger.isWarnEnabled()) - { - logger.warn("failed to read basic auth credentials"); - } + return user; + } - return user; - } + // ~--- fields + // --------------------------------------------------------------- - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private ScmConfiguration configuration; + /** Field description */ + private ScmConfiguration configuration; } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 83c25b5b91..6d8b379c64 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -43,7 +43,6 @@ import com.google.inject.servlet.ServletModule; import com.google.inject.throwingproviders.ThrowingProviderBinder; import org.apache.shiro.authz.permission.PermissionResolver; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -130,6 +129,7 @@ import sonia.scm.util.DebugServlet; import sonia.scm.util.ScmConfigurationUtil; import sonia.scm.web.cgi.CGIExecutorFactory; import sonia.scm.web.cgi.DefaultCGIExecutorFactory; +import sonia.scm.web.filter.AutoLoginFilter; import sonia.scm.web.filter.LoggingFilter; import sonia.scm.web.security.AdministrationContext; import sonia.scm.web.security.ApiBasicAuthenticationFilter; @@ -141,6 +141,7 @@ import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ + import com.sun.jersey.api.core.PackagesResourceConfig; import com.sun.jersey.api.core.ResourceConfig; import com.sun.jersey.api.json.JSONConfiguration; @@ -347,6 +348,7 @@ public class ScmServletModule extends ServletModule * PATTERN_STATIC_RESOURCES).through(StaticResourceFilter.class); */ filter(PATTERN_ALL).through(BaseUrlFilter.class); + filter(PATTERN_ALL).through(AutoLoginFilter.class); filterRegex(RESOURCE_REGEX).through(GZipFilter.class); filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(ApiBasicAuthenticationFilter.class); diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/ApiBasicAuthenticationFilter.java b/scm-webapp/src/main/java/sonia/scm/web/security/ApiBasicAuthenticationFilter.java index 12e168b8ea..29285b68eb 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/security/ApiBasicAuthenticationFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/web/security/ApiBasicAuthenticationFilter.java @@ -39,11 +39,14 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import sonia.scm.config.ScmConfiguration; +import sonia.scm.web.filter.AutoLoginModule; import sonia.scm.web.filter.BasicAuthenticationFilter; //~--- JDK imports ------------------------------------------------------------ + import java.io.IOException; +import java.util.Set; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -76,9 +79,10 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter * @param configuration */ @Inject - public ApiBasicAuthenticationFilter(ScmConfiguration configuration) + public ApiBasicAuthenticationFilter(ScmConfiguration configuration, + Set autoLoginModules) { - super(configuration); + super(configuration, autoLoginModules); } //~--- methods -------------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java b/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java index e7ef7c43af..358ea76b29 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java +++ b/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java @@ -29,8 +29,6 @@ * */ - - package sonia.scm.web.security; //~--- non-JDK imports -------------------------------------------------------- @@ -66,323 +64,296 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** - * + * * @author Sebastian Sdorra */ @Singleton -public class ChainAuthenticatonManager extends AbstractAuthenticationManager -{ +public class ChainAuthenticatonManager extends AbstractAuthenticationManager { - /** Field description */ - public static final String CACHE_NAME = "sonia.cache.auth"; + /** Field description */ + public static final String CACHE_NAME = "sonia.cache.auth"; - /** the logger for ChainAuthenticatonManager */ - private static final Logger logger = - LoggerFactory.getLogger(ChainAuthenticatonManager.class); + /** the logger for ChainAuthenticatonManager */ + private static final Logger logger = LoggerFactory + .getLogger(ChainAuthenticatonManager.class); - //~--- constructors --------------------------------------------------------- + // ~--- constructors + // --------------------------------------------------------- - /** - * Constructs ... - * - * - * - * @param userManager - * @param authenticationHandlerSet - * @param encryptionHandler - * @param cacheManager - * @param authenticationListenerProvider - * @param authenticationListeners - */ - @Inject - public ChainAuthenticatonManager(UserManager userManager, - Set authenticationHandlerSet, - EncryptionHandler encryptionHandler, CacheManager cacheManager, - Set authenticationListeners) - { - AssertUtil.assertIsNotEmpty(authenticationHandlerSet); - AssertUtil.assertIsNotNull(cacheManager); - this.authenticationHandlers = sort(userManager, authenticationHandlerSet); - this.encryptionHandler = encryptionHandler; - this.cache = cacheManager.getCache(String.class, - AuthenticationCacheValue.class, CACHE_NAME); + /** + * Constructs ... + * + * + * + * @param userManager + * @param authenticationHandlerSet + * @param encryptionHandler + * @param cacheManager + * @param authenticationListenerProvider + * @param authenticationListeners + */ + @Inject + public ChainAuthenticatonManager(UserManager userManager, + Set authenticationHandlerSet, + EncryptionHandler encryptionHandler, CacheManager cacheManager, + Set authenticationListeners) { + AssertUtil.assertIsNotEmpty(authenticationHandlerSet); + AssertUtil.assertIsNotNull(cacheManager); + this.authenticationHandlers = sort(userManager, + authenticationHandlerSet); + this.encryptionHandler = encryptionHandler; + this.cache = cacheManager.getCache(String.class, + AuthenticationCacheValue.class, CACHE_NAME); - if (Util.isNotEmpty(authenticationListeners)) - { - addListeners(authenticationListeners); - } - } + if (Util.isNotEmpty(authenticationListeners)) { + addListeners(authenticationListeners); + } + } - //~--- methods -------------------------------------------------------------- + // ~--- methods + // -------------------------------------------------------------- - /** - * Method description - * - * - * @param request - * @param response - * @param username - * @param password - * - * @return - */ - @Override - public AuthenticationResult authenticate(HttpServletRequest request, - HttpServletResponse response, String username, String password) - { - AssertUtil.assertIsNotEmpty(username); - AssertUtil.assertIsNotEmpty(password); + /** + * Method description + * + * + * @param request + * @param response + * @param username + * @param password + * + * @return + */ + @Override + public AuthenticationResult authenticate(HttpServletRequest request, + HttpServletResponse response, String username, String password) { + AssertUtil.assertIsNotEmpty(username); + AssertUtil.assertIsNotEmpty(password); - String encryptedPassword = encryptionHandler.encrypt(password); - AuthenticationResult ar = getCached(username, encryptedPassword); + String encryptedPassword = encryptionHandler.encrypt(password); + AuthenticationResult ar = getCached(username, encryptedPassword); - if (ar == null) - { - if (logger.isTraceEnabled()) - { - logger.trace("no authentication result for user {} found in cache", - username); - } + if (ar == null) { + if (logger.isTraceEnabled()) { + logger.trace( + "no authentication result for user {} found in cache", + username); + } - ar = doAuthentication(request, response, username, password); + ar = doAuthentication(request, response, username, password); - if ((ar != null) && ar.isCacheable()) - { - cache.put(username, - new AuthenticationCacheValue(ar, encryptedPassword)); - } - } - else if (logger.isDebugEnabled()) - { - logger.debug("authenticate {} via cache", username); - } + if ((ar != null) && ar.isCacheable()) { + cache.put(username, new AuthenticationCacheValue(ar, + encryptedPassword)); + } + } else if (logger.isDebugEnabled()) { + logger.debug("authenticate {} via cache", username); + } - return ar; - } + return ar; + } - /** - * Method description - * - * - * @throws IOException - */ - @Override - public void close() throws IOException - { - for (AuthenticationHandler authenticator : authenticationHandlers) - { - if (logger.isTraceEnabled()) - { - logger.trace("close authenticator {}", authenticator.getClass()); - } + /** + * Method description + * + * + * @throws IOException + */ + @Override + public void close() throws IOException { + for (AuthenticationHandler authenticator : authenticationHandlers) { + if (logger.isTraceEnabled()) { + logger.trace("close authenticator {}", authenticator.getClass()); + } - IOUtil.close(authenticator); - } - } + IOUtil.close(authenticator); + } + } - /** - * Method description - * - * - * @param context - */ - @Override - public void init(SCMContextProvider context) - { - for (AuthenticationHandler authenticator : authenticationHandlers) - { - if (logger.isTraceEnabled()) - { - logger.trace("initialize authenticator {}", authenticator.getClass()); - } + /** + * Method description + * + * + * @param context + */ + @Override + public void init(SCMContextProvider context) { + for (AuthenticationHandler authenticator : authenticationHandlers) { + if (logger.isTraceEnabled()) { + logger.trace("initialize authenticator {}", + authenticator.getClass()); + } - authenticator.init(context); - } - } + authenticator.init(context); + } + } - /** - * Method description - * - * - * @param request - * @param response - * @param username - * @param password - * - * @return - */ - private AuthenticationResult doAuthentication(HttpServletRequest request, - HttpServletResponse response, String username, String password) - { - AuthenticationResult ar = null; + /** + * Method description + * + * + * @param request + * @param response + * @param username + * @param password + * + * @return + */ + private AuthenticationResult doAuthentication(HttpServletRequest request, + HttpServletResponse response, String username, String password) { + AuthenticationResult ar = null; - if (logger.isTraceEnabled()) - { - logger.trace("start authentication chain for user {}", username); - } + if (logger.isTraceEnabled()) { + logger.trace("start authentication chain for user {}", username); + } - for (AuthenticationHandler authenticator : authenticationHandlers) - { - if (logger.isTraceEnabled()) - { - logger.trace("check authenticator {} for user {}", - authenticator.getClass(), username); - } + for (AuthenticationHandler authenticator : authenticationHandlers) { + if (logger.isTraceEnabled()) { + logger.trace("check authenticator {} for user {}", + authenticator.getClass(), username); + } - try - { - AuthenticationResult result = authenticator.authenticate(request, - response, username, password); + try { + AuthenticationResult result = authenticator.authenticate( + request, response, username, password); - if (logger.isDebugEnabled()) - { - logger.debug("authenticator {} ends with result, {}", - authenticator.getClass().getName(), result); - } + if (logger.isDebugEnabled()) { + logger.debug("authenticator {} ends with result, {}", + authenticator.getClass().getName(), result); + } - if ((result != null) && (result.getState() != null) - && (result.getState().isSuccessfully() - || (result.getState() == AuthenticationState.FAILED))) - { - if (result.getState().isSuccessfully() && (result.getUser() != null)) - { - User user = result.getUser(); + // CR: Removed check on state=failed to allow next module to + // continue + if ((result != null) && (result.getState() != null) + && result.getState().isSuccessfully()) { + if (result.getUser() != null) { + User user = result.getUser(); - user.setType(authenticator.getType()); - ar = result; + user.setType(authenticator.getType()); + ar = result; - // notify authentication listeners - fireAuthenticationEvent(request, response, user); - } + // notify authentication listeners + fireAuthenticationEvent(request, response, user); + } - break; - } - } - catch (Exception ex) - { - logger.error( - "error durring authentication process of ".concat( - authenticator.getClass().getName()), ex); - } - } + break; + } + } catch (Exception ex) { + logger.error("error durring authentication process of " + .concat(authenticator.getClass().getName()), ex); + } + } - return ar; - } + return ar; + } - /** - * Method description - * - * - * @param userManager - * @param authenticationHandlerSet - * - * @return - */ - @VisibleForTesting - private List sort(UserManager userManager, - Set authenticationHandlerSet) - { - List handlers = - Lists.newArrayListWithCapacity(authenticationHandlerSet.size()); + /** + * Method description + * + * + * @param userManager + * @param authenticationHandlerSet + * + * @return + */ + @VisibleForTesting + private List sort(UserManager userManager, + Set authenticationHandlerSet) { + List handlers = Lists + .newArrayListWithCapacity(authenticationHandlerSet.size()); - String first = Strings.nullToEmpty(userManager.getDefaultType()); + String first = Strings.nullToEmpty(userManager.getDefaultType()); - for (AuthenticationHandler handler : authenticationHandlerSet) - { - if (first.equals(handler.getType())) - { - handlers.add(0, handler); - } - else - { - handlers.add(handler); - } - } + for (AuthenticationHandler handler : authenticationHandlerSet) { + if (first.equals(handler.getType())) { + handlers.add(0, handler); + } else { + handlers.add(handler); + } + } - return handlers; - } + return handlers; + } - //~--- get methods ---------------------------------------------------------- + // ~--- get methods + // ---------------------------------------------------------- - /** - * Method description - * - * - * @param username - * @param encryptedPassword - * - * @return - */ - private AuthenticationResult getCached(String username, - String encryptedPassword) - { - AuthenticationResult result = null; - AuthenticationCacheValue value = cache.get(username); + /** + * Method description + * + * + * @param username + * @param encryptedPassword + * + * @return + */ + private AuthenticationResult getCached(String username, + String encryptedPassword) { + AuthenticationResult result = null; + AuthenticationCacheValue value = cache.get(username); - if (value != null) - { - String cachedPassword = value.password; + if (value != null) { + String cachedPassword = value.password; - if (cachedPassword.equals(encryptedPassword)) - { - result = value.authenticationResult; - } - } + if (cachedPassword.equals(encryptedPassword)) { + result = value.authenticationResult; + } + } - return result; - } + return result; + } - //~--- inner classes -------------------------------------------------------- + // ~--- inner classes + // -------------------------------------------------------- - /** - * Class description - * - * - * @version Enter version here..., 2011-01-15 - * @author Sebastian Sdorra - */ - private static class AuthenticationCacheValue implements Serializable - { + /** + * Class description + * + * + * @version Enter version here..., 2011-01-15 + * @author Sebastian Sdorra + */ + private static class AuthenticationCacheValue implements Serializable { - /** Field description */ - private static final long serialVersionUID = 2201116145941277549L; + /** Field description */ + private static final long serialVersionUID = 2201116145941277549L; - //~--- constructors ------------------------------------------------------- + // ~--- constructors + // ------------------------------------------------------- - /** - * Constructs ... - * - * - * - * @param ar - * @param password - */ - public AuthenticationCacheValue(AuthenticationResult ar, String password) - { - this.authenticationResult = - new AuthenticationResult(ar.getUser().clone(), ar.getGroups(), - ar.getState()); - this.password = password; - } + /** + * Constructs ... + * + * + * + * @param ar + * @param password + */ + public AuthenticationCacheValue(AuthenticationResult ar, String password) { + this.authenticationResult = new AuthenticationResult(ar.getUser() + .clone(), ar.getGroups(), ar.getState()); + this.password = password; + } - //~--- fields ------------------------------------------------------------- + // ~--- fields + // ------------------------------------------------------------- - /** Field description */ - private AuthenticationResult authenticationResult; + /** Field description */ + private AuthenticationResult authenticationResult; - /** Field description */ - private String password; - } + /** Field description */ + private String password; + } + // ~--- fields + // --------------------------------------------------------------- - //~--- fields --------------------------------------------------------------- + /** Field description */ + private List authenticationHandlers; - /** Field description */ - private List authenticationHandlers; + /** Field description */ + private Cache cache; - /** Field description */ - private Cache cache; - - /** Field description */ - private EncryptionHandler encryptionHandler; + /** Field description */ + private EncryptionHandler encryptionHandler; }