From 920921d19c45df217466fc0c33786256db449bfd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 9 Oct 2014 13:22:42 +0200 Subject: [PATCH] implement util class for AutoLoginModules to mark request as completed or send redirects from an AutoLoginModule --- .../sonia/scm/web/filter/AutoLoginFilter.java | 52 ++++---- .../scm/web/filter/AutoLoginModules.java | 111 +++++++++++++++ .../web/filter/BasicAuthenticationFilter.java | 126 +++++++++++------- 3 files changed, 217 insertions(+), 72 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModules.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 index 412a4db515..8f2d5219f6 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginFilter.java @@ -100,33 +100,37 @@ public class AutoLoginFilter extends HttpFilter { * @return The user or null if no user was found. */ protected User getAuthenticatedUser(HttpServletRequest request, - HttpServletResponse response) { - Subject subject = SecurityUtils.getSubject(); - User user = null; + HttpServletResponse response) + { + Subject subject = SecurityUtils.getSubject(); + User user = null; - if (subject.isAuthenticated() || subject.isRemembered()) { - if (logger.isTraceEnabled()) { - logger.trace("user is allready authenticated"); - } + if (subject.isAuthenticated() || subject.isRemembered()) + { + 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); - 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) + { + logger.trace("user {} successfully authenticated by authentication filter", user.getName()); + break; + } + else if (AutoLoginModules.isComplete(request)) + { + logger.debug("stop auto login chain, because the AutoLoginModule {} marked the request as complete", filter.getClass()); + break; + } + } + } - if (user != null) { - if (logger.isTraceEnabled()) { - logger.trace( - "user {} successfully authenticated by authentication filter", - user.getName()); - } - break; - } - } - } - - return user; + return user; } /** diff --git a/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModules.java b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModules.java new file mode 100644 index 0000000000..ae3010d4be --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/filter/AutoLoginModules.java @@ -0,0 +1,111 @@ +/** +* Copyright (c) 2010, Sebastian Sdorra +* 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. +* +* http://bitbucket.org/sdorra/scm-manager +* +*/ + + + +package sonia.scm.web.filter; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Helper methods for implementations of the {@link AutoLoginModule}. + * + * @author Sebastian Sdorra + * + * @since 1.42 + */ +public final class AutoLoginModules +{ + + /** Field description */ + private static final String FLAG_COMPLETE = + AutoLoginModules.class.getName().concat("complete"); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + private AutoLoginModules() {} + + //~--- methods -------------------------------------------------------------- + + /** + * Mark the request as completed. No further actions will be executed. + * + * @param request http servlet request + */ + public static void markAsComplete(HttpServletRequest request) + { + request.setAttribute(FLAG_COMPLETE, Boolean.TRUE); + } + + /** + * Sends a redirect to the specified url and marks the request as completed. + * This method is useful for SSO solutions which have to redirect the user + * to a central login page. This method must be used in favor of + * {@link HttpServletResponse#sendRedirect(java.lang.String)} which could + * result in an error. + * + * @param request http servlet request + * @param response http servlet response + * @param url redirect target + * + * @throws IOException if client could not be redirected + */ + public static void sendRedirect(HttpServletRequest request, + HttpServletResponse response, String url) + throws IOException + { + markAsComplete(request); + response.sendRedirect(url); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns {@code true} is the request is marked as complete. + * + * @param request http servlet request + * + * @return {@code true} if request is complete + */ + public static boolean isComplete(HttpServletRequest request) + { + return request.getAttribute(FLAG_COMPLETE) != null; + } +} 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 38532ff3ac..408c0ca16e 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 @@ -139,60 +139,17 @@ public class BasicAuthenticationFilter extends AutoLoginFilter throws IOException, ServletException { Subject subject = SecurityUtils.getSubject(); + // get authenticated user or process AutoLoginModule's User user = getAuthenticatedUser(request, response); - // Fallback to basic authentication scheme - if (user == null) + if (AutoLoginModules.isComplete(request)) { - String authentication = request.getHeader(HEADER_AUTHORIZATION); - - if (Util.startWithIgnoreCase(authentication, AUTHORIZATION_BASIC_PREFIX)) - { - if (logger.isTraceEnabled()) - { - logger.trace( - "found basic authorization header, start 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 ((configuration != null) - && configuration.isAnonymousAccessEnabled()) - { - if (logger.isTraceEnabled()) - { - logger.trace("anonymous access granted"); - } - - user = SCMContext.ANONYMOUS; - } - } - - if (user == null) - { - if (logger.isTraceEnabled()) - { - logger.trace("could not find user send unauthorized"); - } - - handleUnauthorized(request, response, chain); + logger.debug("request marked as complete from an auto login module"); } else { - chain.doFilter(new SecurityHttpServletRequestWrapper(request, user), - response); + // process with basic authentication + processRequest(request, response, chain, subject, user); } } @@ -331,6 +288,79 @@ public class BasicAuthenticationFilter extends AutoLoginFilter return user; } + /** + * Method description + * + * + * @param request + * @param response + * @param chain + * @param subject + * @param user + * + * @throws IOException + * @throws ServletException + */ + private void processRequest(HttpServletRequest request, + HttpServletResponse response, FilterChain chain, Subject subject, User user) + throws IOException, ServletException + { + + // 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"); + } + + 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 ((configuration != null) + && configuration.isAnonymousAccessEnabled()) + { + if (logger.isTraceEnabled()) + { + logger.trace("anonymous access granted"); + } + + user = SCMContext.ANONYMOUS; + } + } + + 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); + } + } + //~--- fields --------------------------------------------------------------- /** scm main configuration */