remove auto login modules because they are not longer needed, since we can determine the order of the filter chain

This commit is contained in:
Sebastian Sdorra
2015-02-06 22:41:34 +01:00
parent ed76f14aa4
commit 8ccaaf3695
12 changed files with 83 additions and 478 deletions

View File

@@ -43,9 +43,18 @@ public final class Filters
/** Field description */
public static final String PATTERN_ALL = "/*";
/** Field description */
public static final String PATTERN_CONFIG = "/api/rest/config*";
/** Field description */
public static final String PATTERN_DEBUG = "/debug.html";
/** Field description */
public static final String PATTERN_GROUPS = "/api/rest/groups*";
/** Field description */
public static final String PATTERN_PLUGINS = "/api/rest/plugins*";
/** Field description */
public static final String PATTERN_RESOURCE_REGEX =
"^/(?:resources|api|plugins|index)[\\./].*(?:html|\\.css|\\.js|\\.xml|\\.json|\\.txt)";
@@ -53,6 +62,9 @@ public final class Filters
/** Field description */
public static final String PATTERN_RESTAPI = "/api/rest/*";
/** Field description */
public static final String PATTERN_USERS = "/api/rest/users*";
/** authentication priority */
public static final int PRIORITY_AUTHENTICATION = 5000;

View File

@@ -1,176 +0,0 @@
/**
* 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;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
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;
/**
* 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);
//~--- constructors ---------------------------------------------------------
/**
* Constructor.
*
* @param autoLoginModules
* - The auto-login modules.
*/
@Inject
public AutoLoginFilter(Set<AutoLoginModule> autoLoginModules)
{
this.autoLoginModules = autoLoginModules;
}
//~--- 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
{
User user = getAuthenticatedUser(request, response);
if (user == null)
{
chain.doFilter(request, response);
}
else
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
response);
}
}
//~--- get methods ----------------------------------------------------------
/**
* 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;
}
//~--- fields ---------------------------------------------------------------
/**
* Set of AutoLoginModule objects.
*/
private final Set<AutoLoginModule> autoLoginModules;
}

View File

@@ -1,64 +0,0 @@
/**
* 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);
}

View File

@@ -1,111 +0,0 @@
/**
* 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 <sebastian.sdorra@gmail.com>
*
* @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;
}
}

View File

@@ -58,8 +58,6 @@ import com.sun.jersey.core.util.Base64;
import java.io.IOException;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -70,7 +68,7 @@ import javax.servlet.http.HttpServletResponse;
* @author Sebastian Sdorra
*/
@Singleton
public class BasicAuthenticationFilter extends AutoLoginFilter
public class BasicAuthenticationFilter extends HttpFilter
{
/** Field description */
@@ -92,18 +90,15 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
//~--- constructors ---------------------------------------------------------
/**
* Constructs a new basic authenticaton filter
* Constructs a new basic authenticaton filter.
*
* @param configuration scm-manager global configuration
* @param autoLoginModules auto login modules
*
* @since 1.21
*/
@Inject
public BasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
public BasicAuthenticationFilter(ScmConfiguration configuration)
{
super(autoLoginModules);
this.configuration = configuration;
}
@@ -126,17 +121,54 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
throws IOException, ServletException
{
Subject subject = SecurityUtils.getSubject();
// get authenticated user or process AutoLoginModule's
User user = getAuthenticatedUser(request, response);
User user = null;
if (AutoLoginModules.isComplete(request))
String authentication = request.getHeader(HEADER_AUTHORIZATION);
if (Util.startWithIgnoreCase(authentication, AUTHORIZATION_BASIC_PREFIX))
{
logger.debug("request marked as complete from an auto login module");
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
{
// process with basic authentication
processRequest(request, response, chain, subject, user);
logger.trace("authentcation failed, user object is null");
}
}
}
else if (subject.isAuthenticated())
{
logger.trace("user is allready authenticated");
user = subject.getPrincipals().oneByType(User.class);
}
else if ((configuration != null)
&& configuration.isAnonymousAccessEnabled())
{
if (logger.isTraceEnabled())
{
logger.trace("anonymous access granted");
}
user = SCMContext.ANONYMOUS;
}
if (user == null)
{
logger.trace("could not find user send unauthorized");
handleUnauthorized(request, response, chain);
}
else
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
response);
}
}
@@ -232,11 +264,8 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
String password = token.substring(index + 1);
if (Util.isNotEmpty(username) && Util.isNotEmpty(password))
{
if (logger.isTraceEnabled())
{
logger.trace("try to authenticate user {}", username);
}
try
{
@@ -275,79 +304,6 @@ 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 */

View File

@@ -34,7 +34,6 @@ package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
@@ -44,15 +43,12 @@ import sonia.scm.config.ScmConfiguration;
import sonia.scm.filter.Filters;
import sonia.scm.filter.WebElement;
import sonia.scm.repository.GitUtil;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -70,13 +66,11 @@ public class GitBasicAuthenticationFilter extends BasicAuthenticationFilter
*
*
* @param configuration
* @param autoLoginModules
*/
@Inject
public GitBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
public GitBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(configuration, autoLoginModules);
super(configuration);
}
//~--- methods --------------------------------------------------------------

View File

@@ -39,7 +39,6 @@ import sonia.scm.Priority;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.filter.Filters;
import sonia.scm.filter.WebElement;
import sonia.scm.web.filter.AutoLoginModule;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
@@ -65,13 +64,11 @@ public class HgBasicAuthenticationFilter extends BasicAuthenticationFilter
*
*
* @param configuration
* @param autoLoginModules
*/
@Inject
public HgBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
public HgBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(configuration, autoLoginModules);
super(configuration);
}
//~--- methods --------------------------------------------------------------

