From 3f772b3688e789a5c96a0f04889f85fcc5be0468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 4 Sep 2018 12:40:41 +0200 Subject: [PATCH] Use links from protocols instead of resource links Add links only for requests with according permissions --- .../scm/repository/api/RepositoryService.java | 4 +- .../sonia/scm/repository/api/ScmProtocol.java | 4 +- .../repository/api/RepositoryServiceTest.java | 9 +-- .../RepositoryToRepositoryDtoMapper.java | 21 ++++++- .../scm/api/v2/resources/ResourceLinks.java | 5 -- .../scm/api/v2/resources/MockScmProtocol.java | 25 ++++++++ .../resources/RepositoryRootResourceTest.java | 24 +++++++- .../RepositoryToRepositoryDtoMapperTest.java | 58 +++++++++++++++++-- .../resources/sonia/scm/repository/shiro.ini | 2 + 9 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/MockScmProtocol.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java index 926caad530..61c3ce5d87 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java @@ -42,7 +42,7 @@ import sonia.scm.repository.spi.RepositoryServiceProvider; import java.io.Closeable; import java.io.IOException; -import java.util.Set; +import java.util.Collection; /** * From the {@link RepositoryService} it is possible to access all commands for @@ -359,7 +359,7 @@ public final class RepositoryService implements Closeable { } - public Set getSupportedProtocols() { + public Collection getSupportedProtocols() { return provider.getSupportedProtocols(); } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java b/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java index c987510491..0bd1ed77ef 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/ScmProtocol.java @@ -1,5 +1,7 @@ package sonia.scm.repository.api; +import javax.ws.rs.core.UriInfo; + /** * An ScmProtocol represents a concrete protocol provided by the SCM-Manager instance * to interact with a repository depending on its type. There may be multiple protocols @@ -15,5 +17,5 @@ public interface ScmProtocol { /** * The URL to access the repository providing this protocol. */ - String getUrl(); + String getUrl(UriInfo uriInfo); } diff --git a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java index f70e5a66a4..c5e38908ec 100644 --- a/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/api/RepositoryServiceTest.java @@ -7,8 +7,9 @@ import sonia.scm.repository.spi.RepositoryServiceProvider; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.UriInfo; +import java.util.Collection; import java.util.Collections; -import java.util.Set; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.util.IterableUtil.sizeOf; @@ -26,7 +27,7 @@ public class RepositoryServiceTest { when(provider.getSupportedProtocols()).thenReturn(Collections.singleton(new DummyHttpProtocol())); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null); - Set supportedProtocols = repositoryService.getSupportedProtocols(); + Collection supportedProtocols = repositoryService.getSupportedProtocols(); assertThat(sizeOf(supportedProtocols)).isEqualTo(1); } @@ -38,7 +39,7 @@ public class RepositoryServiceTest { RepositoryService repositoryService = new RepositoryService(null, provider, repository, null); HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class); - assertThat(protocol.getUrl()).isEqualTo("dummy"); + assertThat(protocol.getUrl(null)).isEqualTo("dummy"); } @Test @@ -54,7 +55,7 @@ public class RepositoryServiceTest { private static class DummyHttpProtocol implements HttpScmProtocol { @Override - public String getUrl() { + public String getUrl(UriInfo uriInfo) { return "dummy"; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java index f76669fbb9..0bc9b7af48 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.google.inject.Inject; +import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; @@ -11,9 +12,14 @@ import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.api.Command; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; +import sonia.scm.repository.api.ScmProtocol; + +import java.util.Collection; +import java.util.List; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; +import static java.util.stream.Collectors.toList; // Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. @SuppressWarnings("squid:S3306") @@ -24,13 +30,14 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper supportedProtocols = repositoryService.getSupportedProtocols(); + List protocolLinks = supportedProtocols + .stream() + .map(this::createProtocolLink) + .collect(toList()); + linksBuilder.array(protocolLinks); + } if (repositoryService.isSupported(Command.TAGS)) { linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName()))); } @@ -50,4 +65,8 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper createSingletonPageResult(Repository repository) { return new PageResult<>(singletonList(repository), 0); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java index 0c77d40023..9fe2700e4a 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java @@ -7,7 +7,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import sonia.scm.repository.HealthCheckFailure; @@ -15,13 +14,18 @@ import sonia.scm.repository.Permission; import sonia.scm.repository.PermissionType; import sonia.scm.repository.Repository; import sonia.scm.repository.api.Command; +import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; +import sonia.scm.repository.api.ScmProtocol; import java.net.URI; +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -39,8 +43,12 @@ public class RepositoryToRepositoryDtoMapperTest { private final URI baseUri = URI.create("http://example.com/base/"); @SuppressWarnings("unused") // Is injected private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); - @Mock(answer = Answers.RETURNS_DEEP_STUBS) + @Mock//(answer = Answers.RETURNS_DEEP_STUBS) private RepositoryServiceFactory serviceFactory; + @Mock + private RepositoryService repositoryService; + @Mock + private UriInfoStore uriInfoStore; @InjectMocks private RepositoryToRepositoryDtoMapperImpl mapper; @@ -48,7 +56,9 @@ public class RepositoryToRepositoryDtoMapperTest { @Before public void init() { initMocks(this); - when(serviceFactory.create(any(Repository.class)).isSupported(any(Command.class))).thenReturn(true); + when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService); + when(repositoryService.isSupported(any(Command.class))).thenReturn(true); + when(repositoryService.getSupportedProtocols()).thenReturn(emptySet()); } @After @@ -129,14 +139,14 @@ public class RepositoryToRepositoryDtoMapperTest { @Test public void shouldNotCreateTagsLink_ifNotSupported() { - when(serviceFactory.create(any(Repository.class)).isSupported(Command.TAGS)).thenReturn(false); + when(repositoryService.isSupported(Command.TAGS)).thenReturn(false); RepositoryDto dto = mapper.map(createTestRepository()); assertFalse(dto.getLinks().getLinkBy("tags").isPresent()); } @Test public void shouldNotCreateBranchesLink_ifNotSupported() { - when(serviceFactory.create(any(Repository.class)).isSupported(Command.BRANCHES)).thenReturn(false); + when(repositoryService.isSupported(Command.BRANCHES)).thenReturn(false); RepositoryDto dto = mapper.map(createTestRepository()); assertFalse(dto.getLinks().getLinkBy("branches").isPresent()); } @@ -165,6 +175,43 @@ public class RepositoryToRepositoryDtoMapperTest { dto.getLinks().getLinkBy("permissions").get().getHref()); } + @Test + public void shouldCreateCorrectProtocolLinks() { + when(repositoryService.getSupportedProtocols()).thenReturn( + asList(mockProtocol("http", "http://scm"), mockProtocol("other", "some://protocol")) + ); + + RepositoryDto dto = mapper.map(createTestRepository()); + assertTrue("should contain http link", dto.getLinks().stream().anyMatch(l -> l.getName().equals("http") && l.getHref().equals("http://scm"))); + assertTrue("should contain other link", dto.getLinks().stream().anyMatch(l -> l.getName().equals("other") && l.getHref().equals("some://protocol"))); + } + + @Test + @SubjectAware(username = "community") + public void shouldCreateProtocolLinksForPullPermission() { + when(repositoryService.getSupportedProtocols()).thenReturn( + asList(mockProtocol("http", "http://scm"), mockProtocol("other", "some://protocol")) + ); + + RepositoryDto dto = mapper.map(createTestRepository()); + assertEquals(2, dto.getLinks().getLinksBy("protocol").size()); + } + + @Test + @SubjectAware(username = "unpriv") + public void shouldNotCreateProtocolLinksWithoutPullPermission() { + when(repositoryService.getSupportedProtocols()).thenReturn( + asList(mockProtocol("http", "http://scm"), mockProtocol("other", "some://protocol")) + ); + + RepositoryDto dto = mapper.map(createTestRepository()); + assertTrue(dto.getLinks().getLinksBy("protocol").isEmpty()); + } + + private ScmProtocol mockProtocol(String type, String protocol) { + return new MockScmProtocol(type, protocol); + } + private Repository createTestRepository() { Repository repository = new Repository(); repository.setNamespace("testspace"); @@ -179,4 +226,5 @@ public class RepositoryToRepositoryDtoMapperTest { return repository; } + } diff --git a/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini b/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini index 5073bf398d..9a39a2d46c 100644 --- a/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini +++ b/scm-webapp/src/test/resources/sonia/scm/repository/shiro.ini @@ -3,9 +3,11 @@ trillian = secret, admin dent = secret, creator, heartOfGold, puzzle42 unpriv = secret crato = secret, creator +community = secret, oss [roles] admin = * creator = repository:create heartOfGold = "repository:read,modify,delete:hof" puzzle42 = "repository:read,write:p42" +oss = "repository:pull"