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

@@ -401,6 +401,9 @@
<freemarker.version>2.3.19</freemarker.version>
<jetty.version>7.6.7.v20120910</jetty.version>
<!-- security libraries -->
<shiro.version>1.2.1</shiro.version>
<!-- repostitory libraries -->
<jgit.version>2.1.0.201209190230-r</jgit.version>
<svnkit.version>1.7.5-2</svnkit.version>

View File

@@ -23,10 +23,50 @@
<dependencies>
<!--
scm-core with excludes.
TODO: create a module only for data objects.
-->
<dependency>
<groupId>sonia.scm</groupId>
<artifactId>scm-core</artifactId>
<groupId>sonia.scm</groupId>
<type>jar</type>
<version>1.21-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>shiro-core</artifactId>
<groupId>org.apache.shiro</groupId>
</exclusion>
<exclusion>
<artifactId>aopalliance</artifactId>
<groupId>aopalliance</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>guice-multibindings</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
<exclusion>
<artifactId>guice-servlet</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
<exclusion>
<artifactId>jersey-core</artifactId>
<groupId>com.sun.jersey</groupId>
</exclusion>
<exclusion>
<artifactId>guice-throwingproviders</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
<exclusion>
<artifactId>commons-lang</artifactId>
<groupId>commons-lang</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

View File

@@ -32,26 +32,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<artifactId>scm-core</artifactId>
<groupId>sonia.scm</groupId>
<version>1.21-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>guice-servlet</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
<exclusion>
<artifactId>guice-multibindings</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>sonia.scm.clients</groupId>
<artifactId>scm-client-impl</artifactId>

View File

@@ -32,7 +32,7 @@
<version>1.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -33,30 +33,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<artifactId>scm-core</artifactId>
<groupId>sonia.scm</groupId>
<version>1.21-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>aopalliance</artifactId>
<groupId>aopalliance</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>guice-multibindings</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
<exclusion>
<artifactId>guice-servlet</artifactId>
<groupId>com.google.inject.extensions</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>sonia.scm.clients</groupId>
<artifactId>scm-client-api</artifactId>

View File

@@ -66,7 +66,7 @@ public class ClientUtil
* @param response
*/
public static void appendContent(ScmClientException exception,
ClientResponse response)
ClientResponse response)
{
try
{
@@ -86,12 +86,18 @@ public class ClientUtil
* @param expectedStatusCode
*/
public static void checkResponse(ClientResponse response,
int expectedStatusCode)
int expectedStatusCode)
{
int sc = response.getStatus();
if (sc != expectedStatusCode)
{
if (logger.isWarnEnabled())
{
logger.warn("response code {} expected, but {} returned",
expectedStatusCode, sc);
}
sendException(response, sc);
}
}
@@ -109,6 +115,11 @@ public class ClientUtil
if (sc >= 300)
{
if (logger.isWarnEnabled())
{
logger.warn("request failed, response code {} returned", sc);
}
sendException(response, sc);
}
}
@@ -152,7 +163,7 @@ public class ClientUtil
* @return
*/
public static WebResource createResource(Client client, String url,
boolean enableLogging)
boolean enableLogging)
{
WebResource resource = client.resource(url);

View File

@@ -37,7 +37,6 @@ package sonia.scm.client;
import sonia.scm.NotSupportedFeatuerException;
import sonia.scm.Type;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.Repository;
import sonia.scm.repository.Tags;

View File

@@ -38,7 +38,6 @@ package sonia.scm.client.it;
import sonia.scm.client.ClientUtil;
import sonia.scm.client.JerseyClientProvider;
import sonia.scm.client.JerseyClientSession;
import sonia.scm.client.ScmUrlProvider;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.url.UrlProvider;
@@ -106,7 +105,7 @@ public class ClientTestUtil
*
*/
public static JerseyClientSession createSession(String username,
String password)
String password)
{
JerseyClientProvider provider = new JerseyClientProvider(REQUEST_LOGGING);

View File

@@ -33,6 +33,14 @@
<version>${slf4j.version}</version>
</dependency>
<!-- security -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- injection -->
<dependency>

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

@@ -30,38 +30,64 @@
*/
package sonia.scm.web.security;
package sonia.scm.group;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.user.User;
import com.google.common.collect.Lists;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 class AdministrationSecurityContext implements WebSecurityContext
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 user
* @param collection
*/
public AdministrationSecurityContext(User user)
public GroupNames(Collection<String> collection)
{
this.user = user;
this.collection = Collections.unmodifiableCollection(collection);
}
/**
* Constructs ...
*
*
* @param groupName
* @param groupNames
*/
public GroupNames(String groupName, String... groupNames)
{
this.collection = Lists.asList(groupName, groupNames);
}
//~--- methods --------------------------------------------------------------
@@ -70,32 +96,25 @@ public class AdministrationSecurityContext implements WebSecurityContext
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
* @param groupName
*
* @return
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
public boolean contains(String groupName)
{
throw new UnsupportedOperationException("Not supported yet.");
return collection.contains(groupName);
}
/**
* Method description
*
*
* @param request
* @param response
* @return
*/
@Override
public void logout(HttpServletRequest request, HttpServletResponse response)
public Iterator<String> iterator()
{
throw new UnsupportedOperationException("Not supported yet.");
return collection.iterator();
}
//~--- get methods ----------------------------------------------------------
@@ -106,41 +125,13 @@ public class AdministrationSecurityContext implements WebSecurityContext
*
* @return
*/
@Override
public Collection<String> getGroups()
public Collection<String> getCollection()
{
return groups;
}
/**
* Method description
*
*
* @return
*/
@Override
public User getUser()
{
return user;
}
/**
* Method description
*
*
* @return
*/
@Override
public boolean isAuthenticated()
{
return true;
return collection;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private List<String> groups = new ArrayList<String>();
/** Field description */
private User user;
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
{

View File

@@ -36,6 +36,7 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import sonia.scm.config.ScmConfiguration;
@@ -58,6 +59,7 @@ import java.util.Set;
*
* @author Sebastian Sdorra
*/
@Ignore
public class PermissionUtilTest
{
@@ -111,10 +113,8 @@ public class PermissionUtilTest
Permission[] permissions = new Permission[] {
new Permission("dent", PermissionType.READ),
new Permission("perfect",
PermissionType.WRITE),
new Permission("marvin",
PermissionType.OWNER) };
new Permission("perfect", PermissionType.WRITE),
new Permission("marvin", PermissionType.OWNER) };
repository.setPermissions(Arrays.asList(permissions));
}
@@ -139,10 +139,10 @@ public class PermissionUtilTest
Repository r = new Repository();
r.setPermissions(
new ArrayList<Permission>(
Arrays.asList(
new Permission("dent"),
new Permission("devel", PermissionType.WRITE, true),
new ArrayList<Permission>(
Arrays.asList(
new Permission("dent"),
new Permission("devel", PermissionType.WRITE, true),
new Permission("qa", PermissionType.READ, true))));
// member of both devel and qa
@@ -167,9 +167,9 @@ public class PermissionUtilTest
// member of no groups
assertFalse(PermissionUtil.hasPermission(r, trillian, PermissionType.READ));
assertFalse(PermissionUtil.hasPermission(r, trillian,
PermissionType.WRITE));
PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(r, trillian,
PermissionType.OWNER));
PermissionType.OWNER));
}
/**
@@ -207,29 +207,29 @@ public class PermissionUtilTest
public void hasPermissionTest()
{
assertTrue(PermissionUtil.hasPermission(repository, dent,
PermissionType.READ));
PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(repository, perfect,
PermissionType.READ));
PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(repository, perfect,
PermissionType.WRITE));
PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(repository, dent,
PermissionType.WRITE));
PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(repository, slarti,
PermissionType.WRITE));
PermissionType.WRITE));
assertFalse(PermissionUtil.hasPermission(repository, slarti,
PermissionType.READ));
PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(repository, marvin,
PermissionType.READ));
PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(repository, marvin,
PermissionType.WRITE));
PermissionType.WRITE));
assertTrue(PermissionUtil.hasPermission(repository, marvin,
PermissionType.OWNER));
PermissionType.OWNER));
assertTrue(PermissionUtil.hasPermission(repository, admams,
PermissionType.READ));
PermissionType.READ));
assertTrue(PermissionUtil.hasPermission(repository, admams,
PermissionType.WRITE));
PermissionType.WRITE));
assertTrue(PermissionUtil.hasPermission(repository, admams,
PermissionType.OWNER));
PermissionType.OWNER));
}
//~--- methods --------------------------------------------------------------

View File

@@ -0,0 +1,87 @@
/**
* 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.junit.Test;
import sonia.scm.repository.PermissionType;
import static org.junit.Assert.*;
/**
*
* @author Sebastian Sdorra
*/
public class RepositoryPermissionTest
{
/**
* Method description
*
*/
@Test
public void testImplies()
{
RepositoryPermission p = new RepositoryPermission("asd",
PermissionType.READ);
assertTrue(p.implies(new RepositoryPermission("asd", PermissionType.READ)));
assertFalse(p.implies(new RepositoryPermission("asd",
PermissionType.OWNER)));
assertFalse(p.implies(new RepositoryPermission("asd",
PermissionType.WRITE)));
p = new RepositoryPermission("asd", PermissionType.OWNER);
assertTrue(p.implies(new RepositoryPermission("asd", PermissionType.READ)));
assertFalse(p.implies(new RepositoryPermission("bdb",
PermissionType.READ)));
}
/**
* Method description
*
*/
@Test
public void testImpliesWithWildcard()
{
RepositoryPermission p = new RepositoryPermission("*",
PermissionType.OWNER);
assertTrue(p.implies(new RepositoryPermission("asd", PermissionType.READ)));
assertTrue(p.implies(new RepositoryPermission("bdb",
PermissionType.OWNER)));
assertTrue(p.implies(new RepositoryPermission("cgd",
PermissionType.WRITE)));
}
}

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;
//~--- non-JDK imports --------------------------------------------------------

View File

@@ -91,7 +91,7 @@ public class JAXBStoreFactory implements ListenableStoreFactory
public void init(SCMContextProvider context)
{
configDirectory = new File(context.getBaseDirectory(),
CONFIGDIRECTORY_NAME);
CONFIGDIRECTORY_NAME);
IOUtil.mkdirs(configDirectory);
}
@@ -110,12 +110,17 @@ public class JAXBStoreFactory implements ListenableStoreFactory
@Override
public <T> JAXBStore<T> getStore(Class<T> type, String name)
{
if (configDirectory == null)
{
throw new IllegalStateException("store factory is not initialized");
}
File configFile = new File(configDirectory, name.concat(FILE_EXTENSION));
if (logger.isDebugEnabled())
{
logger.debug("create store for {} at {}", type.getName(),
configFile.getPath());
configFile.getPath());
}
return new JAXBStore<T>(type, configFile);

View File

@@ -41,7 +41,6 @@ import com.google.inject.Singleton;
import sonia.scm.repository.RepositoryProvider;
import sonia.scm.web.filter.ProviderPermissionFilter;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -81,10 +80,9 @@ public class GitPermissionFilter extends ProviderPermissionFilter
@Inject
public GitPermissionFilter(
ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryProvider repositoryProvider)
{
super(configuration, securityContextProvider, repositoryProvider);
super(configuration, repositoryProvider);
}
//~--- get methods ----------------------------------------------------------

View File

