diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 84b27d8277..b881c3e44f 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -42,6 +42,8 @@ import com.google.inject.servlet.RequestScoped; import com.google.inject.servlet.ServletModule; import com.google.inject.throwingproviders.ThrowingProviderBinder; +import org.apache.shiro.authz.permission.PermissionResolver; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,6 +92,7 @@ import sonia.scm.security.DefaultSecuritySystem; import sonia.scm.security.EncryptionHandler; import sonia.scm.security.KeyGenerator; import sonia.scm.security.MessageDigestEncryptionHandler; +import sonia.scm.security.RepositoryPermissionResolver; import sonia.scm.security.SecurityContext; import sonia.scm.security.SecuritySystem; import sonia.scm.store.BlobStoreFactory; @@ -273,6 +276,7 @@ public class ScmServletModule extends ServletModule pluginLoader.processExtensions(binder()); // bind security stuff + bind(PermissionResolver.class, RepositoryPermissionResolver.class); bind(AuthenticationManager.class, ChainAuthenticatonManager.class); bind(SecurityContext.class).to(BasicSecurityContext.class); bind(WebSecurityContext.class).to(BasicSecurityContext.class); diff --git a/scm-webapp/src/main/java/sonia/scm/security/PermissionCollector.java b/scm-webapp/src/main/java/sonia/scm/security/PermissionCollector.java new file mode 100644 index 0000000000..3ce66ca926 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/PermissionCollector.java @@ -0,0 +1,259 @@ +/** + * 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.Predicate; +import com.google.common.collect.ImmutableList.Builder; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.apache.shiro.authz.Permission; +import org.apache.shiro.authz.permission.PermissionResolver; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.group.GroupNames; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryDAO; +import sonia.scm.user.User; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * + * @author Sebastian Sdorra + */ +@Singleton +public class PermissionCollector +{ + + /** + * the logger for PermissionCollector + */ + private static final Logger logger = + LoggerFactory.getLogger(PermissionCollector.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param repositoryDAO + * @param securitySystem + * @param resolver + */ + @Inject + public PermissionCollector(RepositoryDAO repositoryDAO, + SecuritySystem securitySystem, PermissionResolver resolver) + { + this.repositoryDAO = repositoryDAO; + this.securitySystem = securitySystem; + this.resolver = resolver; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param user + * @param groups + * + * @return + */ + public List collect(User user, GroupNames groups) + { + Builder builder = new Builder(); + + collectRepositoryPermissions(builder, user, groups); + collectGlobalPermissions(builder, user, groups); + + return builder.build(); + } + + /** + * Method description + * + * + * + * @param builder + * @param user + * @param groups + * + * @return + */ + private void collectGlobalPermissions(Builder builder, + final User user, final GroupNames groups) + { + if (logger.isTraceEnabled()) + { + logger.trace("collect global permissions for user {}", user.getName()); + } + + List globalPermissions = + securitySystem.getPermissions(new Predicate() + { + + @Override + public boolean apply(AssignedPermission input) + { + return isUserPermission(user, groups, input); + } + }); + + for (StoredAssignedPermission gp : globalPermissions) + { + if (logger.isTraceEnabled()) + { + logger.trace("add permission {} for user {}", gp.getPermission(), + user.getName()); + } + + Permission permission = resolver.resolvePermission(gp.getPermission()); + + if (permission != null) + { + builder.add(permission); + } + } + } + + /** + * Method description + * + * + * + * @param builder + * @param user + * @param groups + * + * @return + */ + private void collectRepositoryPermissions(Builder builder, + User user, GroupNames groups) + { + for (Repository repository : repositoryDAO.getAll()) + { + if (logger.isTraceEnabled()) + { + logger.trace("collect permissions for repository {} and user {}", + repository.getName(), user.getName()); + } + + collectRepositoryPermissions(builder, repository, user, groups); + } + } + + /** + * Method description + * + * + * @param permissions + * + * @param builder + * @param repository + * @param user + * @param groups + */ + private void collectRepositoryPermissions(Builder builder, + Repository repository, User user, GroupNames groups) + { + List repositoryPermissions = + repository.getPermissions(); + + if (Util.isNotEmpty(repositoryPermissions)) + { + + for (sonia.scm.repository.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()); + } + + builder.add(rp); + } + } + } + else if (logger.isTraceEnabled()) + { + logger.trace("repository {} has not permission entries", + repository.getName()); + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param user + * @param groups + * @param perm + * + * @return + */ + private boolean isUserPermission(User user, GroupNames groups, + PermissionObject perm) + { + //J- + return (perm.isGroupPermission() && groups.contains(perm.getName())) + || ((!perm.isGroupPermission()) && user.getName().equals(perm.getName())); + //J+ + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private RepositoryDAO repositoryDAO; + + /** Field description */ + private PermissionResolver resolver; + + /** Field description */ + private SecuritySystem securitySystem; +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java index 94e49d0287..382d56e99b 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java +++ b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java @@ -36,7 +36,6 @@ package sonia.scm.security; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Joiner; -import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; @@ -70,10 +69,7 @@ import sonia.scm.event.Subscriber; 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.RepositoryEvent; import sonia.scm.repository.RepositoryManager; import sonia.scm.user.User; @@ -130,6 +126,7 @@ public class ScmRealm extends AuthorizingRealm * * @param configuration * @param securitySystem + * @param collector * @param cacheManager * @param userManager * @param groupManager @@ -142,18 +139,16 @@ public class ScmRealm extends AuthorizingRealm */ @Inject public ScmRealm(ScmConfiguration configuration, - SecuritySystem securitySystem, CacheManager cacheManager, - UserManager userManager, GroupManager groupManager, - RepositoryDAO repositoryDAO, UserDAO userDAO, + PermissionCollector collector, CacheManager cacheManager, + UserManager userManager, GroupManager groupManager, UserDAO userDAO, AuthenticationManager authenticator, RepositoryManager manager, Provider requestProvider, Provider responseProvider) { this.configuration = configuration; - this.securitySystem = securitySystem; + this.collector = collector; this.userManager = userManager; this.groupManager = groupManager; - this.repositoryDAO = repositoryDAO; this.userDAO = userDAO; this.authenticator = authenticator; this.requestProvider = requestProvider; @@ -499,121 +494,6 @@ public class ScmRealm extends AuthorizingRealm } } - /** - * Method description - * - * - * @param user - * @param groups - * - * @return - */ - private List collectGlobalPermissions(final User user, - final GroupNames groups) - { - if (logger.isTraceEnabled()) - { - logger.trace("collect global permissions for user {}", user.getName()); - } - - List permissions = Lists.newArrayList(); - - List globalPermissions = - securitySystem.getPermissions(new Predicate() - { - - @Override - public boolean apply(AssignedPermission input) - { - return isUserPermission(user, groups, input); - } - }); - - for (StoredAssignedPermission gp : globalPermissions) - { - if (logger.isTraceEnabled()) - { - logger.trace("add permission {} for user {}", gp.getPermission(), - user.getName()); - } - - permissions.add(gp.getPermission()); - - } - - return permissions; - } - - /** - * Method description - * - * - * @param user - * @param groups - * - * @return - */ - private List collectRepositoryPermissions( - User user, GroupNames groups) - { - List 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 permissions, Repository repository, - User user, GroupNames groups) - { - List 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 * @@ -674,19 +554,13 @@ public class ScmRealm extends AuthorizingRealm } else { - permissions = collectRepositoryPermissions(user, groups); - globalPermissions = collectGlobalPermissions(user, groups); + permissions = collector.collect(user, groups); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); info.addObjectPermissions(permissions); - if (globalPermissions != null) - { - info.addStringPermissions(globalPermissions); - } - return info; } @@ -830,24 +704,21 @@ public class ScmRealm extends AuthorizingRealm /** Field description */ private Cache cache; + /** Field description */ + private PermissionCollector collector; + /** Field description */ private ScmConfiguration configuration; /** Field description */ private GroupManager groupManager; - /** Field description */ - private RepositoryDAO repositoryDAO; - /** Field description */ private Provider requestProvider; /** Field description */ private Provider responseProvider; - /** Field description */ - private SecuritySystem securitySystem; - /** Field description */ private UserDAO userDAO; diff --git a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java b/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java index 443f297d32..570e220e95 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java @@ -470,18 +470,20 @@ public class ScmRealmTest SecuritySystem securitySystem = mock(SecuritySystem.class); when( - securitySystem.getPermissions(Mockito.any()) + securitySystem.getPermissions(Mockito.any(Predicate.class)) ).thenReturn( Collections.EMPTY_LIST ); + + PermissionCollector collector = new PermissionCollector(repositoryDAO, + securitySystem, new RepositoryPermissionResolver()); return new ScmRealm( new ScmConfiguration(), - securitySystem, + collector, new MapCacheManager(), userManager, groupManager, - repositoryDAO, userDAO, authManager, null,