View File

@@ -41,15 +41,12 @@ import sonia.scm.filter.Filters;
import sonia.scm.filter.WebElement;
import sonia.scm.repository.SvnUtil;
import sonia.scm.util.HttpUtil;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -67,13 +64,11 @@ public class SvnBasicAuthenticationFilter extends BasicAuthenticationFilter
*
*
* @param configuration
* @param autoLoginModules
*/
@Inject
public SvnBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
public SvnBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(configuration, autoLoginModules);
super(configuration);
}
//~--- methods --------------------------------------------------------------

View File

@@ -38,8 +38,8 @@ package sonia.scm.filter;
import com.google.inject.Inject;
import org.apache.shiro.subject.Subject;
import sonia.scm.Priority;
import sonia.scm.Priority;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.Role;
@@ -47,8 +47,15 @@ import sonia.scm.security.Role;
*
* @author Sebastian Sdorra
*/
@WebElement(
value = Filters.PATTERN_CONFIG,
morePatterns = {
Filters.PATTERN_USERS,
Filters.PATTERN_GROUPS,
Filters.PATTERN_PLUGINS
}
)
@Priority(Filters.PRIORITY_AUTHORIZATION + 1)
@WebElement(value = Filters.PATTERN_RESTAPI, morePatterns = {Filters.PATTERN_DEBUG})
public class AdminSecurityFilter extends SecurityFilter
{

View File

@@ -36,32 +36,28 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import sonia.scm.Priority;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.web.filter.AutoLoginModule;
import sonia.scm.filter.Filters;
import sonia.scm.filter.WebElement;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
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 sonia.scm.Priority;
import sonia.scm.filter.Filters;
import sonia.scm.filter.WebElement;
/**
*
* @author Sebastian Sdorra
*/
@Priority(Filters.PRIORITY_AUTHENTICATION)
@WebElement(value = Filters.PATTERN_RESTAPI, morePatterns = {Filters.PATTERN_DEBUG})
@WebElement(value = Filters.PATTERN_RESTAPI, morePatterns = { Filters.PATTERN_DEBUG })
public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
{
@@ -83,10 +79,9 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
* @param configuration
*/
@Inject
public ApiBasicAuthenticationFilter(ScmConfiguration configuration,
Set<AutoLoginModule> autoLoginModules)
public ApiBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(configuration, autoLoginModules);
super(configuration);
}
//~--- methods --------------------------------------------------------------

View File

@@ -90,7 +90,7 @@ public abstract class AbstractPermissionITCaseBase<T>
@Parameters
public static Collection<Credentials[]> createParameters()
{
Collection<Credentials[]> params = new ArrayList<Credentials[]>();
Collection<Credentials[]> params = new ArrayList<>();
params.add(new Credentials[] { new Credentials() });
params.add(new Credentials[] {

View File

@@ -105,9 +105,9 @@ public class AdminPermissionITCase
@Parameters
public static Collection<Object[]> createParameters()
{
Collection<Object[]> params = new ArrayList<Object[]>();
Collection<Object[]> params = new ArrayList<>();
params.add(new Object[] { new Credentials() });
// params.add(new Object[] { new Credentials() });
User u = UserTestData.createMarvin();