merge with branch apache-shiro

This commit is contained in:
Sebastian Sdorra
2012-10-16 07:08:28 +02:00
82 changed files with 3376 additions and 1317 deletions

View File

@@ -44,33 +44,75 @@ public enum HandlerEvent
/**
* After a new object is stored by a handler.
*/
CREATE,
CREATE(true),
/**
* After a object is modified by a handler.
*/
MODIFY,
MODIFY(true),
/**
* After a object is removed by a handler.
*/
DELETE,
DELETE(true),
/**
* Before a new object is stored by a handler.
* @since 1.16
*/
BEFORE_CREATE,
BEFORE_CREATE(false),
/**
* Before a object is modified by a handler.
* @since 1.16
*/
BEFORE_MODIFY,
BEFORE_MODIFY(false),
/**
* Before a object is removed by a handler.
* @since 1.16
*/
BEFORE_DELETE
BEFORE_DELETE(false);
/**
* Constructs ...
*
*
* @param post
*/
private HandlerEvent(boolean post)
{
this.post = post;
}
//~--- get methods ----------------------------------------------------------
/**
* Returns true if the event is fired after the action is occurred.
*
*
* @return true if the event is fired after the action is occurred
* @since 1.21
*/
public boolean isPost()
{
return post;
}
/**
* Returns true if the event is fired before the action is occurred.
*
*
* @return true if the event is fired before the action is occurred
* @since 1.21
*/
public boolean isPre()
{
return !post;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private boolean post;
}

View File

@@ -35,6 +35,7 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.user.User;
import sonia.scm.util.ServiceUtil;
/**
@@ -52,6 +53,14 @@ public class SCMContext
/** Name of the anonymous user */
public static final String USER_ANONYMOUS = "anonymous";
/**
* the anonymous user
* @since 1.21
*/
public static final User ANONYMOUS = new User(USER_ANONYMOUS,
"SCM Anonymous",
"scm-anonymous@scm-manager.com");
/** Singleton instance of {@link SCMContextProvider} */
private static volatile SCMContextProvider provider;

View File