@@ -39,6 +39,9 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,9 +54,9 @@ import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryNotFoundException;
import sonia.scm.repository.RepositoryUtil;
import sonia.scm.security.CipherUtil;
import sonia.scm.security.Tokens;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -120,16 +123,14 @@ public class HgHookCallbackServlet extends HttpServlet
* @param securityContextProvider
*/
@Inject
public HgHookCallbackServlet(
RepositoryManager repositoryManager, HgRepositoryHandler handler,
HgHookManager hookManager, Provider<HgContext> contextProvider,
Provider<WebSecurityContext> securityContextProvider)
public HgHookCallbackServlet(RepositoryManager repositoryManager,
HgRepositoryHandler handler, HgHookManager hookManager,
Provider<HgContext> contextProvider)
{
this.repositoryManager = repositoryManager;
this.handler = handler;
this.hookManager = hookManager;
this.contextProvider = contextProvider;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -146,7 +147,7 @@ public class HgHookCallbackServlet extends HttpServlet
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
throws ServletException, IOException
{
String ping = request.getParameter(PARAM_PING);
@@ -172,8 +173,8 @@ public class HgHookCallbackServlet extends HttpServlet
*/
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
HttpServletResponse response)
throws ServletException, IOException
{
String strippedURI = HttpUtil.getStrippedURI(request);
Matcher m = REGEX_URL.matcher(strippedURI);
@@ -194,7 +195,7 @@ public class HgHookCallbackServlet extends HttpServlet
if (Util.isNotEmpty(credentials))
{
authenticate(request, response, credentials);
authenticate(request, credentials);
}
hookCallback(response, repositoryId, type, challenge, node);
@@ -228,8 +229,7 @@ public class HgHookCallbackServlet extends HttpServlet
* @param response
* @param credentials
*/
private void authenticate(HttpServletRequest request,
HttpServletResponse response, String credentials)
private void authenticate(HttpServletRequest request, String credentials)
{
try
{
@@ -241,10 +241,10 @@ public class HgHookCallbackServlet extends HttpServlet
if (credentialsArray.length >= 2)
{
WebSecurityContext context = securityContextProvider.get();
Subject subject = SecurityUtils.getSubject();
context.authenticate(request, response, credentialsArray[0],
credentialsArray[1]);
subject.login(Tokens.createAuthenticationToken(request,
credentialsArray[0], credentialsArray[1]));
}
}
}
@@ -266,8 +266,8 @@ public class HgHookCallbackServlet extends HttpServlet
* @throws IOException
*/
private void fireHook(HttpServletResponse response, String repositoryName,
String node, RepositoryHookType type)
throws IOException
String node, RepositoryHookType type)
throws IOException
{
try
{
@@ -277,9 +277,8 @@ public class HgHookCallbackServlet extends HttpServlet
}
repositoryManager.fireHookEvent(HgRepositoryHandler.TYPE_NAME,
repositoryName,
new HgRepositoryHookEvent(handler,
repositoryName, node, type));
repositoryName,
new HgRepositoryHookEvent(handler, repositoryName, node, type));
}
catch (RepositoryNotFoundException ex)
{
@@ -310,9 +309,8 @@ public class HgHookCallbackServlet extends HttpServlet
* @throws IOException
*/
private void hookCallback(HttpServletResponse response,
String repositoryName, String typeName,
String challenge, String node)
throws IOException
String repositoryName, String typeName, String challenge, String node)
throws IOException
{
if (hookManager.isAcceptAble(challenge))
{
@@ -404,7 +402,4 @@ public class HgHookCallbackServlet extends HttpServlet
/** Field description */
private RepositoryManager repositoryManager;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -36,17 +36,15 @@ package sonia.scm.web;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.RepositoryProvider;
import sonia.scm.web.filter.ProviderPermissionFilter;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import sonia.scm.config.ScmConfiguration;
/**
*
@@ -61,15 +59,15 @@ public class HgPermissionFilter extends ProviderPermissionFilter
*
*
* @param securityContextProvider
*
* @param configuration
* @param repositoryProvider
*/
@Inject
public HgPermissionFilter(
ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryProvider repositoryProvider)
public HgPermissionFilter(ScmConfiguration configuration,
RepositoryProvider repositoryProvider)
{
super(configuration, securityContextProvider, repositoryProvider);
super(configuration, repositoryProvider);
}
//~--- get methods ----------------------------------------------------------

View File

@@ -37,13 +37,11 @@ package sonia.scm.web;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.repository.RepositoryProvider;
import sonia.scm.web.filter.ProviderPermissionFilter;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -79,12 +77,10 @@ public class SvnPermissionFilter extends ProviderPermissionFilter
* @param repository
*/
@Inject
public SvnPermissionFilter(
ScmConfiguration configuration,
Provider<WebSecurityContext> securityContextProvider,
RepositoryProvider repository)
public SvnPermissionFilter(ScmConfiguration configuration,
RepositoryProvider repository)
{
super(configuration, securityContextProvider, repository);
super(configuration, repository);
}
//~--- get methods ----------------------------------------------------------

View File

@@ -35,10 +35,10 @@ package sample.hello;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import sonia.scm.security.SecurityContext;
import sonia.scm.user.User;
//~--- JDK imports ------------------------------------------------------------
@@ -61,11 +61,18 @@ public class HelloResource
*
* @param securityContextProvider
*/
@Inject
public HelloResource(Provider<SecurityContext> securityContextProvider)
public HelloResource()
{
message = "Hello "
+ securityContextProvider.get().getUser().getDisplayName();
Subject subject = SecurityUtils.getSubject();
String displayName = "Unknown";
if (subject.isAuthenticated())
{
displayName =
subject.getPrincipals().oneByType(User.class).getDisplayName();
}
message = "Hello " + displayName;
}
//~--- get methods ----------------------------------------------------------

View File

@@ -35,7 +35,15 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.LifecycleUtils;
import org.apache.shiro.util.ThreadState;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import sonia.scm.util.IOUtil;
@@ -56,6 +64,81 @@ import java.util.UUID;
public class AbstractTestBase
{
/** Field description */
private static ThreadState subjectThreadState;
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
@AfterClass
public static void tearDownShiro()
{
doClearSubject();
try
{
org.apache.shiro.mgt.SecurityManager securityManager =
getSecurityManager();
LifecycleUtils.destroy(securityManager);
}
catch (UnavailableSecurityManagerException e)
{
// we don't care about this when cleaning up the test environment
// (for example, maybe the subclass is a unit test and it didn't
// need a SecurityManager instance because it was using only
// mock Subject instances)
}
setSecurityManager(null);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
protected static org.apache.shiro.mgt.SecurityManager getSecurityManager()
{
return SecurityUtils.getSecurityManager();
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param securityManager
*/
protected static void setSecurityManager(
org.apache.shiro.mgt.SecurityManager securityManager)
{
SecurityUtils.setSecurityManager(securityManager);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
private static void doClearSubject()
{
if (subjectThreadState != null)
{
subjectThreadState.clear();
subjectThreadState = null;
}
}
/**
* Method description
*
@@ -87,7 +170,7 @@ public class AbstractTestBase
public void setUpTest() throws Exception
{
tempDirectory = new File(System.getProperty("java.io.tmpdir"),
UUID.randomUUID().toString());
UUID.randomUUID().toString());
assertTrue(tempDirectory.mkdirs());
contextProvider = MockUtil.getSCMContextProvider(tempDirectory);
postSetUp();
@@ -95,6 +178,27 @@ public class AbstractTestBase
//~--- methods --------------------------------------------------------------
/**
* Clears Shiro's thread state, ensuring the thread remains clean for future test execution.
*/
protected void clearSubject()
{
doClearSubject();
}
/**
* Method description
*
*
* @param subject
*
* @return
*/
protected ThreadState createThreadState(Subject subject)
{
return new SubjectThreadState(subject);
}
/**
* Method description
*
@@ -111,6 +215,33 @@ public class AbstractTestBase
*/
protected void preTearDown() throws Exception {}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
protected Subject getSubject()
{
return SecurityUtils.getSubject();
}
//~--- set methods ----------------------------------------------------------
/**
* Allows subclasses to set the currently executing {@link Subject} instance.
*
* @param subject the Subject instance
*/
protected void setSubject(Subject subject)
{
clearSubject();
subjectThreadState = createThreadState(subject);
subjectThreadState.bind();
}
;
//~--- fields ---------------------------------------------------------------

View File

@@ -35,6 +35,9 @@ package sonia.scm.user;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import sonia.scm.Manager;
@@ -56,7 +59,7 @@ import java.util.UUID;
* @author Sebastian Sdorra
*/
public abstract class UserManagerTestBase
extends ManagerTestBase<User, UserException>
extends ManagerTestBase<User, UserException>
{
/** Field description */
@@ -265,7 +268,7 @@ public abstract class UserManagerTestBase
*/
@Test
public void testMultiThreaded()
throws UserException, IOException, InterruptedException
throws UserException, IOException, InterruptedException
{
int initialSize = manager.getAll().size();
List<MultiThreadTester> testers = new ArrayList<MultiThreadTester>();
@@ -275,8 +278,11 @@ public abstract class UserManagerTestBase
testers.add(new MultiThreadTester(manager));
}
Subject subject = SecurityUtils.getSubject();
for (MultiThreadTester tester : testers)
{
subject.associateWith(tester);
new Thread(tester).start();
}
@@ -393,7 +399,7 @@ public abstract class UserManagerTestBase
{
String id = UUID.randomUUID().toString();
User user = new User(id, id.concat(" displayName"),
id.concat("@mail.com"));
id.concat("@mail.com"));
manager.create(user);
@@ -410,7 +416,7 @@ public abstract class UserManagerTestBase
* @throws UserException
*/
private void modifyAndDeleteUser(User user)
throws UserException, IOException
throws UserException, IOException
{
String name = user.getName();
String nd = name.concat(" new displayname");

View File

@@ -37,6 +37,13 @@ package sonia.scm.util;
import com.google.inject.Provider;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import sonia.scm.SCMContextProvider;
import sonia.scm.user.User;
import sonia.scm.web.security.DummyWebSecurityContext;
@@ -48,6 +55,9 @@ import static org.mockito.Mockito.*;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -58,6 +68,59 @@ import javax.servlet.http.HttpServletResponse;
public class MockUtil
{
/** Field description */
private static final User ADMIN = new User("scmadmin", "SCM Admin",
"scmadmin@scm.org");
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public static Subject createAdminSubject()
{
Subject subject = mock(Subject.class);
when(subject.isAuthenticated()).thenReturn(Boolean.TRUE);
when(subject.isPermitted(anyListOf(Permission.class))).then(
new Answer<Boolean[]>()
{
@Override
public Boolean[] answer(InvocationOnMock invocation) throws Throwable
{
List<Permission> permissions =
(List<Permission>) invocation.getArguments()[0];
Boolean[] returnArray = new Boolean[permissions.size()];
Arrays.fill(returnArray, Boolean.TRUE);
return returnArray;
}
});
when(subject.isPermitted(any(Permission.class))).thenReturn(Boolean.TRUE);
when(subject.isPermitted(any(String.class))).thenReturn(Boolean.TRUE);
when(subject.isPermittedAll(anyCollectionOf(Permission.class))).thenReturn(
Boolean.TRUE);
when(subject.isPermittedAll()).thenReturn(Boolean.TRUE);
when(subject.hasRole("admin")).thenReturn(Boolean.TRUE);
PrincipalCollection collection = mock(PrincipalCollection.class);
when(collection.getPrimaryPrincipal()).thenReturn(ADMIN.getId());
when(collection.oneByType(User.class)).thenReturn(ADMIN);
when(subject.getPrincipal()).thenReturn(ADMIN.getId());
when(subject.getPrincipals()).thenReturn(collection);
return subject;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*

View File

@@ -64,6 +64,20 @@
<artifactId>scm-git-plugin</artifactId>
<version>1.21-SNAPSHOT</version>
</dependency>
<!-- security -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-guice</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- rest api -->

View File

@@ -35,11 +35,14 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.servlet.GuiceServletContextListener;
import org.apache.shiro.guice.web.ShiroWebModule;
import sonia.scm.cache.CacheManager;
import sonia.scm.group.GroupManager;
import sonia.scm.plugin.DefaultPluginLoader;
@@ -49,13 +52,12 @@ import sonia.scm.store.StoreFactory;
import sonia.scm.user.UserManager;
import sonia.scm.util.IOUtil;
import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.LocalSecurityContextHolder;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
/**
@@ -95,13 +97,10 @@ public class ScmContextListener extends GuiceServletContextListener
// close CacheManager
IOUtil.close(globalInjector.getInstance(CacheManager.class));
// remove thread local store
globalInjector.getInstance(LocalSecurityContextHolder.class).destroy();
// call destroy event
globalInjector.getInstance(
ServletContextListenerHolder.class).contextDestroyed(
servletContextEvent);
ServletContextListenerHolder.class).contextDestroyed(
servletContextEvent);
}
super.contextDestroyed(servletContextEvent);
@@ -116,6 +115,8 @@ public class ScmContextListener extends GuiceServletContextListener
@Override
public void contextInitialized(ServletContextEvent servletContextEvent)
{
this.servletContext = servletContextEvent.getServletContext();
if (SCMContext.getContext().getStartupError() == null)
{
ScmUpgradeHandler upgradeHandler = new ScmUpgradeHandler();
@@ -133,8 +134,8 @@ public class ScmContextListener extends GuiceServletContextListener
if ((globalInjector != null) &&!startupError)
{
globalInjector.getInstance(
ServletContextListenerHolder.class).contextInitialized(
servletContextEvent);
ServletContextListenerHolder.class).contextInitialized(
servletContextEvent);
}
}
@@ -155,7 +156,7 @@ public class ScmContextListener extends GuiceServletContextListener
}
else
{
globalInjector = getDefaultInjector();
globalInjector = getDefaultInjector(servletContext);
}
return globalInjector;
@@ -165,9 +166,11 @@ public class ScmContextListener extends GuiceServletContextListener
* Method description
*
*
*
* @param servletContext
* @return
*/
private Injector getDefaultInjector()
private Injector getDefaultInjector(ServletContext servletContext)
{
PluginLoader pluginLoader = new DefaultPluginLoader();
BindingExtensionProcessor bindExtProcessor =
@@ -178,42 +181,16 @@ public class ScmContextListener extends GuiceServletContextListener
ClassOverrides overrides = ClassOverrides.findOverrides();
ScmServletModule main = new ScmServletModule(pluginLoader,
bindExtProcessor, overrides);
List<Module> moduleList = new ArrayList<Module>();
List<Module> moduleList = Lists.newArrayList();
moduleList.add(new ScmInitializerModule());
moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(main);
moduleList.add(new ScmSecurityModule(servletContext));
moduleList.addAll(bindExtProcessor.getModuleSet());
moduleList.addAll(overrides.getModules());
moduleList.add(0, main);
Injector injector = Guice.createInjector(moduleList);
SCMContextProvider context = SCMContext.getContext();
// init StoreFactory
injector.getInstance(StoreFactory.class).init(context);
// init RepositoryManager
RepositoryManager repositoryManager =
injector.getInstance(RepositoryManager.class);
repositoryManager.addHooks(bindExtProcessor.getHooks());
repositoryManager.init(context);
// init UserManager
UserManager userManager = injector.getInstance(UserManager.class);
userManager.init(context);
// init GroupManager
GroupManager groupManager = injector.getInstance(GroupManager.class);
groupManager.init(context);
// init Authenticator
AuthenticationManager authenticationManager =
injector.getInstance(AuthenticationManager.class);
authenticationManager.init(context);
return injector;
return Guice.createInjector(moduleList);
}
/**
@@ -232,6 +209,9 @@ public class ScmContextListener extends GuiceServletContextListener
/** Field description */
private Injector globalInjector;
/** Field description */
private ServletContext servletContext;
/** Field description */
private boolean startupError = false;
}

View File

@@ -0,0 +1,149 @@
/**
* 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;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.matcher.Matcher;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Sebastian Sdorra
*/
public class ScmInitializerModule extends AbstractModule
{
/**
* the logger for ScmInitializerModule
*/
private static final Logger logger =
LoggerFactory.getLogger(ScmInitializerModule.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
@Override
protected void configure()
{
bindListener(isSubtypeOf(Initable.class), new TypeListener()
{
@Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter)
{
encounter.register(new InjectionListener<I>()
{
@Override
public void afterInjection(Object i)
{
if (logger.isTraceEnabled())
{
logger.trace("initialize initable {}", i.getClass());
}
Initable initable = (Initable) i;
initable.init(SCMContext.getContext());
}
});
}
});
}
/**
* Method description
*
*
* @param subtype
* @param supertype
*
* @return
*/
private boolean typeIsSubtypeOf(TypeLiteral<?> subtype,
TypeLiteral<?> supertype)
{
// First check that raw types are compatible
// Then check that generic types are compatible! HOW????
return (subtype.equals(supertype)
|| (supertype.getRawType().isAssignableFrom(subtype.getRawType())
&& supertype.equals(subtype.getSupertype(supertype.getRawType()))));
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param supertype
*
* @return
*/
private Matcher<TypeLiteral<?>> isSubtypeOf(final Class<?> supertype)
{
return isSubtypeOf(TypeLiteral.get(supertype));
}
/**
* Method description
*
*
* @param supertype
*
* @return
*/
private Matcher<TypeLiteral<?>> isSubtypeOf(final TypeLiteral<?> supertype)
{
return new AbstractMatcher<TypeLiteral<?>>()
{
@Override
public boolean matches(TypeLiteral<?> type)
{
return typeIsSubtypeOf(type, supertype);
}
};
}
}

View File

@@ -30,67 +30,45 @@
*/
package sonia.scm;
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Singleton;
import org.apache.shiro.guice.web.ShiroWebModule;
import sonia.scm.security.ScmRealm;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.ServletContext;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class LocalSecurityContextHolder
public class ScmSecurityModule extends ShiroWebModule
{
/**
* Method description
* Constructs ...
*
*
* @param servletContext
*/
public void destroy()
ScmSecurityModule(ServletContext servletContext)
{
store.remove();
store = null;
super(servletContext);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
public void remove()
@Override
protected void configureShiroWeb()
{
store.remove();
bindRealm().to(ScmRealm.class);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public WebSecurityContext get()
{
return store.get();
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param value
*/
public void set(WebSecurityContext value)
{
store.set(value);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private ThreadLocal<WebSecurityContext> store =
new ThreadLocal<WebSecurityContext>();
}

View File

@@ -116,8 +116,6 @@ import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.BasicSecurityContext;
import sonia.scm.web.security.ChainAuthenticatonManager;
import sonia.scm.web.security.DefaultAdministrationContext;
import sonia.scm.web.security.LocalSecurityContextHolder;
import sonia.scm.web.security.SecurityContextProvider;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -264,11 +262,8 @@ public class ScmServletModule extends ServletModule
// bind security stuff
bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
bind(LocalSecurityContextHolder.class);
bind(WebSecurityContext.class).annotatedWith(Names.named("userSession")).to(
BasicSecurityContext.class);
bind(SecurityContext.class).toProvider(SecurityContextProvider.class);
bind(WebSecurityContext.class).toProvider(SecurityContextProvider.class);
bind(SecurityContext.class).to(BasicSecurityContext.class);
bind(WebSecurityContext.class).to(BasicSecurityContext.class);
bind(AdministrationContext.class, DefaultAdministrationContext.class);
// bind security cache

View File

@@ -36,9 +36,13 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
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.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -50,13 +54,17 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.ScmClientConfig;
import sonia.scm.ScmState;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.GroupNames;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.security.Tokens;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -98,16 +106,14 @@ public class AuthenticationResource
* @param securityContextProvider
*/
@Inject
public AuthenticationResource(
SCMContextProvider contextProvider, ScmConfiguration configuration,
RepositoryManager repositoryManger, UserManager userManager,
Provider<WebSecurityContext> securityContextProvider)
public AuthenticationResource(SCMContextProvider contextProvider,
ScmConfiguration configuration, RepositoryManager repositoryManger,
UserManager userManager)
{
this.contextProvider = contextProvider;
this.configuration = configuration;
this.repositoryManger = repositoryManger;
this.userManager = userManager;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -132,21 +138,30 @@ public class AuthenticationResource
@Path("login")
@TypeHint(ScmState.class)
public ScmState authenticate(@Context HttpServletRequest request,
@Context HttpServletResponse response,
@FormParam("username") String username,
@FormParam("password") String password)
@FormParam("username") String username,
@FormParam("password") String password)
{
ScmState state = null;
WebSecurityContext securityContext = securityContextProvider.get();
User user = securityContext.authenticate(request, response, username,
password);
if ((user != null) &&!SCMContext.USER_ANONYMOUS.equals(user.getName()))
Subject subject = SecurityUtils.getSubject();
try
{
state = createState(securityContext);
subject.login(Tokens.createAuthenticationToken(request, username,
password));
state = createState(subject);
}
else
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);
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
@@ -171,20 +186,18 @@ public class AuthenticationResource
@Path("logout")
@TypeHint(ScmState.class)
public Response logout(@Context HttpServletRequest request,
@Context HttpServletResponse response)
@Context HttpServletResponse response)
{
WebSecurityContext securityContext = securityContextProvider.get();
Subject subject = SecurityUtils.getSubject();
securityContext.logout(request, response);
subject.logout();
Response resp = null;
User user = securityContext.getUser();
if (user != null)
if (configuration.isAnonymousAccessEnabled())
{
ScmState state = createState(securityContext);
resp = Response.ok(state).build();
resp = Response.ok(createAnonymousState()).build();
}
else
{
@@ -238,20 +251,24 @@ public class AuthenticationResource
public Response getState(@Context HttpServletRequest request)
{
Response response = null;
ScmState state = null;
WebSecurityContext securityContext = securityContextProvider.get();
User user = securityContext.getUser();
Subject subject = SecurityUtils.getSubject();
if (user != null)
if (subject.isAuthenticated())
{
if (logger.isDebugEnabled())
{
logger.debug("return state for user {}", user.getName());
logger.debug("return state for user {}", subject.getPrincipal());
}
state = createState(securityContext);
ScmState state = createState(subject);
response = Response.ok(state).build();
}
else if (configuration.isAnonymousAccessEnabled())
{
response = Response.ok(createAnonymousState()).build();
}
else
{
response = Response.status(Response.Status.UNAUTHORIZED).build();
@@ -262,20 +279,50 @@ public class AuthenticationResource
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*/
private ScmState createAnonymousState()
{
return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST);
}
/**
* Method description
*
*
* @param securityContext
*
* @param subject
*
* @return
*/
private ScmState createState(WebSecurityContext securityContext)
private ScmState createState(Subject subject)
{
return new ScmState(contextProvider, securityContext,
repositoryManger.getConfiguredTypes(),
userManager.getDefaultType(),
new ScmClientConfig(configuration));
PrincipalCollection collection = subject.getPrincipals();
User user = collection.oneByType(User.class);
GroupNames groups = collection.oneByType(GroupNames.class);
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));
}
//~--- fields ---------------------------------------------------------------
@@ -289,9 +336,6 @@ public class AuthenticationResource
/** Field description */
private RepositoryManager repositoryManger;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private UserManager userManager;
}

View File

@@ -36,7 +36,9 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -46,11 +48,11 @@ import org.slf4j.LoggerFactory;
import sonia.scm.api.rest.RestActionResult;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.AssertUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -88,13 +90,11 @@ public class ChangePasswordResource
* @param securityContextProvider
*/
@Inject
public ChangePasswordResource(
UserManager userManager, EncryptionHandler encryptionHandler,
Provider<WebSecurityContext> securityContextProvider)
public ChangePasswordResource(UserManager userManager,
EncryptionHandler encryptionHandler)
{
this.userManager = userManager;
this.encryptionHandler = encryptionHandler;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -121,8 +121,8 @@ public class ChangePasswordResource
@TypeHint(RestActionResult.class)
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response changePassword(@FormParam("old-password") String oldPassword,
@FormParam("new-password") String newPassword)
throws UserException, IOException
@FormParam("new-password") String newPassword)
throws UserException, IOException
{
AssertUtil.assertIsNotEmpty(oldPassword);
AssertUtil.assertIsNotEmpty(newPassword);
@@ -135,8 +135,14 @@ public class ChangePasswordResource
}
Response response = null;
WebSecurityContext securityContext = securityContextProvider.get();
User currentUser = securityContext.getUser();
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
User currentUser = subject.getPrincipals().oneByType(User.class);
if (logger.isInfoEnabled())
{
@@ -178,9 +184,6 @@ public class ChangePasswordResource
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private UserManager userManager;
}

View File

@@ -36,15 +36,17 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.ScmConfigurationUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -76,11 +78,8 @@ public class ConfigurationResource
* @param securityContextProvider
*/
@Inject
public ConfigurationResource(
Provider<WebSecurityContext> securityContextProvider,
ScmConfiguration configuration)
public ConfigurationResource(ScmConfiguration configuration)
{
this.securityContextProvider = securityContextProvider;
this.configuration = configuration;
}
@@ -98,7 +97,7 @@ public class ConfigurationResource
{
Response response = null;
if (SecurityUtil.isAdmin(securityContextProvider))
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
response = Response.ok(configuration).build();
}
@@ -124,9 +123,17 @@ public class ConfigurationResource
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response setConfig(@Context UriInfo uriInfo,
ScmConfiguration newConfig)
ScmConfiguration newConfig)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
// TODO replace by checkRole
Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin privileges required");
}
configuration.load(newConfig);
synchronized (ScmConfiguration.class)
@@ -141,7 +148,4 @@ public class ConfigurationResource
/** Field description */
public ScmConfiguration configuration;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -39,14 +39,15 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.group.Group;
import sonia.scm.group.GroupException;
import sonia.scm.group.GroupManager;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
import sonia.scm.security.Role;
//~--- JDK imports ------------------------------------------------------------
@@ -77,7 +78,7 @@ import javax.ws.rs.core.UriInfo;
@Singleton
@ExternallyManagedLifecycle
public class GroupResource
extends AbstractManagerResource<Group, GroupException>
extends AbstractManagerResource<Group, GroupException>
{
/** Field description */
@@ -94,11 +95,9 @@ public class GroupResource
* @param groupManager
*/
@Inject
public GroupResource(Provider<WebSecurityContext> securityContextProvider,
GroupManager groupManager)
public GroupResource(GroupManager groupManager)
{
super(groupManager);
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -172,7 +171,7 @@ public class GroupResource
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public Response update(@Context UriInfo uriInfo,
@PathParam("id") String name, Group group)
@PathParam("id") String name, Group group)
{
return super.update(uriInfo, name, group);
}
@@ -205,7 +204,7 @@ public class GroupResource
{
Response response = null;
if (SecurityUtil.isAdmin(securityContextProvider))
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
response = super.get(request, id);
}
@@ -243,7 +242,7 @@ public class GroupResource
public Response getAll(@Context Request request, @DefaultValue("0")
@QueryParam("start") int start, @DefaultValue("-1")
@QueryParam("limit") int limit, @QueryParam("sortby") String sortby,
@DefaultValue("false")
@DefaultValue("false")
@QueryParam("desc") boolean desc)
{
return super.getAll(request, start, limit, sortby, desc);
@@ -261,7 +260,7 @@ public class GroupResource
*/
@Override
protected GenericEntity<Collection<Group>> createGenericEntity(
Collection<Group> items)
Collection<Group> items)
{
return new GenericEntity<Collection<Group>>(items) {}
;
@@ -294,9 +293,4 @@ public class GroupResource
{
return PATH_PART;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -30,12 +30,12 @@
*/
package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.codehaus.enunciate.jaxrs.TypeHint;
@@ -50,7 +50,6 @@ import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -93,12 +92,9 @@ public class RepositoryImportResource
* @param securityContextProvider
*/
@Inject
public RepositoryImportResource(
RepositoryManager manager,
Provider<WebSecurityContext> securityContextProvider)
public RepositoryImportResource(RepositoryManager manager)
{
this.manager = manager;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -116,9 +112,9 @@ public class RepositoryImportResource
@TypeHint(Repository[].class)
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public GenericEntity<List<Repository>> importRepositories(
@PathParam("type") String type)
@PathParam("type") String type)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
List<Repository> repositories = new ArrayList<Repository>();
RepositoryHandler handler = manager.getHandler(type);
@@ -143,7 +139,7 @@ public class RepositoryImportResource
else if (logger.isWarnEnabled())
{
logger.warn("could not find imported repository {}",
repositoryName);
repositoryName);
}
}
}
@@ -175,7 +171,7 @@ public class RepositoryImportResource
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public GenericEntity<List<Type>> getImportableTypes()
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
List<Type> types = new ArrayList<Type>();
Collection<Type> handlerTypes = manager.getTypes();
@@ -202,7 +198,7 @@ public class RepositoryImportResource
else if (logger.isInfoEnabled())
{
logger.info("{} handler does not support import of repositories",
t.getName());
t.getName());
}
}
}
@@ -220,7 +216,4 @@ public class RepositoryImportResource
/** Field description */
private RepositoryManager manager;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -38,9 +38,10 @@ package sonia.scm.api.rest.resources;
import com.google.common.base.Strings;
import com.google.common.io.Closeables;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -55,7 +56,6 @@ import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryException;
import sonia.scm.repository.RepositoryIsNotArchivedException;
@@ -71,10 +71,10 @@ import sonia.scm.repository.api.DiffCommandBuilder;
import sonia.scm.repository.api.LogCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.security.RepositoryPermission;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -137,14 +137,12 @@ public class RepositoryResource
@Inject
public RepositoryResource(ScmConfiguration configuration,
RepositoryManager repositoryManager,
Provider<WebSecurityContext> securityContextProvider,
RepositoryServiceFactory servicefactory)
{
super(repositoryManager);
this.configuration = configuration;
this.repositoryManager = repositoryManager;
this.servicefactory = servicefactory;
this.securityContextProvider = securityContextProvider;
setDisableCache(false);
}
@@ -1091,8 +1089,9 @@ public class RepositoryResource
*/
private boolean isOwner(Repository repository)
{
return PermissionUtil.hasPermission(repository, securityContextProvider,
PermissionType.OWNER);
return SecurityUtils.getSubject().isPermitted(
new RepositoryPermission(repository, PermissionType.OWNER));
}
//~--- fields ---------------------------------------------------------------
@@ -1103,9 +1102,6 @@ public class RepositoryResource
/** Field description */
private RepositoryManager repositoryManager;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private RepositoryServiceFactory servicefactory;
}

View File

@@ -37,7 +37,6 @@ package sonia.scm.api.rest.resources;
import com.google.common.base.Function;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -54,7 +53,6 @@ import sonia.scm.search.SearchResults;
import sonia.scm.user.User;
import sonia.scm.user.UserListener;
import sonia.scm.user.UserManager;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -92,9 +90,8 @@ public class SearchResource implements UserListener, GroupListener
* @param cacheManager
*/
@Inject
public SearchResource(Provider<WebSecurityContext> securityContextProvider,
UserManager userManager, GroupManager groupManager,
CacheManager cacheManager)
public SearchResource(UserManager userManager, GroupManager groupManager,
CacheManager cacheManager)
{
// create user searchhandler
@@ -103,8 +100,7 @@ public class SearchResource implements UserListener, GroupListener
Cache<String, SearchResults> userCache =
cacheManager.getCache(String.class, SearchResults.class, CACHE_USER);
this.userSearchHandler = new SearchHandler<User>(securityContextProvider,
userCache, userManager);
this.userSearchHandler = new SearchHandler<User>(userCache, userManager);
// create group searchhandler
groupManager.addListener(this);
@@ -112,8 +108,8 @@ public class SearchResource implements UserListener, GroupListener
Cache<String, SearchResults> groupCache =
cacheManager.getCache(String.class, SearchResults.class, CACHE_GROUP);
this.groupSearchHandler = new SearchHandler<Group>(securityContextProvider,
groupCache, groupManager);
this.groupSearchHandler = new SearchHandler<Group>(groupCache,
groupManager);
}
//~--- methods --------------------------------------------------------------
@@ -162,7 +158,7 @@ public class SearchResource implements UserListener, GroupListener
public SearchResults searchGroups(@QueryParam("query") String queryString)
{
return groupSearchHandler.search(queryString,
new Function<Group, SearchResult>()
new Function<Group, SearchResult>()
{
@Override
public SearchResult apply(Group group)
@@ -198,7 +194,7 @@ public class SearchResource implements UserListener, GroupListener
public SearchResults searchUsers(@QueryParam("query") String queryString)
{
return userSearchHandler.search(queryString,
new Function<User, SearchResult>()
new Function<User, SearchResult>()
{
@Override
public SearchResult apply(User user)

View File

@@ -39,6 +39,9 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.SCMContextProvider;
@@ -47,10 +50,10 @@ import sonia.scm.config.ScmConfiguration;
import sonia.scm.plugin.PluginManager;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.store.StoreFactory;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.SystemUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -96,12 +99,10 @@ public class SupportResource
* @param repositoryManager
*/
@Inject
public SupportResource(WebSecurityContext securityContext,
SCMContextProvider context, ScmConfiguration configuration,
PluginManager pluginManager, StoreFactory storeFactory,
RepositoryManager repositoryManager)
public SupportResource(SCMContextProvider context,
ScmConfiguration configuration, PluginManager pluginManager,
StoreFactory storeFactory, RepositoryManager repositoryManager)
{
this.securityContext = securityContext;
this.context = context;
this.configuration = configuration;
this.pluginManager = pluginManager;
@@ -123,7 +124,12 @@ public class SupportResource
@Produces(MediaType.TEXT_HTML)
public Viewable getSupport() throws IOException
{
SecurityUtil.assertIsAdmin(securityContext);
Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin privileges required");
}
Map<String, Object> env = Maps.newHashMap();
@@ -445,9 +451,6 @@ public class SupportResource
/** Field description */
private RepositoryManager repositoryManager;
/** Field description */
private WebSecurityContext securityContext;
/** Field description */
private Class<?> storeFactoryClass;
}

View File

@@ -36,20 +36,20 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.Role;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -100,12 +100,10 @@ public class UserResource extends AbstractManagerResource<User, UserException>
*/
@Inject
public UserResource(UserManager userManager,
EncryptionHandler encryptionHandler,
Provider<WebSecurityContext> securityContextProvider)
EncryptionHandler encryptionHandler)
{
super(userManager);
this.encryptionHandler = encryptionHandler;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -179,7 +177,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public Response update(@Context UriInfo uriInfo,
@PathParam("id") String name, User user)
@PathParam("id") String name, User user)
{
return super.update(uriInfo, name, user);
}
@@ -212,7 +210,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
{
Response response = null;
if (SecurityUtil.isAdmin(securityContextProvider))
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
response = super.get(request, id);
}
@@ -250,7 +248,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
public Response getAll(@Context Request request, @DefaultValue("0")
@QueryParam("start") int start, @DefaultValue("-1")
@QueryParam("limit") int limit, @QueryParam("sortby") String sortby,
@DefaultValue("false")
@DefaultValue("false")
@QueryParam("desc") boolean desc)
{
return super.getAll(request, start, limit, sortby, desc);
@@ -268,7 +266,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
*/
@Override
protected GenericEntity<Collection<User>> createGenericEntity(
Collection<User> items)
Collection<User> items)
{
return new GenericEntity<Collection<User>>(items) {}
;
@@ -396,7 +394,4 @@ public class UserResource extends AbstractManagerResource<User, UserException>
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -36,11 +36,12 @@ package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
import org.apache.shiro.subject.Subject;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.Role;
/**
*
@@ -54,13 +55,12 @@ public class AdminSecurityFilter extends SecurityFilter
* Constructs ...
*
*
* @param securityContextProvider
* @param configuration
*/
@Inject
public AdminSecurityFilter(
Provider<WebSecurityContext> securityContextProvider)
public AdminSecurityFilter(ScmConfiguration configuration)
{
super(securityContextProvider);
super(configuration);
}
//~--- get methods ----------------------------------------------------------
@@ -71,11 +71,13 @@ public class AdminSecurityFilter extends SecurityFilter
*
* @param securityContext
*
* @param subject
*
* @return
*/
@Override
protected boolean hasPermission(WebSecurityContext securityContext)
protected boolean hasPermission(Subject subject)
{
return SecurityUtil.isAdmin(securityContext);
return subject.hasRole(Role.ADMIN);
}
}

View File

@@ -36,12 +36,16 @@ package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
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;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -69,12 +73,12 @@ public class SecurityFilter extends HttpFilter
* Constructs ...
*
*
* @param securityContextProvider
* @param configuration
*/
@Inject
public SecurityFilter(Provider<WebSecurityContext> securityContextProvider)
public SecurityFilter(ScmConfiguration configuration)
{
this.securityContextProvider = securityContextProvider;
this.configuration = configuration;
}
//~--- methods --------------------------------------------------------------
@@ -92,40 +96,37 @@ public class SecurityFilter 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();
if (securityContext != null)
String uri =
request.getRequestURI().substring(request.getContextPath().length());
if (!uri.startsWith(URL_AUTHENTICATION))
{
String uri =
request.getRequestURI().substring(request.getContextPath().length());
if (!uri.startsWith(URL_AUTHENTICATION))
if (hasPermission(subject))
{
if (hasPermission(securityContext))
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request,
securityContext.getUser()), response);
}
else if (securityContext.isAuthenticated())
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
chain.doFilter(new SecurityHttpServletRequestWrapper(request,
getUser(subject)), response);
}
else if (subject.isAuthenticated())
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else if (configuration.isAnonymousAccessEnabled())
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
chain.doFilter(request, response);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
chain.doFilter(request, response);
}
}
@@ -135,17 +136,42 @@ public class SecurityFilter extends HttpFilter
* Method description
*
*
* @param securityContext
* @param subject
*
* @return
*/
protected boolean hasPermission(WebSecurityContext securityContext)
protected boolean hasPermission(Subject subject)
{
return securityContext.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 = SCMContext.ANONYMOUS;
}
return user;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
private ScmConfiguration configuration;
}

View File

@@ -47,7 +47,6 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.TransformFilter;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import sonia.scm.security.SecurityContext;
import sonia.scm.util.CollectionAppender;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
@@ -87,11 +86,9 @@ public class DefaultGroupManager extends AbstractGroupManager
* @param groupListenerProvider
*/
@Inject
public DefaultGroupManager(Provider<SecurityContext> securityContextProvider,
GroupDAO groupDAO,
public DefaultGroupManager(GroupDAO groupDAO,
Provider<Set<GroupListener>> groupListenerProvider)
{
this.securityContextProvider = securityContextProvider;
this.groupDAO = groupDAO;
this.groupListenerProvider = groupListenerProvider;
}
@@ -136,7 +133,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
if (groupDAO.contains(group.getName()))
{
@@ -167,7 +164,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
String name = group.getName();
@@ -218,7 +215,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
String name = group.getName();
@@ -253,7 +250,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
Group fresh = groupDAO.get(group.getName());
@@ -346,7 +343,7 @@ public class DefaultGroupManager extends AbstractGroupManager
@Override
public Collection<Group> getAll(Comparator<Group> comparator)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
List<Group> groups = new ArrayList<Group>();
@@ -378,7 +375,7 @@ public class DefaultGroupManager extends AbstractGroupManager
public Collection<Group> getAll(Comparator<Group> comparator, int start,
int limit)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
return Util.createSubCollection(groupDAO.getAll(), comparator,
new CollectionAppender<Group>()
@@ -449,7 +446,4 @@ public class DefaultGroupManager extends AbstractGroupManager
/** Field description */
private Provider<Set<GroupListener>> groupListenerProvider;
/** Field description */
private Provider<SecurityContext> securityContextProvider;
}

View File

@@ -53,7 +53,6 @@ import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.io.ZipUnArchiver;
import sonia.scm.net.HttpClient;
import sonia.scm.security.SecurityContext;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
@@ -123,12 +122,10 @@ public class DefaultPluginManager
*/
@Inject
public DefaultPluginManager(SCMContextProvider context,
Provider<SecurityContext> securityContextProvicer,
ScmConfiguration configuration, PluginLoader pluginLoader,
CacheManager cacheManager, Provider<HttpClient> clientProvider)
{
this.context = context;
this.securityContextProvicer = securityContextProvicer;
this.configuration = configuration;
this.cache = cacheManager.getCache(String.class, PluginCenter.class,
CACHE_NAME);
@@ -196,7 +193,7 @@ public class DefaultPluginManager
@Override
public void install(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
PluginCenter center = getPluginCenter();
@@ -230,7 +227,7 @@ public class DefaultPluginManager
@Override
public void installPackage(InputStream packageStream) throws IOException
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
File tempDirectory = Files.createTempDir();
@@ -276,7 +273,7 @@ public class DefaultPluginManager
@Override
public void uninstall(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Plugin plugin = installedPlugins.get(id);
@@ -320,7 +317,7 @@ public class DefaultPluginManager
@Override
public void update(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
String[] idParts = id.split(":");
String groupId = idParts[0];
@@ -364,7 +361,7 @@ public class DefaultPluginManager
@Override
public PluginInformation get(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
PluginInformation result = null;
@@ -393,7 +390,7 @@ public class DefaultPluginManager
public Set<PluginInformation> get(PluginFilter filter)
{
AssertUtil.assertIsNotNull(filter);
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> infoSet = new HashSet<PluginInformation>();
@@ -412,7 +409,7 @@ public class DefaultPluginManager
@Override
public Collection<PluginInformation> getAll()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> infoSet = getInstalled();
@@ -430,7 +427,7 @@ public class DefaultPluginManager
@Override
public Collection<PluginInformation> getAvailable()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> availablePlugins = new HashSet<PluginInformation>();
Set<PluginInformation> centerPlugins = getPluginCenter().getPlugins();
@@ -455,7 +452,7 @@ public class DefaultPluginManager
@Override
public Set<PluginInformation> getAvailableUpdates()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
return get(FILTER_UPDATES);
}
@@ -469,7 +466,7 @@ public class DefaultPluginManager
@Override
public Set<PluginInformation> getInstalled()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> infoSet = new LinkedHashSet<PluginInformation>();
@@ -765,9 +762,6 @@ public class DefaultPluginManager
/** Field description */
private AetherPluginHandler pluginHandler;
/** Field description */
private Provider<SecurityContext> securityContextProvicer;
/** Field description */
private Unmarshaller unmarshaller;
}

View File

@@ -36,10 +36,15 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.concurrent.SubjectAwareExecutorService;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,15 +60,12 @@ import sonia.scm.util.AssertUtil;
import sonia.scm.util.CollectionAppender;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -94,10 +96,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
/**
* Constructs ...
*
*
*
*
*
* @param configuration
* @param contextProvider
* @param keyGenerator
@@ -110,18 +108,18 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
@Inject
public DefaultRepositoryManager(ScmConfiguration configuration,
SCMContextProvider contextProvider, KeyGenerator keyGenerator,
Provider<WebSecurityContext> securityContextProvider,
RepositoryDAO repositoryDAO, Set<RepositoryHandler> handlerSet,
Provider<Set<RepositoryListener>> repositoryListenersProvider,
Provider<Set<RepositoryHook>> repositoryHooksProvider)
{
this.configuration = configuration;
this.securityContextProvider = securityContextProvider;
this.keyGenerator = keyGenerator;
this.repositoryDAO = repositoryDAO;
this.repositoryListenersProvider = repositoryListenersProvider;
this.repositoryHooksProvider = repositoryHooksProvider;
this.executorService = Executors.newCachedThreadPool();
this.executorService =
new SubjectAwareExecutorService(Executors.newCachedThreadPool());
handlerMap = new HashMap<String, RepositoryHandler>();
types = new HashSet<Type>();
@@ -169,7 +167,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
repository.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
assertIsAdmin();
AssertUtil.assertIsValid(repository);
if (repositoryDAO.contains(repository))
@@ -475,7 +473,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
@Override
public Collection<Repository> getAll(Comparator<Repository> comparator)
{
List<Repository> repositories = new ArrayList<Repository>();
List<Repository> repositories = Lists.newArrayList();
for (Repository repository : repositoryDAO.getAll())
{
@@ -603,7 +601,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
@Override
public Collection<Type> getConfiguredTypes()
{
List<Type> validTypes = new ArrayList<Type>();
List<Type> validTypes = Lists.newArrayList();
for (RepositoryHandler handler : handlerMap.values())
{
@@ -867,25 +865,44 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
/**
* Method description
*
*/
private void assertIsAdmin()
{
if (!SecurityUtils.getSubject().hasRole("admin"))
{
throw new ScmSecurityException("admin role is required");
}
}
/**
* TODO use {@link Subject#checkPermission(org.apache.shiro.authz.Permission)}
* in version 2.x.
*
*
* @param repository
*/
private void assertIsOwner(Repository repository)
{
PermissionUtil.assertPermission(repository, securityContextProvider,
PermissionType.OWNER);
if (!isPermitted(repository, PermissionType.OWNER))
{
throw new ScmSecurityException(
"owner permission is required, access denied");
}
}
/**
* Method description
*
* TODO use {@link Subject#checkPermission(org.apache.shiro.authz.Permission)}
* in version 2.x.
*
* @param repository
*/
private void assertIsReader(Repository repository)
{
PermissionUtil.assertPermission(repository, securityContextProvider,
PermissionType.READ);
if (!isReader(repository))
{
throw new ScmSecurityException(
"reader permission is required, access denied");
}
}
//~--- get methods ----------------------------------------------------------
@@ -944,6 +961,20 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
return result;
}
/**
* Method description
*
*
* @param repository
* @param type
*
* @return
*/
private boolean isPermitted(Repository repository, PermissionType type)
{
return PermissionUtil.hasPermission(configuration, repository, type);
}
/**
* Method description
*
@@ -954,8 +985,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
*/
private boolean isReader(Repository repository)
{
return PermissionUtil.hasPermission(repository, securityContextProvider,
PermissionType.READ);
return isPermitted(repository, PermissionType.READ);
}
//~--- fields ---------------------------------------------------------------
@@ -981,9 +1011,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
/** Field description */
private Provider<Set<RepositoryListener>> repositoryListenersProvider;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private Set<Type> types;
}

View File

@@ -37,15 +37,16 @@ package sonia.scm.search;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
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.cache.Cache;
import sonia.scm.util.SecurityUtil;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -77,11 +78,10 @@ public class SearchHandler<T>
* @param cache
* @param searchable
*/
public SearchHandler(Provider<WebSecurityContext> securityContextProvider,
Cache<String, SearchResults> cache,
Searchable<T> searchable)
public SearchHandler(Cache<String, SearchResults> cache,
Searchable<T> searchable)
{
this.securityContextProvider = securityContextProvider;
this.cache = cache;
this.searchable = searchable;
}
@@ -107,9 +107,14 @@ public class SearchHandler<T>
* @return
*/
public SearchResults search(String queryString,
Function<T, SearchResult> function)
Function<T, SearchResult> function)
{
SecurityUtil.assertIsNotAnonymous(securityContextProvider);
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("Authentication is required");
}
if (Util.isEmpty(queryString))
{
@@ -202,9 +207,6 @@ public class SearchHandler<T>
/** Field description */
protected Searchable<T> searchable;
/** Field description */
protected Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private int maxResults = 5;

View File

@@ -0,0 +1,149 @@
/**
* 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.Splitter;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.PermissionType;
//~--- JDK imports ------------------------------------------------------------
import java.util.Iterator;
import java.util.Locale;
/**
*
* @author Sebastian Sdorra
*/
public class RepositoryPermissionResolver implements PermissionResolver
{
/** Field description */
private static final String TYPE_REPOSITORY = "repository";
/**
* the logger for RepositoryPermissionResolver
*/
private static final Logger logger =
LoggerFactory.getLogger(RepositoryPermissionResolver.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param permissionString
*
* @return
*/
@Override
public RepositoryPermission resolvePermission(String permissionString)
{
RepositoryPermission permission = null;
Iterator<String> permissionIt =
Splitter.on(':').omitEmptyStrings().trimResults().split(
permissionString).iterator();
if (permissionIt.hasNext())
{
String type = permissionIt.next();
if (type.equals(TYPE_REPOSITORY))
{
permission = createRepositoryPermission(permissionIt);
}
else if (logger.isWarnEnabled())
{
logger.warn("permission '{}' is not a repository permission",
permissionString);
}
}
return permission;
}
/**
* Method description
*
*
* @param permissionIt
*
* @return
*/
private RepositoryPermission createRepositoryPermission(
Iterator<String> permissionIt)
{
RepositoryPermission permission = null;
if (permissionIt.hasNext())
{
String repositoryId = permissionIt.next();
if (permissionIt.hasNext())
{
try
{
String typeString = permissionIt.next();
typeString = typeString.trim().toUpperCase(Locale.ENGLISH);
PermissionType type = PermissionType.valueOf(typeString);
permission = new RepositoryPermission(repositoryId, type);
}
catch (IllegalArgumentException ex)
{
logger.warn("type is not a valid permission type", ex);
}
}
else if (logger.isWarnEnabled())
{
logger.warn("permission type is missing");
}
}
else if (logger.isWarnEnabled())
{
logger.warn("repository id and permission type is missing");
}
return permission;
}
}

View File

@@ -0,0 +1,777 @@
/**
* 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.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
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.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.group.GroupNames;
import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryListener;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.user.User;
import sonia.scm.user.UserDAO;
import sonia.scm.user.UserEventHack;
import sonia.scm.user.UserException;
import sonia.scm.user.UserListener;
import sonia.scm.user.UserManager;
import sonia.scm.util.Util;
import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.AuthenticationResult;
import sonia.scm.web.security.AuthenticationState;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class ScmRealm extends AuthorizingRealm
implements RepositoryListener, UserListener
{
/** Field description */
public static final String NAME = "scm";
/** Field description */
private static final String CACHE_NAME = "sonia.cache.authorizing";
/** Field description */
private static final String SCM_CREDENTIALS = "SCM_CREDENTIALS";
/**
* the logger for ScmRealm
*/
private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
*
* @param configuration
* @param cacheManager
* @param userManager
* @param groupManager
* @param repositoryManager
* @param repositoryDAO
* @param userDAO
* @param authenticator
* @param requestProvider
* @param responseProvider
*/
@Inject
public ScmRealm(ScmConfiguration configuration, CacheManager cacheManager,
UserManager userManager, GroupManager groupManager,
RepositoryManager repositoryManager, RepositoryDAO repositoryDAO,
UserDAO userDAO, AuthenticationManager authenticator,
Provider<HttpServletRequest> requestProvider,
Provider<HttpServletResponse> responseProvider)
{
this.configuration = configuration;
this.userManager = userManager;
this.groupManager = groupManager;
this.repositoryDAO = repositoryDAO;
this.userDAO = userDAO;
this.authenticator = authenticator;
this.requestProvider = requestProvider;
this.responseProvider = responseProvider;
// init cache
this.cache = cacheManager.getCache(String.class, AuthorizationInfo.class,
CACHE_NAME);
// set token class
setAuthenticationTokenClass(UsernamePasswordToken.class);
// use own custom caching
setCachingEnabled(false);
setAuthenticationCachingEnabled(false);
setAuthorizationCachingEnabled(false);
// set components
setPermissionResolver(new RepositoryPermissionResolver());
// add listeners for caching
userManager.addListener(this);
repositoryManager.addListener(this);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param repository
* @param event
*/
@Override
public void onEvent(Repository repository, HandlerEvent event)
{
if (event.isPost())
{
if (logger.isDebugEnabled())
{
logger.debug("clear cache, because repository {} has changed",
repository.getName());
}
cache.clear();
}
}
/**
* Method description
*
*
* @param user
* @param event
*/
@Override
public void onEvent(User user, HandlerEvent event)
{
if (event.isPost())
{
if (logger.isDebugEnabled())
{
logger.debug(
"clear cache of user {}, because user properties have changed",
user.getName());
}
cache.remove(user.getId());
}
}
/**
* Method description
*
*
* @param token
*
* @param authToken
*
* @return
*
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authToken)
throws AuthenticationException
{
if (!(authToken instanceof UsernamePasswordToken))
{
throw new UnsupportedTokenException("ScmAuthenticationToken is required");
}
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
AuthenticationInfo info = null;
AuthenticationResult result =
authenticator.authenticate(requestProvider.get(), responseProvider.get(),
token.getUsername(), new String(token.getPassword()));
if ((result != null) && (AuthenticationState.SUCCESS == result.getState()))
{
info = createAuthenticationInfo(token, result);
}
else if ((result != null)
&& (AuthenticationState.NOT_FOUND == result.getState()))
{
throw new UnknownAccountException(
"unknown account ".concat(token.getUsername()));
}
else
{
throw new AccountException("authentication failed");
}
return info;
}
/**
* Method description
*
*
* @param principals
*
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals)
{
User user = principals.oneByType(User.class);
AuthorizationInfo info = cache.get(user.getId());
if (info == null)
{
if (logger.isTraceEnabled())
{
logger.trace("collect AuthorizationInfo for user {}", user.getName());
}
GroupNames groups = principals.oneByType(GroupNames.class);
info = createAuthorizationInfo(user, groups);
cache.put(user.getId(), info);
}
else if (logger.isTraceEnabled())
{
logger.trace("retrieve AuthorizationInfo for user {} from cache",
user.getName());
}
return info;
}
/**
* Method description
*
*
* @param request
* @param password
* @param ar
*
* @return
*/
private Set<String> authenticate(HttpServletRequest request, String password,
AuthenticationResult ar)
{
Set<String> groupSet = null;
User user = ar.getUser();
try
{
groupSet = createGroupSet(ar);
// check for admin user
checkForAuthenticatedAdmin(user, groupSet);
// store user
User dbUser = userDAO.get(user.getName());
if (dbUser != null)
{
checkDBForAdmin(user, dbUser);
checkDBForActive(user, dbUser);
}
// create new user
else if (user.isValid())
{
user.setCreationDate(System.currentTimeMillis());
// TODO find a better way
UserEventHack.fireEvent(userManager, user, HandlerEvent.BEFORE_CREATE);
userDAO.add(user);
UserEventHack.fireEvent(userManager, user, HandlerEvent.CREATE);
}
else if (logger.isErrorEnabled())
{
logger.error("could not create user {}, beacause it is not valid",
user.getName());
}
if (user.isActive())
{
if (logger.isDebugEnabled())
{
logGroups(user, groupSet);
}
// store encrypted credentials in session
String credentials = user.getName();
if (Util.isNotEmpty(password))
{
credentials = credentials.concat(":").concat(password);
}
credentials = CipherUtil.getInstance().encode(credentials);
request.getSession(true).setAttribute(SCM_CREDENTIALS, credentials);
}
else
{
String msg = "user ".concat(user.getName()).concat(" is deactivated");
if (logger.isWarnEnabled())
{
logger.warn(msg);
}
throw new DisabledAccountException(msg);
}
}
catch (Exception ex)
{
logger.error("authentication failed", ex);
throw new AuthenticationException("authentication failed", ex);
}
return groupSet;
}
/**
* Method description
*
*
* @param user
* @param dbUser
*/
private void checkDBForActive(User user, User dbUser)
{
// user is deactivated by database
if (!dbUser.isActive())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} is marked as deactivated by local database",
user.getName());
}
user.setActive(false);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*
* @throws IOException
* @throws UserException
*/
private void checkDBForAdmin(User user, User dbUser)
throws UserException, IOException
{
// if database user is an admin, set admin for the current user
if (dbUser.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} of type {} is marked as admin by local database",
user.getName(), user.getType());
}
user.setAdmin(true);
}
// modify existing user, copy properties except password and admin
if (user.copyProperties(dbUser, false))
{
userManager.modify(dbUser);
}
}
/**
* Method description
*
*
* @param user
* @param groupSet
*/
private void checkForAuthenticatedAdmin(User user, Set<String> groupSet)
{
if (!user.isAdmin())
{
user.setAdmin(isAdmin(user, groupSet));
if (logger.isDebugEnabled() && user.isAdmin())
{
logger.debug("user {} is marked as admin by configuration",
user.getName());
}
}
else if (logger.isDebugEnabled())
{
logger.debug("authenticator {} marked user {} as admin", user.getType(),
user.getName());
}
}
/**
* Method description
*
*
* @param user
* @param groups
*
* @return
*/
private List<org.apache.shiro.authz.Permission> collectRepositoryPermissions(
User user, GroupNames groups)
{
List<org.apache.shiro.authz.Permission> permissions = Lists.newArrayList();
for (Repository repository : repositoryDAO.getAll())
{
if (logger.isTraceEnabled())
{
logger.trace("collect permissions for repository {} and user {}",
repository.getName(), user.getName());
}
collectRepositoryPermissions(permissions, repository, user, groups);
}
return permissions;
}
/**
* Method description
*
*
* @param permissions
* @param repository
* @param user
* @param groups
*/
private void collectRepositoryPermissions(
List<org.apache.shiro.authz.Permission> permissions, Repository repository,
User user, GroupNames groups)
{
List<Permission> repositoryPermissions = repository.getPermissions();
if (Util.isNotEmpty(repositoryPermissions))
{
for (Permission permission : repositoryPermissions)
{
if (isUserPermission(user, groups, permission))
{
RepositoryPermission rp = new RepositoryPermission(repository,
permission.getType());
if (logger.isTraceEnabled())
{
logger.trace("add repository permission {} for user {}", rp,
user.getName());
}
permissions.add(rp);
}
}
}
else if (logger.isTraceEnabled())
{
logger.trace("repository {} has not permission entries",
repository.getName());
}
}
/**
* Method description
*
*
* @param token
* @param result
*
* @return
*/
private AuthenticationInfo createAuthenticationInfo(
UsernamePasswordToken token, AuthenticationResult result)
{
User user = result.getUser();
Collection<String> groups = authenticate(requestProvider.get(),
new String(token.getPassword()), result);
SimplePrincipalCollection collection = new SimplePrincipalCollection();
/*
* the first (primary) principal should be a unique identifier
*/
collection.add(user.getId(), NAME);
collection.add(user, NAME);
collection.add(new GroupNames(groups), NAME);
return new SimpleAuthenticationInfo(collection, token.getPassword());
}
/**
* Method description
*
*
* @param user
* @param groups
*
* @return
*/
private AuthorizationInfo createAuthorizationInfo(User user,
GroupNames groups)
{
Set<String> roles = Sets.newHashSet();
List<org.apache.shiro.authz.Permission> permissions = null;
roles.add(Role.USER);
if (user.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("grant admin role for user {}", user.getName());
}
roles.add(Role.ADMIN);
permissions = Lists.newArrayList();
permissions.add(new RepositoryPermission(RepositoryPermission.WILDCARD,
PermissionType.OWNER));
}
else
{
permissions = collectRepositoryPermissions(user, groups);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
info.addObjectPermissions(permissions);
return info;
}
/**
* Method description
*
*
* @param ar
*
* @return
*/
private Set<String> createGroupSet(AuthenticationResult ar)
{
Set<String> groupSet = Sets.newHashSet();
// load external groups
Collection<String> extGroups = ar.getGroups();
if (extGroups != null)
{
groupSet.addAll(extGroups);
}
// load internal groups
loadGroups(ar.getUser(), groupSet);
return groupSet;
}
/**
* Method description
*
*
*
* @param user
* @param groupSet
*/
private void loadGroups(User user, Set<String> groupSet)
{
Collection<Group> groupCollection =
groupManager.getGroupsForMember(user.getName());
if (groupCollection != null)
{
for (Group group : groupCollection)
{
groupSet.add(group.getName());
}
}
}
/**
* Method description
*
*
* @param user
* @param groups
*/
private void logGroups(User user, Set<String> groups)
{
StringBuilder msg = new StringBuilder("user ");
msg.append(user.getName());
if (Util.isNotEmpty(groups))
{
msg.append(" is member of ");
Joiner.on(", ").appendTo(msg, groups);
}
else
{
msg.append(" is not a member of a group");
}
logger.debug(msg.toString());
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
*
*
* @param user
* @param groups
* @return
*/
private boolean isAdmin(User user, Collection<String> groups)
{
boolean result = false;
Set<String> adminUsers = configuration.getAdminUsers();
if (adminUsers != null)
{
result = adminUsers.contains(user.getName());
}
if (!result)
{
Set<String> adminGroups = configuration.getAdminGroups();
if (adminGroups != null)
{
result = Util.containsOne(adminGroups, groups);
}
}
return result;
}
/**
* Method description
*
*
* @param user
* @param groups
* @param perm
*
* @return
*/
private boolean isUserPermission(User user, GroupNames groups,
Permission perm)
{
//J-
return (perm.isGroupPermission() && groups.contains(perm.getName()))
|| ((!perm.isGroupPermission()) && user.getName().equals(perm.getName()));
//J+
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private AuthenticationManager authenticator;
/** Field description */
private Cache<String, AuthorizationInfo> cache;
/** Field description */
private ScmConfiguration configuration;
/** Field description */
private GroupManager groupManager;
/** Field description */
private RepositoryDAO repositoryDAO;
/** Field description */
private Provider<HttpServletRequest> requestProvider;
/** Field description */
private Provider<HttpServletResponse> responseProvider;
/** Field description */
private UserDAO userDAO;
/** Field description */
private UserManager userManager;
}

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;
/**

View File

@@ -39,6 +39,9 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,13 +50,13 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.TransformFilter;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.CollectionAppender;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -104,11 +107,9 @@ public class DefaultUserManager extends AbstractUserManager
* @param userListenerProvider
*/
@Inject
public DefaultUserManager(
Provider<WebSecurityContext> scurityContextProvider, UserDAO userDAO,
Provider<Set<UserListener>> userListenerProvider)
public DefaultUserManager(UserDAO userDAO,
Provider<Set<UserListener>> userListenerProvider)
{
this.scurityContextProvider = scurityContextProvider;
this.userDAO = userDAO;
this.userListenerProvider = userListenerProvider;
}
@@ -166,9 +167,14 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("create user {} of type {}", user.getName(), user.getType());
}
User currentUser = SecurityUtil.getCurrentUser(scurityContextProvider);
Subject subject = SecurityUtils.getSubject();
if (!user.equals(currentUser) &&!currentUser.isAdmin())
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin account is required");
}
@@ -202,7 +208,7 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("delete user {} of type {}", user.getName(), user.getType());
}
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
String name = user.getName();
@@ -259,9 +265,17 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("modify user {} of type {}", user.getName(), user.getType());
}
User currentUser = SecurityUtil.getCurrentUser(scurityContextProvider);
Subject subject = SecurityUtils.getSubject();
if (!user.getName().equals(currentUser.getName()) &&!currentUser.isAdmin())
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
User currentUser = subject.getPrincipals().oneByType(User.class);
if (!user.getName().equals(currentUser.getName())
&&!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin account is required");
}
@@ -299,7 +313,7 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("refresh user {} of type {}", user.getName(), user.getType());
}
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
User fresh = userDAO.get(user.getName());
@@ -328,7 +342,7 @@ public class DefaultUserManager extends AbstractUserManager
}
return SearchUtil.search(searchRequest, userDAO.getAll(),
new TransformFilter<User>()
new TransformFilter<User>()
{
@Override
public User accept(User user)
@@ -336,7 +350,7 @@ public class DefaultUserManager extends AbstractUserManager
User result = null;
if (SearchUtil.matchesOne(searchRequest, user.getName(),
user.getDisplayName(), user.getMail()))
user.getDisplayName(), user.getMail()))
{
result = user.clone();
}
@@ -394,7 +408,7 @@ public class DefaultUserManager extends AbstractUserManager
@Override
public Collection<User> getAll(Comparator<User> comparator)
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
List<User> users = new ArrayList<User>();
@@ -424,12 +438,12 @@ public class DefaultUserManager extends AbstractUserManager
*/
@Override
public Collection<User> getAll(Comparator<User> comaparator, int start,
int limit)
int limit)
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
return Util.createSubCollection(userDAO.getAll(), comaparator,
new CollectionAppender<User>()
new CollectionAppender<User>()
{
@Override
public void append(Collection<User> collection, User item)
@@ -525,15 +539,12 @@ public class DefaultUserManager extends AbstractUserManager
}
catch (JAXBException ex)
{
logger.error(ex.getMessage(), ex);
logger.error("could not create default accounts", ex);
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> scurityContextProvider;
/** Field description */
private UserDAO userDAO;

View File

@@ -0,0 +1,76 @@
/**
* 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.user;
//~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.HandlerEvent;
/**
*
* @author Sebastian Sdorra
*/
public class UserEventHack
{
/**
* the logger for UserEventHack
*/
private static final Logger logger =
LoggerFactory.getLogger(UserEventHack.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param userManager
* @param user
* @param event
*/
public static void fireEvent(UserManager userManager, User user,
HandlerEvent event)
{
if (userManager instanceof AbstractUserManager)
{
((AbstractUserManager) userManager).fireEvent(user, event);
}
else if (logger.isWarnEnabled())
{
logger.warn("user manager is not an instance of AbstractUserManager");
}
}
}

View File

@@ -36,9 +36,9 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
@@ -73,13 +73,12 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
* Constructs ...
*
*
* @param securityContextProvider
* @param configuration
*/
@Inject
public ApiBasicAuthenticationFilter(
Provider<WebSecurityContext> securityContextProvider)
public ApiBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(securityContextProvider);
super(configuration);
}
//~--- methods --------------------------------------------------------------
@@ -97,14 +96,14 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
// skip filter on authentication resource
if (request.getRequestURI().contains(URI_LOGIN)
|| request.getRequestURI().contains(URI_STATE)
|| request.getRequestURI().contains(URI_LOGOUT))
|| request.getRequestURI().contains(URI_STATE)
|| request.getRequestURI().contains(URI_LOGOUT))
{
chain.doFilter(request, response);
}
@@ -127,9 +126,8 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
*/
@Override
protected void handleUnauthorized(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
chain.doFilter(request, response);
}

View File

@@ -35,30 +35,26 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.servlet.SessionScoped;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.security.CipherUtil;
import sonia.scm.group.GroupNames;
import sonia.scm.security.Tokens;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -68,7 +64,6 @@ import javax.servlet.http.HttpSession;
*
* @author Sebastian Sdorra
*/
@SessionScoped
public class BasicSecurityContext implements WebSecurityContext
{
@@ -88,21 +83,14 @@ public class BasicSecurityContext implements WebSecurityContext
* Constructs ...
*
*
*
* @param configuration
* @param authenticator
* @param groupManager
* @param userManager
*/
@Inject
public BasicSecurityContext(ScmConfiguration configuration,
AuthenticationManager authenticator,
GroupManager groupManager,
UserManager userManager)
UserManager userManager)
{
this.configuration = configuration;
this.authenticator = authenticator;
this.groupManager = groupManager;
this.userManager = userManager;
}
@@ -121,22 +109,27 @@ public class BasicSecurityContext implements WebSecurityContext
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
HttpServletResponse response, String username, String password)
{
if ( logger.isTraceEnabled() ){
logger.trace("start authentication for user {}", username);
}
AuthenticationResult ar = authenticator.authenticate(request, response,
username, password);
User user = null;
if ( logger.isTraceEnabled() ){
logger.trace("authentication ends with {}", ar);
}
if ((ar != null) && (ar.getState() == AuthenticationState.SUCCESS))
try
{
authenticate(request, password, ar);
Subject subject = SecurityUtils.getSubject();
subject.login(Tokens.createAuthenticationToken(request, username,
password));
user = subject.getPrincipals().oneByType(User.class);
}
catch (AuthenticationException ex)
{
if (logger.isWarnEnabled())
{
logger.warn("authentication failed", ex);
}
}
return user;
@@ -152,8 +145,7 @@ public class BasicSecurityContext implements WebSecurityContext
@Override
public void logout(HttpServletRequest request, HttpServletResponse response)
{
user = null;
groups = new HashSet<String>();
SecurityUtils.getSubject().logout();
HttpSession session = request.getSession(false);
@@ -174,12 +166,20 @@ public class BasicSecurityContext implements WebSecurityContext
@Override
public Collection<String> getGroups()
{
if (groups == null)
GroupNames groups = getPrincipal(GroupNames.class);
Collection<String> groupCollection = null;
if (groups != null)
{
groups = new HashSet<String>();
groupCollection = groups.getCollection();
}
else
{
groupCollection = Collections.EMPTY_LIST;
}
return groups;
return groupCollection;
}
/**
@@ -191,6 +191,8 @@ public class BasicSecurityContext implements WebSecurityContext
@Override
public User getUser()
{
User user = getPrincipal(User.class);
if ((user == null) && configuration.isAnonymousAccessEnabled())
{
user = userManager.get(USER_ANONYMOUS);
@@ -211,276 +213,27 @@ public class BasicSecurityContext implements WebSecurityContext
return getUser() != null;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param password
* @param ar
*/
private void authenticate(HttpServletRequest request, String password,
AuthenticationResult ar)
{
user = ar.getUser();
try
{
Set<String> groupSet = createGroupSet(ar);
// check for admin user
checkForAuthenticatedAdmin(user, groupSet);
// store user
User dbUser = userManager.get(user.getName());
if (dbUser != null)
{
checkDBForAdmin(user, dbUser);
checkDBForActive(user, dbUser);
}
// create new user
else
{
userManager.create(user);
}
if (user.isActive())
{
groups = groupSet;
if (logger.isDebugEnabled())
{
logGroups();
}
// store encrypted credentials in session
String credentials = user.getName();
if (Util.isNotEmpty(password))
{
credentials = credentials.concat(":").concat(password);
}
credentials = CipherUtil.getInstance().encode(credentials);
request.getSession(true).setAttribute(SCM_CREDENTIALS, credentials);
}
else
{
if (logger.isWarnEnabled())
{
logger.warn("user {} is deactivated", user.getName());
}
user = null;
groups = null;
}
}
catch (Exception ex)
{
user = null;
if (groups != null)
{
groups.clear();
}
logger.error("authentication failed", ex);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*/
private void checkDBForActive(User user, User dbUser)
{
// user is deactivated by database
if (!dbUser.isActive())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} is marked as deactivated by local database",
user.getName());
}
user.setActive(false);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*
* @throws IOException
* @throws UserException
*/
private void checkDBForAdmin(User user, User dbUser)
throws UserException, IOException
{
// if database user is an admin, set admin for the current user
if (dbUser.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} of type {} is marked as admin by local database",
user.getName(), user.getType());
}
user.setAdmin(true);
}
// modify existing user, copy properties except password and admin
if (user.copyProperties(dbUser, false))
{
userManager.modify(dbUser);
}
}
/**
* Method description
*
*
* @param user
* @param groupSet
*/
private void checkForAuthenticatedAdmin(User user, Set<String> groupSet)
{
if (!user.isAdmin())
{
user.setAdmin(isAdmin(groupSet));
if (logger.isDebugEnabled() && user.isAdmin())
{
logger.debug("user {} is marked as admin by configuration",
user.getName());
}
}
else if (logger.isDebugEnabled())
{
logger.debug("authenticator {} marked user {} as admin", user.getType(),
user.getName());
}
}
/**
* Method description
*
*
* @param ar
* @param clazz
* @param <T>
*
* @return
*/
private Set<String> createGroupSet(AuthenticationResult ar)
private <T> T getPrincipal(Class<T> clazz)
{
Set<String> groupSet = Sets.newHashSet();
T result = null;
Subject subject = SecurityUtils.getSubject();
// load external groups
Collection<String> extGroups = ar.getGroups();
if (extGroups != null)
if (subject.isAuthenticated())
{
groupSet.addAll(extGroups);
}
PrincipalCollection pc = subject.getPrincipals();
// load internal groups
loadGroups(groupSet);
return groupSet;
}
/**
* Method description
*
*
* @param groupSet
*/
private void loadGroups(Set<String> groupSet)
{
Collection<Group> groupCollection =
groupManager.getGroupsForMember(user.getName());
if (groupCollection != null)
{
for (Group group : groupCollection)
if (pc != null)
{
groupSet.add(group.getName());
}
}
}
/**
* Method description
*
*/
private void logGroups()
{
StringBuilder msg = new StringBuilder("user ");
msg.append(user.getName());
if (Util.isNotEmpty(groups))
{
msg.append(" is member of ");
Iterator<String> groupIt = groups.iterator();
while (groupIt.hasNext())
{
msg.append(groupIt.next());
if (groupIt.hasNext())
{
msg.append(", ");
}
}
}
else
{
msg.append(" is not a member of a group");
}
logger.debug(msg.toString());
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
*
* @param groups
* @return
*/
private boolean isAdmin(Collection<String> groups)
{
boolean result = false;
Set<String> adminUsers = configuration.getAdminUsers();
if (adminUsers != null)
{
result = adminUsers.contains(user.getName());
}
if (!result)
{
Set<String> adminGroups = configuration.getAdminGroups();
if (adminGroups != null)
{
result = Util.containsOne(adminGroups, groups);
result = pc.oneByType(clazz);
}
}
@@ -489,21 +242,9 @@ public class BasicSecurityContext implements WebSecurityContext
//~--- fields ---------------------------------------------------------------
/** Field description */
private AuthenticationManager authenticator;
/** Field description */
private ScmConfiguration configuration;
/** Field description */
private GroupManager groupManager;
/** Field description */
private Set<String> groups = new HashSet<String>();
/** Field description */
private User user;
/** Field description */
private UserManager userManager;
}

View File

@@ -89,9 +89,9 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
*/
@Inject
public ChainAuthenticatonManager(
Set<AuthenticationHandler> authenticationHandlerSet,
EncryptionHandler encryptionHandler, CacheManager cacheManager,
Provider<Set<AuthenticationListener>> authenticationListenerProvider)
Set<AuthenticationHandler> authenticationHandlerSet,
EncryptionHandler encryptionHandler, CacheManager cacheManager,
Provider<Set<AuthenticationListener>> authenticationListenerProvider)
{
AssertUtil.assertIsNotEmpty(authenticationHandlerSet);
AssertUtil.assertIsNotNull(cacheManager);
@@ -99,8 +99,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
this.encryptionHandler = encryptionHandler;
this.authenticationListenerProvider = authenticationListenerProvider;
this.cache = cacheManager.getCache(String.class,
AuthenticationCacheValue.class,
CACHE_NAME);
AuthenticationCacheValue.class, CACHE_NAME);
// addListeners(authenticationListeners);
}
@@ -120,7 +119,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
*/
@Override
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password)
HttpServletResponse response, String username, String password)
{
AssertUtil.assertIsNotEmpty(username);
AssertUtil.assertIsNotEmpty(password);
@@ -133,7 +132,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if (logger.isTraceEnabled())
{
logger.trace("no authentication result for user {} found in cache",
username);
username);
}
ar = doAuthentication(request, response, username, password);
@@ -141,7 +140,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if ((ar != null) && ar.isCacheable())
{
cache.put(username,
new AuthenticationCacheValue(ar, encryptedPassword));
new AuthenticationCacheValue(ar, encryptedPassword));
}
}
else if (logger.isDebugEnabled())
@@ -212,7 +211,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
* @return
*/
private AuthenticationResult doAuthentication(HttpServletRequest request,
HttpServletResponse response, String username, String password)
HttpServletResponse response, String username, String password)
{
AuthenticationResult ar = null;
@@ -226,7 +225,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if (logger.isTraceEnabled())
{
logger.trace("check authenticator {} for user {}",
authenticator.getClass(), username);
authenticator.getClass(), username);
}
try
@@ -237,12 +236,12 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if (logger.isDebugEnabled())
{
logger.debug("authenticator {} ends with result, {}",
authenticator.getClass().getName(), result);
authenticator.getClass().getName(), result);
}
if ((result != null) && (result.getState() != null)
&& (result.getState().isSuccessfully()
|| (result.getState() == AuthenticationState.FAILED)))
&& (result.getState().isSuccessfully()
|| (result.getState() == AuthenticationState.FAILED)))
{
if (result.getState().isSuccessfully() && (result.getUser() != null))
{
@@ -260,7 +259,9 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
}
catch (Exception ex)
{
logger.error(ex.getMessage(), ex);
logger.error(
"error durring authentication process of ".concat(
authenticator.getClass().getName()), ex);
}
}
@@ -279,7 +280,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
* @return
*/
private AuthenticationResult getCached(String username,
String encryptedPassword)
String encryptedPassword)
{
AuthenticationResult result = null;
AuthenticationCacheValue value = cache.get(username);
@@ -326,7 +327,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
{
this.authenticationResult =
new AuthenticationResult(ar.getUser().clone(), ar.getGroups(),
ar.getState());
ar.getState());
this.password = password;
}

