From c1a3d4268a1665bc54d69eaea9995b1a8e214288 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 31 Dec 2010 14:58:11 +0100 Subject: [PATCH] fix bug with enabled anonymous access --- .../src/main/java/sonia/scm/SCMContext.java | 3 + .../main/java/sonia/scm/util/HttpUtil.java | 74 ++++++++++++++++ .../web/filter/BasicAuthenticationFilter.java | 88 ++++++++----------- .../scm/web/filter/PermissionFilter.java | 78 +++++++++++----- .../repository/xml/XmlRepositoryManager.java | 3 +- .../sonia/scm/user/xml/XmlUserManager.java | 1 + 6 files changed, 176 insertions(+), 71 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/util/HttpUtil.java diff --git a/scm-core/src/main/java/sonia/scm/SCMContext.java b/scm-core/src/main/java/sonia/scm/SCMContext.java index 48afba0bc8..4b5f744277 100644 --- a/scm-core/src/main/java/sonia/scm/SCMContext.java +++ b/scm-core/src/main/java/sonia/scm/SCMContext.java @@ -47,6 +47,9 @@ public class SCMContext /** Field description */ public static final String DEFAULT_PACKAGE = "sonia.scm"; + /** Field description */ + public static final String USER_ANONYMOUS = "anonymous"; + /** Field description */ private static volatile SCMContextProvider provider; diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java new file mode 100644 index 0000000000..79c17543ef --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -0,0 +1,74 @@ +/** + * 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.util; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Sebastian Sdorra + */ +public class HttpUtil +{ + + /** Field description */ + public static final String AUTHENTICATION_REALM = "SONIA :: SCM Manager"; + + /** Field description */ + public static final String HEADERVALUE_CONNECTION_CLOSE = "close"; + + /** Field description */ + public static final String HEADER_CONNECTION = "connection"; + + /** Field description */ + public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param response + */ + public static void sendUnauthorized(HttpServletResponse response) + { + response.setHeader(HEADER_WWW_AUTHENTICATE, + "Basic realm=\"" + AUTHENTICATION_REALM + "\""); + response.setHeader(HEADER_CONNECTION, HEADERVALUE_CONNECTION_CLOSE); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } +} 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 891efedcf6..053b5080a3 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,6 +29,8 @@ * */ + + package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- @@ -38,6 +40,8 @@ import com.google.inject.Provider; import com.google.inject.Singleton; import sonia.scm.user.User; +import sonia.scm.util.AssertUtil; +import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; import sonia.scm.web.security.WebSecurityContext; @@ -60,27 +64,15 @@ import javax.servlet.http.HttpServletResponse; public class BasicAuthenticationFilter extends HttpFilter { - /** Field description */ - public static final String AUTHENTICATION_REALM = "SONIA :: SCM Manager"; - /** Field description */ public static final String AUTHORIZATION_BASIC_PREFIX = "BASIC"; /** Field description */ public static final String CREDENTIAL_SEPARATOR = ":"; - /** Field description */ - public static final String HEADERVALUE_CONNECTION_CLOSE = "close"; - /** Field description */ public static final String HEADER_AUTHORIZATION = "Authorization"; - /** Field description */ - public static final String HEADER_CONNECTION = "connection"; - - /** Field description */ - public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - //~--- constructors --------------------------------------------------------- /** @@ -115,50 +107,34 @@ public class BasicAuthenticationFilter extends HttpFilter throws IOException, ServletException { WebSecurityContext securityContext = securityContextProvider.get(); + + AssertUtil.assertIsNotNull(securityContext); + User user = null; + String authentication = request.getHeader(HEADER_AUTHORIZATION); - if (securityContext != null) + if (Util.isNotEmpty(authentication)) { - if (!securityContext.isAuthenticated()) + if (!authentication.toUpperCase().startsWith(AUTHORIZATION_BASIC_PREFIX)) { - String authentication = request.getHeader(HEADER_AUTHORIZATION); - - if (Util.isEmpty(authentication)) - { - sendUnauthorized(response); - } - else - { - if (!authentication.toUpperCase().startsWith( - AUTHORIZATION_BASIC_PREFIX)) - { - throw new ServletException("wrong basic header"); - } - - String token = authentication.substring(6); - - token = new String(Base64.decode(token.getBytes())); - - String[] credentials = token.split(CREDENTIAL_SEPARATOR); - - user = securityContext.authenticate(request, response, - credentials[0], credentials[1]); - } - } - else - { - user = securityContext.getUser(); + throw new ServletException("wrong basic header"); } + + user = authenticate(request, response, securityContext, authentication); + } + else if (securityContext.isAuthenticated()) + { + user = securityContext.getUser(); } - if (user != null) + if (user == null) { - chain.doFilter(new SecurityHttpServletRequestWrapper(request, user), - response); + HttpUtil.sendUnauthorized(response); } else { - sendUnauthorized(response); + chain.doFilter(new SecurityHttpServletRequestWrapper(request, user), + response); } } @@ -166,14 +142,26 @@ public class BasicAuthenticationFilter extends HttpFilter * Method description * * + * @param request * @param response + * @param securityContext + * @param authentication + * + * @return */ - private void sendUnauthorized(HttpServletResponse response) + private User authenticate(HttpServletRequest request, + HttpServletResponse response, + WebSecurityContext securityContext, + String authentication) { - response.setHeader(HEADER_WWW_AUTHENTICATE, - "Basic realm=\"" + AUTHENTICATION_REALM + "\""); - response.setHeader(HEADER_CONNECTION, HEADERVALUE_CONNECTION_CLOSE); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + String token = authentication.substring(6); + + token = new String(Base64.decode(token.getBytes())); + + String[] credentials = token.split(CREDENTIAL_SEPARATOR); + + return securityContext.authenticate(request, response, credentials[0], + credentials[1]); } //~--- fields --------------------------------------------------------------- diff --git a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java index ecaf350d35..b0bd301c8e 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java @@ -40,11 +40,14 @@ import com.google.inject.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.SCMContext; import sonia.scm.repository.PermissionType; import sonia.scm.repository.PermissionUtil; import sonia.scm.repository.Repository; +import sonia.scm.security.ScmSecurityException; import sonia.scm.user.User; import sonia.scm.util.AssertUtil; +import sonia.scm.util.HttpUtil; import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ @@ -128,41 +131,54 @@ public abstract class PermissionFilter extends HttpFilter if (user != null) { - Repository repository = getRepository(request); - - if (repository != null) + try { - boolean writeRequest = isWriteRequest(request); + Repository repository = getRepository(request); - if (PermissionUtil.hasPermission(repository, securityContext.getUser(), - writeRequest - ? PermissionType.WRITE - : PermissionType.READ)) + if (repository != null) { - chain.doFilter(request, response); + boolean writeRequest = isWriteRequest(request); + + if (PermissionUtil.hasPermission(repository, + securityContext.getUser(), + writeRequest + ? PermissionType.WRITE + : PermissionType.READ)) + { + chain.doFilter(request, response); + } + else + { + if (logger.isInfoEnabled()) + { + logger.info("{} access to repostitory {} for user {} denied", + new Object[] { writeRequest + ? "write" + : "read", repository.getName(), + user.getName() }); + } + + sendAccessDenied(response, user); + } } else { - if (logger.isInfoEnabled()) + if (logger.isDebugEnabled()) { - logger.info("{} access to repostitory {} for user {} denied", - new Object[] { writeRequest - ? "write" - : "read", repository.getName(), - user.getName() }); + logger.debug("repository not found"); } - response.sendError(HttpServletResponse.SC_FORBIDDEN); + response.sendError(HttpServletResponse.SC_NOT_FOUND); } } - else + catch (ScmSecurityException ex) { - if (logger.isDebugEnabled()) + if (logger.isWarnEnabled()) { - logger.debug("repository not found"); + logger.warn("user {} has not enough permissions", user.getName()); } - response.sendError(HttpServletResponse.SC_NOT_FOUND); + sendAccessDenied(response, user); } } else @@ -176,6 +192,28 @@ public abstract class PermissionFilter extends HttpFilter } } + /** + * Method description + * + * + * @param response + * @param user + * + * @throws IOException + */ + private void sendAccessDenied(HttpServletResponse response, User user) + throws IOException + { + if (SCMContext.USER_ANONYMOUS.equals(user.getName())) + { + HttpUtil.sendUnauthorized(response); + } + else + { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + } + //~--- fields --------------------------------------------------------------- /** Field description */ diff --git a/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java index 4b091ef50b..23c9d2cbd5 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java @@ -55,6 +55,7 @@ import sonia.scm.repository.RepositoryAllreadyExistExeption; import sonia.scm.repository.RepositoryException; import sonia.scm.repository.RepositoryHandler; import sonia.scm.repository.RepositoryHandlerNotFoundException; +import sonia.scm.security.ScmSecurityException; import sonia.scm.security.SecurityContext; import sonia.scm.store.Store; import sonia.scm.store.StoreFactory; @@ -358,7 +359,7 @@ public class XmlRepositoryManager extends AbstractRepositoryManager } else { - repository = null; + throw new ScmSecurityException("not enaugh permissions"); } } diff --git a/scm-webapp/src/main/java/sonia/scm/user/xml/XmlUserManager.java b/scm-webapp/src/main/java/sonia/scm/user/xml/XmlUserManager.java index 6b706ef699..0784f06a60 100644 --- a/scm-webapp/src/main/java/sonia/scm/user/xml/XmlUserManager.java +++ b/scm-webapp/src/main/java/sonia/scm/user/xml/XmlUserManager.java @@ -370,6 +370,7 @@ public class XmlUserManager extends AbstractUserManager { User user = (User) unmarshaller.unmarshal(input); + user.setCreationDate(System.currentTimeMillis()); userDB.add(user); storeDB(); }