@@ -73,10 +73,10 @@ public class ScmState
* @param repositoryTypes - available repository types
* @param clientConfig - client configuration
*/
@Deprecated
public ScmState(SCMContextProvider provider,
WebSecurityContext securityContext,
Collection<Type> repositoryTypes,
ScmClientConfig clientConfig)
WebSecurityContext securityContext, Collection<Type> repositoryTypes,
ScmClientConfig clientConfig)
{
this(provider, securityContext, repositoryTypes, null, clientConfig);
}
@@ -93,10 +93,10 @@ public class ScmState
*
* @since 1.14
*/
@Deprecated
public ScmState(SCMContextProvider provider,
WebSecurityContext securityContext,
Collection<Type> repositoryTypes, String defaultUserType,
ScmClientConfig clientConfig)
WebSecurityContext securityContext, Collection<Type> repositoryTypes,
String defaultUserType, ScmClientConfig clientConfig)
{
this.version = provider.getVersion();
this.user = securityContext.getUser();
@@ -106,6 +106,31 @@ public class ScmState
this.defaultUserType = defaultUserType;
}
/**
* Constructs {@link ScmState} object.
*
*
* @param provider context provider
* @param user current user
* @param groups groups of the current user
* @param repositoryTypes available repository types
* @param defaultUserType default user type
* @param clientConfig client configuration
*
* @since 1.21
*/
public ScmState(SCMContextProvider provider, User user,
Collection<String> groups, Collection<Type> repositoryTypes,
String defaultUserType, ScmClientConfig clientConfig)
{
this.version = provider.getVersion();
this.user = user;
this.groups = groups;
this.repositoryTypes = repositoryTypes;
this.clientConfig = clientConfig;
this.defaultUserType = defaultUserType;
}
//~--- get methods ----------------------------------------------------------
/**

View File

@@ -0,0 +1,137 @@
/**
* 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.group;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Lists;
//~--- JDK imports ------------------------------------------------------------
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
/**
* This class represents all associated groups for a user.
*
* @author Sebastian Sdorra
* @since 1.21
*/
public final class GroupNames implements Serializable, Iterable<String>
{
/** Field description */
private static final long serialVersionUID = 8615685985213897947L;
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*/
public GroupNames()
{
this.collection = Collections.EMPTY_LIST;
}
/**
* Constructs ...
*
*
* @param collection
*/
public GroupNames(Collection<String> collection)
{
this.collection = Collections.unmodifiableCollection(collection);
}
/**
* Constructs ...
*
*
* @param groupName
* @param groupNames
*/
public GroupNames(String groupName, String... groupNames)
{
this.collection = Lists.asList(groupName, groupNames);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param groupName
*
* @return
*/
public boolean contains(String groupName)
{
return collection.contains(groupName);
}
/**
* Method description
*
*
* @return
*/
@Override
public Iterator<String> iterator()
{
return collection.iterator();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Collection<String> getCollection()
{
return collection;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Collection<String> collection;
}

View File

@@ -37,18 +37,21 @@ package sonia.scm.repository;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.GroupNames;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.List;
/**
@@ -73,14 +76,13 @@ public class PermissionUtil
* @param repository
* @param securityContext
* @param pt
* @deprecated
*/
@Deprecated
public static void assertPermission(Repository repository,
WebSecurityContext securityContext, PermissionType pt)
WebSecurityContext securityContext, PermissionType pt)
{
if (!hasPermission(repository, securityContext, pt))
{
throw new ScmSecurityException("action denied");
}
assertPermission(repository, pt);
}
/**
@@ -91,13 +93,32 @@ public class PermissionUtil
* @param securityContextProvider
* @param pt
*/
@Deprecated
public static void assertPermission(Repository repository,
Provider<WebSecurityContext> securityContextProvider,
PermissionType pt)
Provider<WebSecurityContext> securityContextProvider, PermissionType pt)
{
assertPermission(repository, securityContextProvider.get(), pt);
}
/**
* Method description
*
*
* @param repository
* @param securityContextProvider
* @param pt
*
* @since 1.21
*/
@Deprecated
public static void assertPermission(Repository repository, PermissionType pt)
{
if (!hasPermission(null, repository, pt))
{
throw new ScmSecurityException("action denied");
}
}
//~--- get methods ----------------------------------------------------------
/**
@@ -109,12 +130,13 @@ public class PermissionUtil
* @param pt
*
* @return
* @deprecated
*/
@Deprecated
public static boolean hasPermission(Repository repository,
Provider<WebSecurityContext> securityContextProvider,
PermissionType pt)
Provider<WebSecurityContext> securityContextProvider, PermissionType pt)
{
return hasPermission(repository, securityContextProvider.get(), pt);
return hasPermission(null, repository, pt);
}
/**
@@ -126,39 +148,67 @@ public class PermissionUtil
* @param pt
*
* @return
* @deprecated use {@link #hasPermission(Repository,PermissionType)} instead
*/
@Deprecated
public static boolean hasPermission(Repository repository,
WebSecurityContext securityContext, PermissionType pt)
WebSecurityContext securityContext, PermissionType pt)
{
return hasPermission(null, repository, pt);
}
/**
* Method description
*
*
* @param configuration
* @param repository
* @param pt
*
* @return
*
* @since 1.21
*/
public static boolean hasPermission(ScmConfiguration configuration,
Repository repository, PermissionType pt)
{
boolean result = false;
if (securityContext != null)
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated())
{
User user = securityContext.getUser();
String username = subject.getPrincipal().toString();
if (user != null)
AssertUtil.assertIsNotEmpty(username);
if (subject.hasRole(Role.ADMIN)
|| ((pt == PermissionType.READ) && repository.isPublicReadable()))
{
String username = user.getName();
result = true;
}
else
{
List<Permission> permissions = repository.getPermissions();
AssertUtil.assertIsNotEmpty(username);
if (user.isAdmin()
|| ((pt == PermissionType.READ) && repository.isPublicReadable()))
if (permissions != null)
{
result = true;
}
else
{
List<Permission> permissions = repository.getPermissions();
GroupNames groupNames =
subject.getPrincipals().oneByType(GroupNames.class);
result = hasPermission(permissions, username, groupNames, pt);
if (permissions != null)
{
result = hasPermission(permissions, username,
securityContext.getGroups(), pt);
}
}
}
}
else
{
// check anonymous access
result = (configuration != null)
&& configuration.isAnonymousAccessEnabled()
&& repository.isPublicReadable() && (pt == PermissionType.READ);
}
return result;
}
@@ -173,10 +223,28 @@ public class PermissionUtil
*
* @return true if the repository is writable
* @since 1.14
* @deprecated use {@link #isWritable(ScmConfiguration, Repository)} instead
*/
@Deprecated
public static boolean isWritable(ScmConfiguration configuration,
Repository repository, WebSecurityContext securityContext)
{
return isWritable(configuration, repository);
}
/**
* Returns true if the repository is writable.
*
*
* @param configuration SCM-Manager main configuration
* @param repository repository to check
* @param securityContext current user security context
*
* @return true if the repository is writable
* @since 1.21
*/
public static boolean isWritable(ScmConfiguration configuration,
Repository repository,
WebSecurityContext securityContext)
Repository repository)
{
boolean permitted = false;
@@ -185,13 +253,13 @@ public class PermissionUtil
if (logger.isWarnEnabled())
{
logger.warn("{} is archived and is not writeable",
repository.getName());
repository.getName());
}
}
else
{
permitted = PermissionUtil.hasPermission(repository, securityContext,
PermissionType.WRITE);
permitted = PermissionUtil.hasPermission(configuration, repository,
PermissionType.WRITE);
}
return permitted;
@@ -209,7 +277,7 @@ public class PermissionUtil
* @return
*/
private static boolean hasPermission(List<Permission> permissions,
String username, Collection<String> groups, PermissionType pt)
String username, GroupNames groups, PermissionType pt)
{
boolean result = false;
@@ -218,8 +286,8 @@ public class PermissionUtil
String name = p.getName();
if (((name != null) && (p.getType().getValue() >= pt.getValue()))
&& (name.equals(username)
|| (p.isGroupPermission() && groups.contains(p.getName()))))
&& (name.equals(username)
|| (p.isGroupPermission() && groups.contains(p.getName()))))
{
result = true;

View File

@@ -38,7 +38,6 @@ package sonia.scm.repository.api;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.slf4j.Logger;
@@ -47,6 +46,7 @@ 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;
@@ -65,7 +65,6 @@ import sonia.scm.repository.Tags;
import sonia.scm.repository.spi.RepositoryServiceProvider;
import sonia.scm.repository.spi.RepositoryServiceResolver;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -134,16 +133,40 @@ 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,
Provider<WebSecurityContext> securityContextProvider,
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.securityContextProvider = securityContextProvider;
this.resolvers = resolvers;
this.preProcessorUtil = preProcessorUtil;
@@ -250,8 +273,11 @@ public final class RepositoryServiceFactory
Preconditions.checkNotNull(repository, "repository is required");
// check for read permissions of current user
PermissionUtil.assertPermission(repository, securityContextProvider,
PermissionType.READ);
if (!PermissionUtil.hasPermission(configuration, repository,
PermissionType.READ))
{
throw new ScmSecurityException("read permission are required");
}
RepositoryService service = null;
@@ -404,6 +430,9 @@ public final class RepositoryServiceFactory
/** Field description */
private CacheManager cacheManager;
/** scm-manager configuration */
private ScmConfiguration configuration;
/** Field description */
private PreProcessorUtil preProcessorUtil;
@@ -412,7 +441,4 @@ public final class RepositoryServiceFactory
/** Field description */
private Set<RepositoryServiceResolver> resolvers;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -0,0 +1,207 @@
/**
* 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.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Objects;
import org.apache.shiro.authz.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
//~--- JDK imports ------------------------------------------------------------
import java.io.Serializable;
/**
* This class represents the permission to a repository of a user.
*
* @author Sebastian Sdorra
* @since 1.21
*/
public final class RepositoryPermission implements Permission, Serializable
{
/** Field description */
public static final String WILDCARD = "*";
/** Field description */
private static final long serialVersionUID = 3832804235417228043L;
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param repository
* @param permissionType
*/
public RepositoryPermission(Repository repository,
PermissionType permissionType)
{
this(repository.getId(), permissionType);
}
/**
* Constructs ...
*
*
* @param repositoryId
* @param permissionType
*/
public RepositoryPermission(String repositoryId,
PermissionType permissionType)
{
this.repositoryId = repositoryId;
this.permissionType = permissionType;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param obj
*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final RepositoryPermission other = (RepositoryPermission) obj;
return Objects.equal(repositoryId, other.repositoryId)
&& Objects.equal(permissionType, other.permissionType);
}
/**
* Method description
*
*
* @return
*/
@Override
public int hashCode()
{
return Objects.hashCode(repositoryId, permissionType);
}
/**
* Method description
*
*
* @param p
*
* @return
*/
@Override
public boolean implies(Permission p)
{
boolean result = false;
if (p instanceof RepositoryPermission)
{
RepositoryPermission rp = (RepositoryPermission) p;
//J-
result = (repositoryId.equals(WILDCARD) || repositoryId.equals(rp.repositoryId))
&& (permissionType.getValue() >= rp.permissionType.getValue());
//J+
}
return result;
}
/**
* Method description
*
*
* @return
*/
@Override
public String toString()
{
//J-
return Objects.toStringHelper(this)
.add("repositoryId", repositoryId)
.add("permissionType", permissionType)
.toString();
//J+
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public PermissionType getPermissionType()
{
return permissionType;
}
/**
* Method description
*
*
* @return
*/
public String getRepositoryId()
{
return repositoryId;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private PermissionType permissionType;
/** Field description */
private String repositoryId;
}

View File

@@ -0,0 +1,48 @@
/**
* 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.security;
/**
*
* @author Sebastian Sdorra
* @since 1.21
*/
public final class Role
{
/** Field description */
public static final String ADMIN = "admin";
/** Field description */
public static final String USER = "user";
}

View File

@@ -40,7 +40,9 @@ import sonia.scm.user.User;
/**
*
* @author Sebastian Sdorra
* @deprecated use {@link SecurityUtils#getSecurityManager()} instead.
*/
@Deprecated
public interface SecurityContext
{

View File

@@ -0,0 +1,70 @@
/**
* 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.security;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
/**
* Create tokens for security reasons.
*
* @author Sebastian Sdorra
* @since 1.21
*/
public final class Tokens
{
/**
* Build an {@link AuthenticationToken} for use with
* {@link Subject#login(org.apache.shiro.authc.AuthenticationToken)}.
*
*
* @param request servlet request
* @param username username of the user to authenticate
* @param password password of the user to authenticate
*
* @return
*/
public static AuthenticationToken createAuthenticationToken(
HttpServletRequest request, String username, String password)
{
return new UsernamePasswordToken(username, password,
request.getRemoteAddr());
}
}

View File

@@ -1,34 +1,35 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
* 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 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.
* 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.template;
//~--- JDK imports ------------------------------------------------------------

View File

@@ -37,7 +37,11 @@ package sonia.scm.util;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import sonia.scm.SCMContext;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.security.SecurityContext;
import sonia.scm.user.User;
@@ -54,36 +58,51 @@ public class SecurityUtil
*
*
* @param contextProvider
* @deprecated use {@link Subject#checkRole(java.lang.String)} with {
* @link Role#ADMIN} instead.
*/
@Deprecated
public static void assertIsAdmin(
Provider<? extends SecurityContext> contextProvider)
Provider<? extends SecurityContext> contextProvider)
{
assertIsAdmin(contextProvider.get());
assertIsAdmin();
}
/**
* Method description
* This method is only present for compatibility reasons.
* Use {@link Subject#checkRole(java.lang.String)} with {
* @link Role#ADMIN} instead.
*
*
* @param context
* @since 1.21
*/
public static void assertIsAdmin(SecurityContext context)
public static void assertIsAdmin()
{
AssertUtil.assertIsNotNull(context);
Subject subject = SecurityUtils.getSubject();
User user = context.getUser();
if (user == null)
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
if (!user.isAdmin())
else if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin account is required");
}
}
/**
* Method description
*
*
* @param context
* @deprecated use {@link Subject#checkRole(java.lang.String)} with {
* @link Role#ADMIN} instead.
*/
@Deprecated
public static void assertIsAdmin(SecurityContext context)
{
assertIsAdmin();
}
/**
* Method description
*
@@ -91,7 +110,7 @@ public class SecurityUtil
* @param contextProvider
*/
public static void assertIsNotAnonymous(
Provider<? extends SecurityContext> contextProvider)
Provider<? extends SecurityContext> contextProvider)
{
if (isAnonymous(contextProvider))
{
@@ -124,7 +143,7 @@ public class SecurityUtil
* @return
*/
public static User getCurrentUser(
Provider<? extends SecurityContext> contextProvider)
Provider<? extends SecurityContext> contextProvider)
{
AssertUtil.assertIsNotNull(contextProvider);
@@ -151,7 +170,7 @@ public class SecurityUtil
* @return
*/
public static boolean isAdmin(
Provider<? extends SecurityContext> contextProvider)
Provider<? extends SecurityContext> contextProvider)
{
return isAdmin(contextProvider.get());
}
@@ -169,7 +188,7 @@ public class SecurityUtil
AssertUtil.assertIsNotNull(contextProvider);
return (contextProvider.getUser() != null)
&& contextProvider.getUser().isAdmin();
&& contextProvider.getUser().isAdmin();
}
/**
@@ -181,7 +200,7 @@ public class SecurityUtil
* @return
*/
public static boolean isAnonymous(
Provider<? extends SecurityContext> contextProvider)
Provider<? extends SecurityContext> contextProvider)
{
return isAnonymous(contextProvider.get());
}

View File

@@ -39,11 +39,17 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
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;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
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;
@@ -87,12 +93,23 @@ public class BasicAuthenticationFilter extends HttpFilter
*
*
* @param securityContextProvider
* @deprecated use the constructor with out arguments instead.
*/
@Deprecated
public BasicAuthenticationFilter(
Provider<WebSecurityContext> securityContextProvider) {}
/**
* Constructs a new basic authenticaton filter
*
* @param configuration scm-manager global configuration
*
* @since 1.21
*/
@Inject
public BasicAuthenticationFilter(
Provider<WebSecurityContext> securityContextProvider)
public BasicAuthenticationFilter(ScmConfiguration configuration)
{
this.securityContextProvider = securityContextProvider;
this.configuration = configuration;
}
//~--- methods --------------------------------------------------------------
@@ -110,12 +127,10 @@ public class BasicAuthenticationFilter extends HttpFilter
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
WebSecurityContext securityContext = securityContextProvider.get();
AssertUtil.assertIsNotNull(securityContext);
Subject subject = SecurityUtils.getSubject();
User user = null;
String authentication = request.getHeader(HEADER_AUTHORIZATION);
@@ -127,7 +142,7 @@ public class BasicAuthenticationFilter extends HttpFilter
logger.trace("found basic authorization header, start authentication");
}
user = authenticate(request, response, securityContext, authentication);
user = authenticate(request, response, subject, authentication);
if (logger.isTraceEnabled())
{
@@ -141,14 +156,20 @@ public class BasicAuthenticationFilter extends HttpFilter
}
}
}
else if (securityContext.isAuthenticated())
else if (subject.isAuthenticated())
{
if (logger.isTraceEnabled())
{
logger.trace("user is allready authenticated");
}
user = securityContext.getUser();
user = subject.getPrincipals().oneByType(User.class);
}
else if ((configuration != null)
&& configuration.isAnonymousAccessEnabled())
{
user = SCMContext.ANONYMOUS;
}
if (user == null)
@@ -158,12 +179,12 @@ public class BasicAuthenticationFilter extends HttpFilter
logger.trace("could not find user send unauthorized");
}
HttpUtil.sendUnauthorized(request, response);
handleUnauthorized(request, response, chain);
}
else
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
response);
response);
}
}
@@ -181,9 +202,8 @@ public class BasicAuthenticationFilter extends HttpFilter
* @since 1.8
*/
protected void handleUnauthorized(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpUtil.sendUnauthorized(request, response);
}
@@ -195,14 +215,13 @@ public class BasicAuthenticationFilter extends HttpFilter
* @param request
* @param response
* @param securityContext
* @param subject
* @param authentication
*
* @return
*/
private User authenticate(HttpServletRequest request,
HttpServletResponse response,
WebSecurityContext securityContext,
String authentication)
HttpServletResponse response, Subject subject, String authentication)
{
String token = authentication.substring(6);
@@ -223,8 +242,25 @@ public class BasicAuthenticationFilter extends HttpFilter
logger.trace("try to authenticate user {}", username);
}
user = securityContext.authenticate(request, response, username,
password);
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())
{
@@ -242,5 +278,5 @@ public class BasicAuthenticationFilter extends HttpFilter
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
private ScmConfiguration configuration;
}

