diff --git a/pom.xml b/pom.xml index 82e0c050d6..a249734dca 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,6 @@ - jitpack https://jitpack.io @@ -167,6 +166,20 @@ test + + + com.github.schnatterer.shiro-static-permissions + ssp-lib + ${ssp.version} + + + + com.github.schnatterer.shiro-static-permissions + ssp-processor + ${ssp.version} + true + + @@ -551,7 +564,7 @@ 9.2.10.v20150310 - 1.0.0-SNAPSHOT + -SNAPSHOT 1.4.0 diff --git a/scm-core/pom.xml b/scm-core/pom.xml index d80ff3bcaa..a500dba32e 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -128,17 +128,16 @@ 2.0.0-SNAPSHOT provided - + + - com.github.sdorra + com.github.schnatterer.shiro-static-permissions ssp-lib - ${ssp.version} - com.github.sdorra + com.github.schnatterer.shiro-static-permissions ssp-processor - ${ssp.version} true diff --git a/scm-core/src/main/java/sonia/scm/config/Configuration.java b/scm-core/src/main/java/sonia/scm/config/Configuration.java new file mode 100644 index 0000000000..e9bf3528d5 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/config/Configuration.java @@ -0,0 +1,28 @@ +package sonia.scm.config; + +import com.github.sdorra.ssp.PermissionObject; +import com.github.sdorra.ssp.StaticPermissions; + +/** + * Base for all kinds of configurations. + * + * Allows for permission like + * + * + * + *
+ * + * And for permission checks like {@code ConfigurationPermissions.read(configurationObject).check();} + */ +@StaticPermissions( + value = "configuration", + permissions = {"read", "write"}, + globalPermissions = {} +) +public interface Configuration extends PermissionObject { +} diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index c7c3517099..6e1db68f91 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -44,6 +44,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.io.File; import java.util.Set; @@ -57,10 +58,11 @@ import java.util.concurrent.TimeUnit; * * @author Sebastian Sdorra */ + @Singleton @XmlRootElement(name = "scm-config") @XmlAccessorType(XmlAccessType.FIELD) -public class ScmConfiguration { +public class ScmConfiguration implements Configuration { /** * Default JavaScript date format @@ -501,4 +503,12 @@ public class ScmConfiguration { public void setDefaultNamespaceStrategy(String defaultNamespaceStrategy) { this.defaultNamespaceStrategy = defaultNamespaceStrategy; } + + @Override + // Only for permission checks, don't serialize to XML + @XmlTransient + public String getId() { + // Don't change this without migrating SCM permission configuration! + return "global"; + } } diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java index a327b50dae..33b6cb8030 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java @@ -56,7 +56,7 @@ import sonia.scm.store.ConfigurationStoreFactory; * * @param */ -public abstract class AbstractRepositoryHandler +public abstract class AbstractRepositoryHandler implements RepositoryHandler { diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index aa28403116..536b7bd9e9 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -55,7 +55,7 @@ import java.net.URL; * @param * @author Sebastian Sdorra */ -public abstract class AbstractSimpleRepositoryHandler +public abstract class AbstractSimpleRepositoryHandler extends AbstractRepositoryHandler implements RepositoryDirectoryHandler { public static final String DEFAULT_VERSION_INFORMATION = "unknown"; diff --git a/scm-core/src/main/java/sonia/scm/repository/SimpleRepositoryConfig.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryConfig.java similarity index 82% rename from scm-core/src/main/java/sonia/scm/repository/SimpleRepositoryConfig.java rename to scm-core/src/main/java/sonia/scm/repository/RepositoryConfig.java index 3654b9c8b4..3db13e5618 100644 --- a/scm-core/src/main/java/sonia/scm/repository/SimpleRepositoryConfig.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryConfig.java @@ -33,15 +33,12 @@ package sonia.scm.repository; -//~--- non-JDK imports -------------------------------------------------------- - import sonia.scm.Validateable; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.File; +import sonia.scm.config.Configuration; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; +import java.io.File; /** * Basic {@link Repository} configuration class. @@ -49,7 +46,7 @@ import javax.xml.bind.annotation.XmlRootElement; * @author Sebastian Sdorra */ @XmlRootElement -public class SimpleRepositoryConfig implements Validateable +public abstract class RepositoryConfig implements Validateable, Configuration { /** @@ -119,4 +116,19 @@ public class SimpleRepositoryConfig implements Validateable /** directory for repositories */ private File repositoryDirectory; + + /** + * Specifies the identifier of the concrete {@link RepositoryConfig} when checking permissions of an object. + * The permission Strings will have the following format: "configuration:*:ID", where the ID part is defined by this + * method. + * + * For example: "configuration:read:git". + * + * No need to serialize this. + * + * @return identifier of this RepositoryConfig in permission strings + */ + @Override + @XmlTransient // Only for permission checks, don't serialize to XML + public abstract String getId(); } diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandlerConfigChangedEvent.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandlerConfigChangedEvent.java index 844c4a78d6..645274e494 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandlerConfigChangedEvent.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandlerConfigChangedEvent.java @@ -37,7 +37,7 @@ import sonia.scm.event.Event; * @since 2.0.0 */ @Event -public class RepositoryHandlerConfigChangedEvent +public class RepositoryHandlerConfigChangedEvent { private final C configuration; diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java index 0be291d4e1..a90eb4cc34 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java @@ -74,7 +74,7 @@ public final class RepositoryUtil { return getRepositoryId(handler.getConfig(), directory); } - public static String getRepositoryId(SimpleRepositoryConfig config, File directory) throws IOException { + public static String getRepositoryId(RepositoryConfig config, File directory) throws IOException { return getRepositoryId(config.getRepositoryDirectory(), directory); } diff --git a/scm-core/src/test/java/sonia/scm/repository/RepositoryUtilTest.java b/scm-core/src/test/java/sonia/scm/repository/RepositoryUtilTest.java index 25fa3eeeb3..82e85568d0 100644 --- a/scm-core/src/test/java/sonia/scm/repository/RepositoryUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/RepositoryUtilTest.java @@ -21,9 +21,14 @@ public class RepositoryUtilTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Mock - private AbstractRepositoryHandler repositoryHandler; + private AbstractRepositoryHandler repositoryHandler; - private SimpleRepositoryConfig repositoryConfig = new SimpleRepositoryConfig(); + private RepositoryConfig repositoryConfig = new RepositoryConfig() { + @Override + public String getId() { + return "repository"; + } + }; @Before public void setUpMocks() { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java index 80fe8907ac..2ec93ed83d 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java @@ -39,6 +39,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; /** * @@ -46,7 +47,7 @@ import javax.xml.bind.annotation.XmlRootElement; */ @XmlRootElement(name = "config") @XmlAccessorType(XmlAccessType.FIELD) -public class GitConfig extends SimpleRepositoryConfig { +public class GitConfig extends RepositoryConfig { @XmlElement(name = "gc-expression") private String gcExpression; @@ -55,5 +56,11 @@ public class GitConfig extends SimpleRepositoryConfig { { return gcExpression; } - + + @Override + @XmlTransient // Only for permission checks, don't serialize to XML + public String getId() { + // Don't change this without migrating SCM permission configuration! + return "git"; + } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java index b9bd650925..6438f49d4c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgConfig.java @@ -33,20 +33,19 @@ package sonia.scm.repository; -//~--- non-JDK imports -------------------------------------------------------- import sonia.scm.util.Util; -//~--- JDK imports ------------------------------------------------------------ - import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + /** * * @author Sebastian Sdorra */ @XmlRootElement(name = "config") -public class HgConfig extends SimpleRepositoryConfig +public class HgConfig extends RepositoryConfig { /** @@ -223,4 +222,11 @@ public class HgConfig extends SimpleRepositoryConfig /** Field description */ private boolean showRevisionInId = false; + + @Override + @XmlTransient // Only for permission checks, don't serialize to XML + public String getId() { + // Don't change this without migrating SCM permission configuration! + return "hg"; + } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnConfig.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnConfig.java index c4b0af57a1..73b4f39219 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnConfig.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnConfig.java @@ -33,12 +33,11 @@ package sonia.scm.repository; -//~--- JDK imports ------------------------------------------------------------ - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; /** * @@ -46,7 +45,7 @@ import javax.xml.bind.annotation.XmlRootElement; */ @XmlRootElement(name = "config") @XmlAccessorType(XmlAccessType.FIELD) -public class SvnConfig extends SimpleRepositoryConfig +public class SvnConfig extends RepositoryConfig { /** @@ -108,4 +107,11 @@ public class SvnConfig extends SimpleRepositoryConfig /** Field description */ private Compatibility compatibility = Compatibility.NONE; + + @Override + @XmlTransient // Only for permission checks, don't serialize to XML + public String getId() { + // Don't change this without migrating SCM permission configuration! + return "svn"; + } } diff --git a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java index 870127b14e..bf68c27b19 100644 --- a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java +++ b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java @@ -37,6 +37,7 @@ import sonia.scm.Type; import sonia.scm.io.DefaultFileSystem; import sonia.scm.store.ConfigurationStoreFactory; +import javax.xml.bind.annotation.XmlRootElement; import java.io.File; import java.util.HashSet; import java.util.Set; @@ -48,7 +49,7 @@ import java.util.Set; * @author Sebastian Sdorra */ public class DummyRepositoryHandler - extends AbstractSimpleRepositoryHandler { + extends AbstractSimpleRepositoryHandler { public static final String TYPE_DISPLAYNAME = "Dummy"; @@ -79,12 +80,20 @@ public class DummyRepositoryHandler } @Override - protected SimpleRepositoryConfig createInitialConfig() { - return new SimpleRepositoryConfig(); + protected DummyRepositoryConfig createInitialConfig() { + return new DummyRepositoryConfig(); } @Override - protected Class getConfigClass() { - return SimpleRepositoryConfig.class; + protected Class getConfigClass() { + return DummyRepositoryConfig.class; + } + + @XmlRootElement(name = "config") + public static class DummyRepositoryConfig extends RepositoryConfig { + @Override + public String getId() { + return TYPE_NAME; + } } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GlobalConfigResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GlobalConfigResource.java index 1e1698a45a..97cfd7fa26 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GlobalConfigResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GlobalConfigResource.java @@ -3,14 +3,17 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; +import sonia.scm.config.ConfigurationPermissions; import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.Role; import sonia.scm.util.ScmConfigurationUtil; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @@ -44,16 +47,12 @@ public class GlobalConfigResource { @ResponseCode(code = 500, condition = "internal server error") }) public Response get() { - Response response; - // TODO ConfigPermisions? - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) { - response = Response.ok(configToDtoMapper.map(configuration)).build(); - } else { - response = Response.status(Response.Status.FORBIDDEN).build(); - } + // We do this permission check in Resource and not in ScmConfiguration, because it must be available for reading + // from within the code (plugins, etc.), but not for the whole anonymous world outside. + ConfigurationPermissions.read(configuration).check(); - return response; + return Response.ok(configToDtoMapper.map(configuration)).build(); } /** @@ -72,20 +71,17 @@ public class GlobalConfigResource { }) @TypeHint(TypeHint.NO_CONTENT.class) public Response update(GlobalConfigDto configDto, @Context UriInfo uriInfo) { - Response response; - // TODO ConfigPermisions? - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) { - ScmConfiguration config = dtoToConfigMapper.map(configDto); + // This *could* be moved to ScmConfiguration or ScmConfigurationUtil classes. + // But to where to check? load() or store()? Leave it for now, SCMv1 legacy that can be cleaned up later. + ConfigurationPermissions.write(configuration).check(); + + ScmConfiguration config = dtoToConfigMapper.map(configDto); + synchronized (ScmConfiguration.class) { configuration.load(config); - synchronized (ScmConfiguration.class) { - ScmConfigurationUtil.getInstance().store(configuration); - } - response = Response.created(uriInfo.getRequestUri()).build(); - } else { - response = Response.status(Response.Status.FORBIDDEN).build(); + ScmConfigurationUtil.getInstance().store(configuration); } - return response; + return Response.created(uriInfo.getRequestUri()).build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapper.java index 129ebc7fbe..f0090d5fb9 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapper.java @@ -1,12 +1,11 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; -import org.apache.shiro.SecurityUtils; import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; +import sonia.scm.config.ConfigurationPermissions; import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.Role; import javax.inject.Inject; @@ -26,8 +25,7 @@ public abstract class ScmConfigurationToGlobalConfigDtoMapper { @AfterMapping void appendLinks(ScmConfiguration config, @MappingTarget GlobalConfigDto target) { Links.Builder linksBuilder = linkingTo().self(resourceLinks.globalConfig().self()); - // TODO: ConfigPermissions? - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) { + if (ConfigurationPermissions.write(config).isPermitted()) { linksBuilder.single(link("update", resourceLinks.globalConfig().update())); } target.add(linksBuilder.build()); diff --git a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java index 633f6aa3b5..a5455c6d21 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java @@ -49,6 +49,7 @@ import sonia.scm.security.Role; * * @author Sebastian Sdorra */ +// TODO before releasing v2, delete this filter (we use Permission objects now) @WebElement( value = Filters.PATTERN_CONFIG, morePatterns = { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GlobalConfigResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GlobalConfigResourceTest.java index e8e0f490e7..c033c0843e 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GlobalConfigResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GlobalConfigResourceTest.java @@ -10,6 +10,7 @@ import org.jboss.resteasy.mock.MockHttpResponse; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.mockito.InjectMocks; import sonia.scm.config.ScmConfiguration; import sonia.scm.web.VndMediaType; @@ -23,19 +24,22 @@ import java.util.Arrays; import java.util.HashSet; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.MockitoAnnotations.initMocks; @SubjectAware( - username = "trillian", - password = "secret", - configuration = "classpath:sonia/scm/repository/shiro.ini" + configuration = "classpath:sonia/scm/configuration/shiro.ini", + password = "secret" ) public class GlobalConfigResourceTest { @Rule public ShiroRule shiro = new ShiroRule(); + @Rule + public ExpectedException thrown = ExpectedException.none(); + private Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); private final URI baseUri = URI.create("/"); @@ -58,6 +62,7 @@ public class GlobalConfigResourceTest { } @Test + @SubjectAware(username = "readOnly") public void shouldGetGlobalConfig() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/" + GlobalConfigResource.GLOBAL_CONFIG_PATH_V2); MockHttpResponse response = new MockHttpResponse(); @@ -65,22 +70,22 @@ public class GlobalConfigResourceTest { assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertTrue(response.getContentAsString().contains("\"proxyPassword\":\"heartOfGold\"")); assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/config/global")); - assertTrue("link not found", response.getContentAsString().contains("\"update\":{\"href\":\"/v2/config/global")); + assertFalse("Update link present", response.getContentAsString().contains("\"update\":{\"href\":\"/v2/config/global")); } - @SubjectAware( - username = "dent" - ) @Test - public void shouldGetForbiddenGlobalConfig() throws URISyntaxException { + @SubjectAware(username = "writeOnly") + public void shouldGetGlobalConfigOnlyWhenAuthorized() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/" + GlobalConfigResource.GLOBAL_CONFIG_PATH_V2); MockHttpResponse response = new MockHttpResponse(); + thrown.expectMessage("Subject does not have permission [configuration:read:global]"); + dispatcher.invoke(request, response); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } @Test + @SubjectAware(username = "readWrite") public void shouldUpdateGlobalConfig() throws URISyntaxException, IOException { URL url = Resources.getResource("sonia/scm/api/v2/globalConfig-test-update.json"); byte[] configJson = Resources.toByteArray(url); @@ -102,11 +107,9 @@ public class GlobalConfigResourceTest { } - @SubjectAware( - username = "dent" - ) @Test - public void shouldUpdateForbiddenGlobalConfig() throws URISyntaxException, IOException { + @SubjectAware(username = "readOnly") + public void shouldUpdateGlobalConfigOnlyWhenAuthorized() throws URISyntaxException, IOException { URL url = Resources.getResource("sonia/scm/api/v2/globalConfig-test-update.json"); byte[] configJson = Resources.toByteArray(url); MockHttpRequest request = MockHttpRequest.put("/" + GlobalConfigResource.GLOBAL_CONFIG_PATH_V2) @@ -114,8 +117,10 @@ public class GlobalConfigResourceTest { .content(configJson); MockHttpResponse response = new MockHttpResponse(); + thrown.expectMessage("Subject does not have permission [configuration:write:global]"); + + dispatcher.invoke(request, response); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } public static ScmConfiguration createConfiguration() { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapperTest.java index 1cf08769f0..38ea7b6af7 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ScmConfigurationToGlobalConfigDtoMapperTest.java @@ -51,7 +51,7 @@ public class ScmConfigurationToGlobalConfigDtoMapperTest { public void shouldMapFields() { ScmConfiguration config = createConfiguration(); - when(subject.hasRole(Role.ADMIN)).thenReturn(true); + when(subject.isPermitted("configuration:write:global")).thenReturn(true); GlobalConfigDto dto = mapper.map(config); assertEquals("baseurl", dto.getBaseUrl()); @@ -63,7 +63,7 @@ public class ScmConfigurationToGlobalConfigDtoMapperTest { public void shouldMapFieldsWithoutUpdate() { ScmConfiguration config = createConfiguration(); - when(subject.hasRole(Role.ADMIN)).thenReturn(false); + when(subject.hasRole("configuration:write:global")).thenReturn(false); GlobalConfigDto dto = mapper.map(config); assertEquals("baseurl", dto.getBaseUrl()); diff --git a/scm-webapp/src/test/resources/sonia/scm/configuration/shiro.ini b/scm-webapp/src/test/resources/sonia/scm/configuration/shiro.ini new file mode 100644 index 0000000000..8647142b19 --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/configuration/shiro.ini @@ -0,0 +1,9 @@ +[users] +readOnly = secret, reader +writeOnly = secret, writer +readWrite = secret, readerWriter + +[roles] +reader = configuration:read +writer = configuration:write +readerWriter = configuration:*