View File

@@ -30,19 +30,29 @@
*/
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.group.GroupNames;
import sonia.scm.security.ScmRealm;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
@@ -77,15 +87,14 @@ public class DefaultAdministrationContext implements AdministrationContext
* @param injector
* @param userSessionProvider
* @param contextHolder
* @param securityManager
*/
@Inject
public DefaultAdministrationContext(Injector injector,
@Named("userSession") Provider<WebSecurityContext> userSessionProvider,
LocalSecurityContextHolder contextHolder)
org.apache.shiro.mgt.SecurityManager securityManager)
{
this.injector = injector;
this.userSessionProvider = userSessionProvider;
this.contextHolder = contextHolder;
this.securityManager = securityManager;
URL url = DefaultAdministrationContext.class.getResource(SYSTEM_ACCOUNT);
@@ -94,9 +103,9 @@ public class DefaultAdministrationContext implements AdministrationContext
throw new RuntimeException("could not find resource for system account");
}
User user = JAXB.unmarshal(url, User.class);
User adminUser = JAXB.unmarshal(url, User.class);
adminContext = new AdministrationSecurityContext(user);
principalCollection = createAdminCollection(adminUser);
}
//~--- methods --------------------------------------------------------------
@@ -112,24 +121,15 @@ public class DefaultAdministrationContext implements AdministrationContext
{
AssertUtil.assertIsNotNull(action);
if (logger.isWarnEnabled())
if (ThreadContext.getSecurityManager() != null)
{
String user = SecurityUtil.getUsername(userSessionProvider);
logger.warn("user {} executes {} as admin", user,
action.getClass().getName());
doRunAsInWebSessionContext(action);
}
else
{
doRunAsInNonWebSessionContext(action);
}
contextHolder.set(adminContext);
try
{
action.run();
}
finally
{
contextHolder.remove();
}
}
/**
@@ -146,17 +146,136 @@ public class DefaultAdministrationContext implements AdministrationContext
runAsAdmin(action);
}
/**
* Method description
*
*
* @param adminUser
*
* @return
*/
private PrincipalCollection createAdminCollection(User adminUser)
{
SimplePrincipalCollection collection = new SimplePrincipalCollection();
collection.add(adminUser.getId(), ScmRealm.NAME);
collection.add(adminUser, ScmRealm.NAME);
collection.add(new GroupNames(), ScmRealm.NAME);
return collection;
}
/**
* Method description
*
*
* @param action
*/
private void doRunAsInNonWebSessionContext(PrivilegedAction action)
{
if (logger.isTraceEnabled())
{
logger.trace("bind shiro security manager to current thread");
}
try
{
SecurityUtils.setSecurityManager(securityManager);
//J-
Subject subject = new Subject.Builder(securityManager)
.authenticated(true)
.principals(principalCollection)
.buildSubject();
//J+
ThreadState state = new SubjectThreadState(subject);
state.bind();
try
{
if (logger.isInfoEnabled())
{
logger.info("execute action {} in administration context",
action.getClass().getName());
}
action.run();
}
finally
{
state.clear();
}
}
finally
{
SecurityUtils.setSecurityManager(null);
}
}
/**
* Method description
*
*
* @param action
*/
private void doRunAsInWebSessionContext(PrivilegedAction action)
{
Subject subject = SecurityUtils.getSubject();
String principal = (String) subject.getPrincipal();
if (logger.isInfoEnabled())
{
String username = null;
if (subject.isAuthenticated())
{
username = principal;
}
else
{
username = SCMContext.USER_ANONYMOUS;
}
logger.info("user {} executes {} as admin", username,
action.getClass().getName());
}
subject.runAs(principalCollection);
try
{
action.run();
}
finally
{
PrincipalCollection collection = subject.releaseRunAs();
if (logger.isDebugEnabled())
{
logger.debug("release runas for user {}",
collection.getPrimaryPrincipal());
}
if (!subject.getPrincipal().equals(principal))
{
logger.error("release runas failed, {} is not equal with {}, logout.",
subject.getPrincipal(), principal);
subject.logout();
}
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private AdministrationSecurityContext adminContext;
/** Field description */
private LocalSecurityContextHolder contextHolder;
/** Field description */
private Injector injector;
/** Field description */
private Provider<WebSecurityContext> userSessionProvider;
private PrincipalCollection principalCollection;
/** Field description */
private org.apache.shiro.mgt.SecurityManager securityManager;
}

View File

@@ -42,7 +42,9 @@ import sonia.scm.SCMContext;
/**
*
* @author Sebastian Sdorra
* @deprecated
*/
@Deprecated
public class SecurityUtil
{

View File

@@ -50,7 +50,7 @@
</encoder>
</appender>
<logger name="sonia.scm" level="DEBUG" />
<logger name="sonia.scm" level="TRACE" />
<!-- suppress massive gzip logging -->
<logger name="sonia.scm.filter.GZipFilter" level="WARN" />

View File

@@ -37,6 +37,9 @@ package sonia.scm.repository;
import com.google.inject.Provider;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
import sonia.scm.Type;
@@ -93,6 +96,20 @@ public class DefaultRepositoryManagerTest extends RepositoryManagerTestBase
assertNull(m.getFromUri("/git/project1/test-3/ka/some/path"));
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*/
@Before
public void setAdminSubject()
{
Subject admin = MockUtil.createAdminSubject();
setSubject(admin);
}
//~--- methods --------------------------------------------------------------
/**
@@ -139,8 +156,8 @@ public class DefaultRepositoryManagerTest extends RepositoryManagerTestBase
ScmConfiguration configuration = new ScmConfiguration();
return new DefaultRepositoryManager(configuration, contextProvider,
new DefaultKeyGenerator(), MockUtil.getAdminSecurityContextProvider(),
repositoryDAO, handlerSet, listenerProvider, hookProvider);
new DefaultKeyGenerator(), repositoryDAO, handlerSet, listenerProvider,
hookProvider);
}
/**

View File

@@ -30,79 +30,74 @@
*/
package sonia.scm.web.security;
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.PermissionType;
import static org.junit.Assert.*;
/**
*
* @author Sebastian Sdorra
*/
public class SecurityContextProvider implements Provider<WebSecurityContext>
public class RepositoryPermissionResolverTest
{
/** the logger for SecurityContextProvider */
private static final Logger logger =
LoggerFactory.getLogger(SecurityContextProvider.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param sessionContext
* @param localContext
*/
@Inject
public SecurityContextProvider(
@Named("userSession") Provider<WebSecurityContext> sessionContext,
LocalSecurityContextHolder localContext)
{
this.sessionContext = sessionContext;
this.localContext = localContext;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public WebSecurityContext get()
@Test
public void testResolvePermission()
{
WebSecurityContext context = localContext.get();
RepositoryPermissionResolver resolver = new RepositoryPermissionResolver();
RepositoryPermission p = resolver.resolvePermission("repository:scm:read");
if (context == null)
{
context = sessionContext.get();
}
else if (logger.isDebugEnabled())
{
String user = SecurityUtil.getUsername(sessionContext);
assertNotNull(p);
assertEquals("scm", p.getRepositoryId());
assertEquals(PermissionType.READ, p.getPermissionType());
logger.debug("return system session for user {}", user);
}
p = resolver.resolvePermission("repository:asd:wRitE");
assertNotNull(p);
assertEquals("asd", p.getRepositoryId());
assertEquals(PermissionType.WRITE, p.getPermissionType());
return context;
p = resolver.resolvePermission("repository:*:OWNER");
assertNotNull(p);
assertEquals("*", p.getRepositoryId());
assertEquals(PermissionType.OWNER, p.getPermissionType());
}
//~--- fields ---------------------------------------------------------------
/**
* Method description
*
*/
@Test
public void testResolveUnknownPermission()
{
RepositoryPermissionResolver resolver = new RepositoryPermissionResolver();
RepositoryPermission p = resolver.resolvePermission("user:scm:read");
/** Field description */
private LocalSecurityContextHolder localContext;
assertNull(p);
/** Field description */
private Provider<WebSecurityContext> sessionContext;
p = resolver.resolvePermission("group:asd:wRitE");
assertNull(p);
}
/**
* Method description
*
*/
@Test
public void testResolveUnknownTypePermission()
{
RepositoryPermissionResolver resolver = new RepositoryPermissionResolver();
RepositoryPermission p = resolver.resolvePermission("repository:scm:asd");
assertNull(p);
}
}

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

@@ -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;
//~--- non-JDK imports --------------------------------------------------------

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

@@ -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;
//~--- non-JDK imports --------------------------------------------------------

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;
//~--- non-JDK imports --------------------------------------------------------

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;
//~--- non-JDK imports --------------------------------------------------------

View File

@@ -48,6 +48,7 @@ import static org.mockito.Mockito.*;
import java.util.HashSet;
import java.util.Set;
import org.junit.Before;
/**
*
@@ -55,6 +56,11 @@ import java.util.Set;
*/
public class DefaultUserManagerTest extends UserManagerTestBase
{
@Before
public void setAdminSubject(){
setSubject(MockUtil.createAdminSubject());
}
/**
* Method description
@@ -74,8 +80,7 @@ public class DefaultUserManagerTest extends UserManagerTestBase
when(listenerProvider.get()).thenReturn(new HashSet<UserListener>());
XmlUserDAO userDAO = new XmlUserDAO(factory);
return new DefaultUserManager(MockUtil.getAdminSecurityContextProvider(),
userDAO, listenerProvider);
return new DefaultUserManager(userDAO, listenerProvider);
}
}

View File

@@ -44,10 +44,11 @@ import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.MessageDigestEncryptionHandler;
import sonia.scm.store.JAXBStoreFactory;
import sonia.scm.store.StoreFactory;
import sonia.scm.user.DefaultUserManager;
import sonia.scm.user.User;
import sonia.scm.user.UserListener;
import sonia.scm.user.UserTestData;
import sonia.scm.user.DefaultUserManager;
import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.util.MockUtil;
import static org.junit.Assert.*;
@@ -61,7 +62,6 @@ import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sonia.scm.user.xml.XmlUserDAO;
/**
*
@@ -141,13 +141,16 @@ public class DefaultAuthenticationHandlerTest extends AbstractTestBase
when(listenerProvider.get()).thenReturn(new HashSet<UserListener>());
XmlUserDAO userDAO = new XmlUserDAO(storeFactory);
DefaultUserManager userManager =
new DefaultUserManager(MockUtil.getAdminSecurityContextProvider(),
userDAO, listenerProvider);
setSubject(MockUtil.createAdminSubject());
DefaultUserManager userManager = new DefaultUserManager(userDAO,
listenerProvider);
userManager.init(contextProvider);
userManager.create(slarti);
clearSubject();
handler = new DefaultAuthenticationHandler(userManager, enc);
handler.init(contextProvider);
request = MockUtil.getHttpServletRequest();

View File

@@ -1 +1,33 @@
<#--
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
-->
Hello ${name}!

View File

@@ -1 +1,33 @@
<#--
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
-->
Hello ${notAvailable}!

View File

@@ -1 +1,33 @@
<#--
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
-->
Hello </#list>!