View File

@@ -38,18 +38,18 @@ package sonia.scm.web.filter;
import com.google.common.base.Splitter;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
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;
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.util.Util;
import sonia.scm.web.security.WebSecurityContext;
@@ -78,6 +78,18 @@ public abstract class PermissionFilter extends HttpFilter
//~--- constructors ---------------------------------------------------------
/**
* Constructs a new permission filter
*
* @param configuration global scm-manager configuration
*
* @since 1.21
*/
public PermissionFilter(ScmConfiguration configuration)
{
this.configuration = configuration;
}
/**
* Constructs ...
*
@@ -85,12 +97,13 @@ public abstract class PermissionFilter extends HttpFilter
*
* @param configuration
* @param securityContextProvider
* @deprecated
*/
@Deprecated
public PermissionFilter(ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider)
Provider<WebSecurityContext> securityContextProvider)
{
this.configuration = configuration;
this.securityContextProvider = securityContextProvider;
}
//~--- get methods ----------------------------------------------------------
@@ -130,97 +143,81 @@ public abstract class PermissionFilter extends HttpFilter
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
WebSecurityContext securityContext = securityContextProvider.get();
Subject subject = SecurityUtils.getSubject();
AssertUtil.assertIsNotNull(securityContext);
User user = securityContext.getUser();
if (user != null)
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, securityContext, writeRequest))
if (logger.isTraceEnabled())
{
if (logger.isTraceEnabled())
{
logger.trace("{} access to repository {} for user {} granted",
new Object[] { writeRequest
? "write"
: "read", repository.getName(),
user.getName() });
}
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(),
user.getName() });
}
sendAccessDenied(response, user);
}
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", user.getName());
}
sendAccessDenied(response, user);
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);
}
}
/**
@@ -234,8 +231,8 @@ public abstract class PermissionFilter extends HttpFilter
private String extractType(HttpServletRequest request)
{
Iterator<String> it = Splitter.on(
HttpUtil.SEPARATOR_PATH).omitEmptyStrings().split(
request.getRequestURI()).iterator();
HttpUtil.SEPARATOR_PATH).omitEmptyStrings().split(
request.getRequestURI()).iterator();
String type = it.next();
if (Util.isNotEmpty(request.getContextPath()))
@@ -252,19 +249,20 @@ public abstract class PermissionFilter extends HttpFilter
*
* @param response
* @param user
* @param subject
*
* @throws IOException
*/
private void sendAccessDenied(HttpServletResponse response, User user)
throws IOException
private void sendAccessDenied(HttpServletResponse response, Subject subject)
throws IOException
{
if (SCMContext.USER_ANONYMOUS.equals(user.getName()))
if (subject.isAuthenticated())
{
HttpUtil.sendUnauthorized(response);
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
HttpUtil.sendUnauthorized(response);
}
}
@@ -299,21 +297,18 @@ public abstract class PermissionFilter extends HttpFilter
*
* @return
*/
private boolean hasPermission(Repository repository,
WebSecurityContext securityContext,
boolean writeRequest)
private boolean hasPermission(Repository repository, boolean writeRequest)
{
boolean permitted = false;
if (writeRequest)
{
permitted = PermissionUtil.isWritable(configuration, repository,
securityContext);
permitted = PermissionUtil.isWritable(configuration, repository);
}
else
{
permitted = PermissionUtil.hasPermission(repository, securityContext,
PermissionType.READ);
permitted = PermissionUtil.hasPermission(configuration, repository,
PermissionType.READ);
}
return permitted;
@@ -321,9 +316,6 @@ public abstract class PermissionFilter extends HttpFilter
//~--- fields ---------------------------------------------------------------
/** Field description */
protected Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private ScmConfiguration configuration;
}

