diff --git a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java index 0e6d710ad5..bbb02eae02 100644 --- a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java +++ b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java @@ -49,7 +49,7 @@ import javax.xml.bind.annotation.XmlRootElement; * @author Sebastian Sdorra * @since 1.31 */ -@XmlRootElement(name = "permissions") +@XmlRootElement(name = "permission") @XmlAccessorType(XmlAccessType.FIELD) public class PermissionDescriptor implements Serializable { diff --git a/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java index 787ded5d94..47f8612665 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultSecuritySystem.java @@ -42,15 +42,30 @@ import com.google.inject.Singleton; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sonia.scm.store.ConfigurationEntryStore; import sonia.scm.store.ConfigurationEntryStoreFactory; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; + +import java.net.URL; + import java.util.Collections; +import java.util.Enumeration; import java.util.List; import java.util.Map.Entry; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + /** * TODO add events * @@ -64,6 +79,16 @@ public class DefaultSecuritySystem implements SecuritySystem /** Field description */ private static final String NAME = "security"; + /** Field description */ + private static final String PERMISSION_DESCRIPTOR = + "META-INF/scm/permissions.xml"; + + /** + * the logger for DefaultSecuritySystem + */ + private static final Logger logger = + LoggerFactory.getLogger(DefaultSecuritySystem.class); + //~--- constructors --------------------------------------------------------- /** @@ -76,6 +101,7 @@ public class DefaultSecuritySystem implements SecuritySystem public DefaultSecuritySystem(ConfigurationEntryStoreFactory storeFactory) { store = storeFactory.getStore(AssignedPermission.class, NAME); + readAvailablePermissions(); } //~--- methods -------------------------------------------------------------- @@ -165,9 +191,9 @@ public class DefaultSecuritySystem implements SecuritySystem @Override public List getAvailablePermissions() { + assertIsAdmin(); - // TODO - return Collections.EMPTY_LIST; + return availablePermissions; } /** @@ -220,8 +246,151 @@ public class DefaultSecuritySystem implements SecuritySystem SecurityUtils.getSubject().checkRole(Role.ADMIN); } + /** + * Method description + * + * + * @param context + * @param descriptorUrl + * + * @return + */ + private List parsePermissionDescriptor( + JAXBContext context, URL descriptorUrl) + { + List descriptors = Collections.EMPTY_LIST; + + try + { + PermissionDescriptors descriptorWrapper = + (PermissionDescriptors) context.createUnmarshaller().unmarshal( + descriptorUrl); + + descriptors = descriptorWrapper.getPermissions(); + + logger.debug("found {} permissions at {}", descriptors.size(), + descriptorUrl); + logger.trace("permissions from {}: {}", descriptorUrl, descriptors); + } + catch (JAXBException ex) + { + logger.error("could not parse permission descriptor", ex); + } + + return descriptors; + } + + /** + * Method description + * + */ + private void readAvailablePermissions() + { + Builder builder = ImmutableList.builder(); + + try + { + JAXBContext context = + JAXBContext.newInstance(PermissionDescriptors.class); + + Enumeration descirptorEnum = + getClassLoader().getResources(PERMISSION_DESCRIPTOR); + + while (descirptorEnum.hasMoreElements()) + { + URL descriptorUrl = descirptorEnum.nextElement(); + + logger.debug("read permission descriptor from {}", descriptorUrl); + + builder.addAll(parsePermissionDescriptor(context, descriptorUrl)); + } + } + catch (IOException ex) + { + logger.error("could not read permission descriptors", ex); + } + catch (JAXBException ex) + { + logger.error( + "could not create jaxb context to read permission descriptors", ex); + } + + availablePermissions = builder.build(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private ClassLoader getClassLoader() + { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (classLoader == null) + { + logger.warn("could not find context classloader, use default"); + + classLoader = DefaultSecuritySystem.class.getClassLoader(); + } + + return classLoader; + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 13/04/30 + * @author Enter your name here... + */ + @XmlRootElement(name = "permissions") + @XmlAccessorType(XmlAccessType.FIELD) + private static class PermissionDescriptors + { + + /** + * Constructs ... + * + */ + public PermissionDescriptors() {} + + //~--- get methods -------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public List getPermissions() + { + if (permissions == null) + { + permissions = Collections.EMPTY_LIST; + } + + return permissions; + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + @XmlElement(name = "permission") + private List permissions; + } + + //~--- fields --------------------------------------------------------------- /** Field description */ private final ConfigurationEntryStore store; + + /** Field description */ + private List availablePermissions; } diff --git a/scm-webapp/src/main/resources/META-INF/scm/permissions.xml b/scm-webapp/src/main/resources/META-INF/scm/permissions.xml new file mode 100644 index 0000000000..4371b17c5a --- /dev/null +++ b/scm-webapp/src/main/resources/META-INF/scm/permissions.xml @@ -0,0 +1,54 @@ + + + + + + + + All Repository (read) + Read access to all repositories + repository:*:READ + + + + All Repository (write) + Write access to all repositories + repository:*:WRITE + + + + All Repository (owner) + Owner access to all repositories + repository:*:OWNER + + + 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 def745f625..80d994c808 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java @@ -30,10 +30,12 @@ */ + package sonia.scm.security; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.inject.Provider; @@ -49,6 +51,8 @@ import org.apache.shiro.subject.PrincipalCollection; import org.junit.Test; +import org.mockito.Mockito; + import sonia.scm.cache.MapCacheManager; import sonia.scm.config.ScmConfiguration; import sonia.scm.group.Group; @@ -75,6 +79,7 @@ import static org.mockito.Mockito.*; //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @@ -461,9 +466,9 @@ public class ScmRealmTest SecuritySystem securitySystem = mock(SecuritySystem.class); when( - securitySystem.getConfiguration() + securitySystem.getPermissions(Mockito.any()) ).thenReturn( - new SecurityConfiguration() + Collections.EMPTY_LIST ); return new ScmRealm(