From 7d994c62a6612aba62b131ef78d93077286a2fe7 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 12 Mar 2019 16:40:08 +0100 Subject: [PATCH] validate namespace strategies on configuration update --- .../scm/api/v2/resources/ConfigResource.java | 10 +++++- .../NamespaceStrategyValidator.java | 26 +++++++++++++++ .../api/v2/resources/ConfigResourceTest.java | 24 +++++++++++++- .../NamespaceStrategyValidatorTest.java | 33 +++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/repository/NamespaceStrategyValidator.java create mode 100644 scm-webapp/src/test/java/sonia/scm/repository/NamespaceStrategyValidatorTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java index c646dceab4..e5e754afea 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java @@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.config.ScmConfiguration; +import sonia.scm.repository.NamespaceStrategyValidator; import sonia.scm.util.ScmConfigurationUtil; import sonia.scm.web.VndMediaType; @@ -27,12 +28,16 @@ public class ConfigResource { private final ConfigDtoToScmConfigurationMapper dtoToConfigMapper; private final ScmConfigurationToConfigDtoMapper configToDtoMapper; private final ScmConfiguration configuration; + private final NamespaceStrategyValidator namespaceStrategyValidator; @Inject - public ConfigResource(ConfigDtoToScmConfigurationMapper dtoToConfigMapper, ScmConfigurationToConfigDtoMapper configToDtoMapper, ScmConfiguration configuration) { + public ConfigResource(ConfigDtoToScmConfigurationMapper dtoToConfigMapper, + ScmConfigurationToConfigDtoMapper configToDtoMapper, + ScmConfiguration configuration, NamespaceStrategyValidator namespaceStrategyValidator) { this.dtoToConfigMapper = dtoToConfigMapper; this.configToDtoMapper = configToDtoMapper; this.configuration = configuration; + this.namespaceStrategyValidator = namespaceStrategyValidator; } /** @@ -78,6 +83,9 @@ public class ConfigResource { // But to where to check? load() or store()? Leave it for now, SCMv1 legacy that can be cleaned up later. ConfigurationPermissions.write(configuration).check(); + // ensure the namespace strategy is valid + namespaceStrategyValidator.check(configDto.getNamespaceStrategy()); + ScmConfiguration config = dtoToConfigMapper.map(configDto); synchronized (ScmConfiguration.class) { configuration.load(config); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/NamespaceStrategyValidator.java b/scm-webapp/src/main/java/sonia/scm/repository/NamespaceStrategyValidator.java new file mode 100644 index 0000000000..89ee0fa1bc --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/NamespaceStrategyValidator.java @@ -0,0 +1,26 @@ +package sonia.scm.repository; + +import javax.inject.Inject; +import java.util.Set; + +import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; + +public class NamespaceStrategyValidator { + + private final Set strategies; + + @Inject + public NamespaceStrategyValidator(Set strategies) { + this.strategies = strategies; + } + + public void check(String name) { + doThrow() + .violation("unknown NamespaceStrategy " + name, "namespaceStrategy") + .when(!isValid(name)); + } + + private boolean isValid(String name) { + return strategies.stream().anyMatch(ns -> ns.getClass().getSimpleName().equals(name)); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java index 66689c6ac6..584ffaf4fd 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java @@ -13,7 +13,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.InjectMocks; +import org.mockito.Mock; import sonia.scm.config.ScmConfiguration; +import sonia.scm.repository.NamespaceStrategyValidator; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; @@ -22,10 +24,12 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; @SubjectAware( @@ -46,6 +50,9 @@ public class ConfigResourceTest { @SuppressWarnings("unused") // Is injected private ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); + @Mock + private NamespaceStrategyValidator namespaceStrategyValidator; + @InjectMocks private ConfigDtoToScmConfigurationMapperImpl dtoToConfigMapper; @InjectMocks @@ -62,7 +69,7 @@ public class ConfigResourceTest { public void prepareEnvironment() { initMocks(this); - ConfigResource configResource = new ConfigResource(dtoToConfigMapper, configToDtoMapper, createConfiguration()); + ConfigResource configResource = new ConfigResource(dtoToConfigMapper, configToDtoMapper, createConfiguration(), namespaceStrategyValidator); dispatcher.getRegistry().addSingletonResource(configResource); } @@ -140,6 +147,21 @@ public class ConfigResourceTest { assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus()); } + + @Test + @SubjectAware(username = "readWrite") + public void shouldValidateNamespaceStrategy() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.put("/" + ConfigResource.CONFIG_PATH_V2) + .contentType(VndMediaType.CONFIG) + .content("{ \"namespaceStrategy\": \"AwesomeStrategy\" }".getBytes(StandardCharsets.UTF_8)); + + MockHttpResponse response = new MockHttpResponse(); + dispatcher.invoke(request, response); + + assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus()); + verify(namespaceStrategyValidator).check("AwesomeStrategy"); + } + private MockHttpRequest post(String resourceName) throws IOException, URISyntaxException { URL url = Resources.getResource(resourceName); byte[] configJson = Resources.toByteArray(url); diff --git a/scm-webapp/src/test/java/sonia/scm/repository/NamespaceStrategyValidatorTest.java b/scm-webapp/src/test/java/sonia/scm/repository/NamespaceStrategyValidatorTest.java new file mode 100644 index 0000000000..ec08710755 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/repository/NamespaceStrategyValidatorTest.java @@ -0,0 +1,33 @@ +package sonia.scm.repository; + +import com.google.common.collect.Sets; +import org.junit.jupiter.api.Test; +import sonia.scm.ScmConstraintViolationException; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +class NamespaceStrategyValidatorTest { + + @Test + void shouldThrowConstraintValidationException() { + NamespaceStrategyValidator validator = new NamespaceStrategyValidator(Collections.emptySet()); + assertThrows(ScmConstraintViolationException.class, () -> validator.check("AwesomeStrategy")); + } + + @Test + void shouldDoNotThrowAnException() { + NamespaceStrategyValidator validator = new NamespaceStrategyValidator(Sets.newHashSet(new AwesomeStrategy())); + validator.check("AwesomeStrategy"); + } + + public static class AwesomeStrategy implements NamespaceStrategy { + + @Override + public String createNamespace(Repository repository) { + return null; + } + } + +}