View File

@@ -67,6 +67,21 @@ public abstract class ProviderPermissionFilter extends PermissionFilter
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param configuration
* @param repositoryProvider
* @since 1.21
*/
public ProviderPermissionFilter(ScmConfiguration configuration,
RepositoryProvider repositoryProvider)
{
super(configuration);
this.repositoryProvider = repositoryProvider;
}
/**
* Constructs ...
*
@@ -75,14 +90,14 @@ public abstract class ProviderPermissionFilter extends PermissionFilter
* @param configuration
* @param securityContextProvider
* @param repositoryProvider
* @deprecated
*/
public ProviderPermissionFilter(
ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryProvider repositoryProvider)
@Deprecated
public ProviderPermissionFilter(ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryProvider repositoryProvider)
{
super(configuration, securityContextProvider);
this.repositoryProvider = repositoryProvider;
this(configuration, repositoryProvider);
}
//~--- get methods ----------------------------------------------------------
@@ -107,7 +122,7 @@ public abstract class ProviderPermissionFilter extends PermissionFilter
catch (ProvisionException ex)
{
Throwables.propagateIfInstanceOf(ex.getCause(),
IllegalStateException.class);
IllegalStateException.class);
if (logger.isErrorEnabled())
{

View File

@@ -71,15 +71,31 @@ public abstract class RegexPermissionFilter extends PermissionFilter
* @param securityContextProvider
* @param repositoryManager
*/
public RegexPermissionFilter(
ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryManager repositoryManager)
public RegexPermissionFilter(ScmConfiguration configuration,
RepositoryManager repositoryManager)
{
super(configuration, securityContextProvider);
super(configuration);
this.repositoryManager = repositoryManager;
}
/**
* Constructs ...
*
*
*
* @param configuration
* @param securityContextProvider
* @param repositoryManager
* @deprecated
*/
@Deprecated
public RegexPermissionFilter(ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryManager repositoryManager)
{
this(configuration, repositoryManager);
}
//~--- get methods ----------------------------------------------------------
/**

View File

@@ -44,11 +44,14 @@ import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
/**
*
* @author Sebastian Sdorra
* @deprecated use {@link SecurityUtils#getSecurityManager()} instead.
*/
@Deprecated
public interface WebSecurityContext extends SecurityContext
{