fix anonymous access

This commit is contained in:
Sebastian Sdorra
2012-09-13 15:28:46 +02:00
parent f33a32a625
commit 492fb08558
9 changed files with 245 additions and 102 deletions

View File

@@ -113,7 +113,7 @@ public class PermissionUtil
@Deprecated
public static void assertPermission(Repository repository, PermissionType pt)
{
if (!hasPermission(repository, pt))
if (!hasPermission(null, repository, pt))
{
throw new ScmSecurityException("action denied");
}
@@ -136,7 +136,7 @@ public class PermissionUtil
public static boolean hasPermission(Repository repository,
Provider<WebSecurityContext> securityContextProvider, PermissionType pt)
{
return hasPermission(repository, securityContextProvider.get(), pt);
return hasPermission(null, repository, pt);
}
/**
@@ -154,24 +154,23 @@ public class PermissionUtil
public static boolean hasPermission(Repository repository,
WebSecurityContext securityContext, PermissionType pt)
{
return hasPermission(repository, pt);
return hasPermission(null, repository, pt);
}
/**
* Method description
*
*
* @param configuration
* @param repository
* @param securityContext
* @param pt
*
* @return
* @since 1.21
*
* @deprecated
* @since 1.21
*/
@Deprecated
public static boolean hasPermission(Repository repository, PermissionType pt)
public static boolean hasPermission(ScmConfiguration configuration,
Repository repository, PermissionType pt)
{
boolean result = false;
@@ -179,7 +178,6 @@ public class PermissionUtil
if (subject.isAuthenticated())
{
String username = subject.getPrincipal().toString();
AssertUtil.assertIsNotEmpty(username);
@@ -203,6 +201,14 @@ public class PermissionUtil
}
}
}
else
{
// check anonymous access
result = (configuration != null)
&& configuration.isAnonymousAccessEnabled()
&& repository.isPublicReadable() && (pt == PermissionType.READ);
}
return result;
}
@@ -252,7 +258,7 @@ public class PermissionUtil
}
else
{
permitted = PermissionUtil.hasPermission(repository,
permitted = PermissionUtil.hasPermission(configuration, repository,
PermissionType.WRITE);
}

View File

@@ -40,20 +40,19 @@ import com.google.common.base.Strings;
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.HandlerEvent;
import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.BlameResult;
import sonia.scm.repository.Branches;
import sonia.scm.repository.BrowserResult;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil;
import sonia.scm.repository.PostReceiveRepositoryHook;
import sonia.scm.repository.PreProcessorUtil;
import sonia.scm.repository.Repository;
@@ -65,7 +64,6 @@ import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.Tags;
import sonia.scm.repository.spi.RepositoryServiceProvider;
import sonia.scm.repository.spi.RepositoryServiceResolver;
import sonia.scm.security.RepositoryPermission;
import sonia.scm.security.ScmSecurityException;
//~--- JDK imports ------------------------------------------------------------
@@ -135,12 +133,38 @@ public final class RepositoryServiceFactory
* @param securityContextProvider provider for the current security context
* @param resolvers a set of {@link RepositoryServiceResolver}
* @param preProcessorUtil helper object for pre processor handling
*
* @deprecated
*/
@Inject
@Deprecated
public RepositoryServiceFactory(CacheManager cacheManager,
RepositoryManager repositoryManager,
Set<RepositoryServiceResolver> resolvers, PreProcessorUtil preProcessorUtil)
{
this(null, cacheManager, repositoryManager, resolvers, preProcessorUtil);
}
/**
* Constructs a new {@link RepositoryServiceFactory}. This constructor
* should not be called manually, it should only be used by the injection
* container.
*
*
* @param configuration configuration
* @param cacheManager cache manager
* @param repositoryManager manager for repositories
* @param securityContextProvider provider for the current security context
* @param resolvers a set of {@link RepositoryServiceResolver}
* @param preProcessorUtil helper object for pre processor handling
*
* @since 1.21
*/
@Inject
public RepositoryServiceFactory(ScmConfiguration configuration,
CacheManager cacheManager, RepositoryManager repositoryManager,
Set<RepositoryServiceResolver> resolvers, PreProcessorUtil preProcessorUtil)
{
this.configuration = configuration;
this.cacheManager = cacheManager;
this.repositoryManager = repositoryManager;
this.resolvers = resolvers;
@@ -249,10 +273,8 @@ public final class RepositoryServiceFactory
Preconditions.checkNotNull(repository, "repository is required");
// check for read permissions of current user
Subject subject = SecurityUtils.getSubject();
if (!subject.isPermitted(new RepositoryPermission(repository,
PermissionType.READ)))
if (!PermissionUtil.hasPermission(configuration, repository,
PermissionType.READ))
{
throw new ScmSecurityException("read permission are required");
}
@@ -408,6 +430,9 @@ public final class RepositoryServiceFactory
/** Field description */
private CacheManager cacheManager;
/** scm-manager configuration */
private ScmConfiguration configuration;
/** Field description */
private PreProcessorUtil preProcessorUtil;

View File

@@ -35,6 +35,7 @@ package sonia.scm.web.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -45,6 +46,8 @@ import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.ScmAuthenticationToken;
import sonia.scm.user.User;
import sonia.scm.util.HttpUtil;
@@ -85,12 +88,6 @@ public class BasicAuthenticationFilter extends HttpFilter
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
* @since 1.21
*/
public BasicAuthenticationFilter() {}
/**
* Constructs ...
*
@@ -102,6 +99,19 @@ public class BasicAuthenticationFilter extends HttpFilter
public BasicAuthenticationFilter(
Provider<WebSecurityContext> securityContextProvider) {}
/**
* Constructs a new basic authenticaton filter
*
* @param configuration scm-manager global configuration
*
* @since 1.21
*/
@Inject
public BasicAuthenticationFilter(ScmConfiguration configuration)
{
this.configuration = configuration;
}
//~--- methods --------------------------------------------------------------
/**
@@ -155,6 +165,13 @@ public class BasicAuthenticationFilter extends HttpFilter
user = subject.getPrincipals().oneByType(User.class);
}
else if ((configuration != null)
&& configuration.isAnonymousAccessEnabled())
{
user = new User(SCMContext.USER_ANONYMOUS, "SCM Anonymous",
"scm-anonymous@scm-manager.com");
}
if (user == null)
{
@@ -258,4 +275,9 @@ public class BasicAuthenticationFilter extends HttpFilter
return user;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private ScmConfiguration configuration;
}

View File

@@ -45,7 +45,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ArgumentIsInvalidException;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil;
@@ -80,12 +79,11 @@ public abstract class PermissionFilter extends HttpFilter
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
*
* @param configuration
* @param securityContextProvider
* Constructs a new permission filter
*
* @param configuration global scm-manager configuration
*
* @since 1.21
*/
public PermissionFilter(ScmConfiguration configuration)
{
@@ -150,87 +148,76 @@ public abstract class PermissionFilter extends HttpFilter
{
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated())
try
{
try
Repository repository = getRepository(request);
if (repository != null)
{
Repository repository = getRepository(request);
boolean writeRequest = isWriteRequest(request);
if (repository != null)
if (hasPermission(repository, writeRequest))
{
boolean writeRequest = isWriteRequest(request);
if (hasPermission(repository, writeRequest))
if (logger.isTraceEnabled())
{
if (logger.isTraceEnabled())
{
logger.trace("{} access to repository {} for user {} granted",
new Object[] { writeRequest
? "write"
: "read", repository.getName(), subject.getPrincipal() });
}
chain.doFilter(request, response);
logger.trace("{} access to repository {} for user {} granted",
new Object[] { writeRequest
? "write"
: "read", repository.getName(), subject.getPrincipal() });
}
else
{
if (logger.isInfoEnabled())
{
logger.info("{} access to repository {} for user {} denied",
new Object[] { writeRequest
? "write"
: "read", repository.getName(), subject.getPrincipal() });
}
sendAccessDenied(response, subject);
}
chain.doFilter(request, response);
}
else
{
if (logger.isDebugEnabled())
if (logger.isInfoEnabled())
{
logger.debug("repository not found");
logger.info("{} access to repository {} for user {} denied",
new Object[] { writeRequest
? "write"
: "read", repository.getName(), subject.getPrincipal() });
}
response.sendError(HttpServletResponse.SC_NOT_FOUND);
sendAccessDenied(response, subject);
}
}
catch (ArgumentIsInvalidException ex)
else
{
if (logger.isTraceEnabled())
if (logger.isDebugEnabled())
{
logger.trace(
"wrong request at ".concat(request.getRequestURI()).concat(
" send redirect"), ex);
}
else if (logger.isWarnEnabled())
{
logger.warn("wrong request at {} send redirect",
request.getRequestURI());
logger.debug("repository not found");
}
response.sendRedirect(getRepositoryRootHelpUrl(request));
}
catch (ScmSecurityException ex)
{
if (logger.isWarnEnabled())
{
logger.warn("user {} has not enough permissions",
subject.getPrincipal());
}
sendAccessDenied(response, subject);
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
else
catch (ArgumentIsInvalidException ex)
{
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("user in not authenticated");
logger.trace(
"wrong request at ".concat(request.getRequestURI()).concat(
" send redirect"), ex);
}
else if (logger.isWarnEnabled())
{
logger.warn("wrong request at {} send redirect",
request.getRequestURI());
}
response.sendError(HttpServletResponse.SC_FORBIDDEN);
response.sendRedirect(getRepositoryRootHelpUrl(request));
}
catch (ScmSecurityException ex)
{
if (logger.isWarnEnabled())
{
logger.warn("user {} has not enough permissions",
subject.getPrincipal());
}
sendAccessDenied(response, subject);
}
}
/**
@@ -269,15 +256,13 @@ public abstract class PermissionFilter extends HttpFilter
private void sendAccessDenied(HttpServletResponse response, Subject subject)
throws IOException
{
// TODO check anonymous access
if (SCMContext.USER_ANONYMOUS.equals(subject.getPrincipal()))
if (subject.isAuthenticated())
{
HttpUtil.sendUnauthorized(response);
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
HttpUtil.sendUnauthorized(response);
}
}
@@ -322,7 +307,8 @@ public abstract class PermissionFilter extends HttpFilter
}
else
{
permitted = PermissionUtil.hasPermission(repository, PermissionType.READ);
permitted = PermissionUtil.hasPermission(configuration, repository,
PermissionType.READ);
}
return permitted;

View File

@@ -49,6 +49,7 @@ import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.SCMContextProvider;
import sonia.scm.ScmClientConfig;
import sonia.scm.ScmState;
@@ -61,6 +62,9 @@ import sonia.scm.user.UserManager;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -253,7 +257,6 @@ public class AuthenticationResource
public Response getState(@Context HttpServletRequest request)
{
Response response = null;
ScmState state = null;
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated())
@@ -263,7 +266,16 @@ public class AuthenticationResource
logger.debug("return state for user {}", subject.getPrincipal());
}
state = createState(subject);
ScmState state = createState(subject);
response = Response.ok(state).build();
}
else if (configuration.isAnonymousAccessEnabled())
{
User user = new User(SCMContext.USER_ANONYMOUS, "SCM Anonymous",
"scm-anonymous@scm-manager.com");
ScmState state = createState(user, Collections.EMPTY_LIST);
response = Response.ok(state).build();
}
else
@@ -292,7 +304,21 @@ public class AuthenticationResource
User user = collection.oneByType(User.class);
GroupNames groups = collection.oneByType(GroupNames.class);
return new ScmState(contextProvider, user, groups.getCollection(),
return createState(user, groups.getCollection());
}
/**
* Method description
*
*
* @param user
* @param groups
*
* @return
*/
private ScmState createState(User user, Collection<String> groups)
{
return new ScmState(contextProvider, user, groups,
repositoryManger.getConfiguredTypes(), userManager.getDefaultType(),
new ScmClientConfig(configuration));
}

View File

@@ -35,10 +35,12 @@ package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.shiro.subject.Subject;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.Role;
/**
@@ -49,6 +51,20 @@ import sonia.scm.security.Role;
public class AdminSecurityFilter extends SecurityFilter
{
/**
* Constructs ...
*
*
* @param configuration
*/
@Inject
public AdminSecurityFilter(ScmConfiguration configuration)
{
super(configuration);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*

View File

@@ -35,11 +35,14 @@ package sonia.scm.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 sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.user.User;
import sonia.scm.web.filter.HttpFilter;
import sonia.scm.web.filter.SecurityHttpServletRequestWrapper;
@@ -64,6 +67,20 @@ public class SecurityFilter extends HttpFilter
/** Field description */
public static final String URL_AUTHENTICATION = "/api/rest/authentication";
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param configuration
*/
@Inject
public SecurityFilter(ScmConfiguration configuration)
{
this.configuration = configuration;
}
//~--- methods --------------------------------------------------------------
/**
@@ -92,7 +109,7 @@ public class SecurityFilter extends HttpFilter
if (hasPermission(subject))
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request,
subject.getPrincipals().oneByType(User.class)), response);
getUser(subject)), response);
}
else if (subject.isAuthenticated())
{
@@ -121,6 +138,37 @@ public class SecurityFilter extends HttpFilter
*/
protected boolean hasPermission(Subject subject)
{
return subject.isAuthenticated();
return ((configuration != null)
&& configuration.isAnonymousAccessEnabled()) || subject.isAuthenticated();
}
/**
* Method description
*
*
* @param subject
*
* @return
*/
private User getUser(Subject subject)
{
User user = null;
if (subject.isAuthenticated())
{
user = subject.getPrincipals().oneByType(User.class);
}
else
{
user = new User(SCMContext.USER_ANONYMOUS, "SCM Anonymous",
"scm-anonymous@scm-manager.com");
}
return user;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private ScmConfiguration configuration;
}

View File

@@ -54,7 +54,6 @@ import sonia.scm.HandlerEvent;
import sonia.scm.SCMContextProvider;
import sonia.scm.Type;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.RepositoryPermission;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.CollectionAppender;
@@ -869,7 +868,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
{
if (!SecurityUtils.getSubject().hasRole("admin"))
{
throw new SecurityException("admin role is required");
throw new ScmSecurityException("admin role is required");
}
}
@@ -971,8 +970,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
*/
private boolean isPermitted(Repository repository, PermissionType type)
{
return SecurityUtils.getSubject().isPermitted(
new RepositoryPermission(repository, PermissionType.READ));
return PermissionUtil.hasPermission(configuration, repository, type);
}
/**

View File

@@ -35,8 +35,10 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
@@ -65,6 +67,20 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
/** Field description */
public static final String URI_STATE = "/api/rest/authentication/state";
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param configuration
*/
@Inject
public ApiBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(configuration);
}
//~--- methods --------------------------------------------------------------
/**