From 681cd52811b03707ceb8e497838e01945376da54 Mon Sep 17 00:00:00 2001 From: Konstantin Schaper Date: Thu, 29 Oct 2020 21:40:57 +0100 Subject: [PATCH 1/6] remove ssh clone link for anonymous users --- .../api/v2/resources/RepositoryToRepositoryDtoMapper.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 31901e4259..9147c65d1c 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 @@ -39,12 +39,14 @@ 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 sonia.scm.security.Authentications; import sonia.scm.web.EdisonHalAppender; import sonia.scm.web.api.RepositoryToHalMapper; import javax.inject.Inject; import java.util.List; import java.util.Set; +import java.util.stream.Stream; import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; @@ -91,7 +93,11 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper protocolLinks = repositoryService.getSupportedProtocols() + Stream supportedProtocols = repositoryService.getSupportedProtocols(); + if (Authentications.isAuthenticatedSubjectAnonymous()) { + supportedProtocols = supportedProtocols.filter(p -> !p.getType().equals("ssh")); + } + List protocolLinks = supportedProtocols .map(this::createProtocolLink) .collect(toList()); linksBuilder.array(protocolLinks); From 7a712f8d9e7a43cc9ff3d3ab91032457ec77eb57 Mon Sep 17 00:00:00 2001 From: Konstantin Schaper Date: Fri, 30 Oct 2020 10:58:36 +0100 Subject: [PATCH 2/6] add flag for anonymous mode availability to scm protocols and filter supported protocols accordingly --- .../java/sonia/scm/repository/api/RepositoryService.java | 4 +++- .../main/java/sonia/scm/repository/api/ScmProtocol.java | 7 +++++++ .../api/v2/resources/RepositoryToRepositoryDtoMapper.java | 6 +----- 3 files changed, 11 insertions(+), 6 deletions(-) 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 aa2a41782d..a6b4265239 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 @@ -34,6 +34,7 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.spi.RepositoryServiceProvider; import sonia.scm.repository.work.WorkdirProvider; +import sonia.scm.security.Authentications; import sonia.scm.user.EMail; import javax.annotation.Nullable; @@ -453,7 +454,8 @@ public final class RepositoryService implements Closeable { public Stream getSupportedProtocols() { return protocolProviders.stream() .filter(protocolProvider -> protocolProvider.getType().equals(getRepository().getType())) - .map(this::createProviderInstanceForRepository); + .map(this::createProviderInstanceForRepository) + .filter(protocol -> !Authentications.isAuthenticatedSubjectAnonymous() || protocol.isAnonymousEnabled()); } @SuppressWarnings({"rawtypes", "java:S3740"}) 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 135191d5eb..ffdab2fc11 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 @@ -40,4 +40,11 @@ public interface ScmProtocol { * The URL to access the repository providing this protocol. */ String getUrl(); + + /** + * Whether the protocol can be used as an anonymous user. + */ + default boolean isAnonymousEnabled() { + return true; + } } 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 9147c65d1c..f5930da3c9 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 @@ -93,11 +93,7 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper supportedProtocols = repositoryService.getSupportedProtocols(); - if (Authentications.isAuthenticatedSubjectAnonymous()) { - supportedProtocols = supportedProtocols.filter(p -> !p.getType().equals("ssh")); - } - List protocolLinks = supportedProtocols + List protocolLinks = repositoryService.getSupportedProtocols() .map(this::createProtocolLink) .collect(toList()); linksBuilder.array(protocolLinks); From 3e6e32a7499b7a2dd70056a0b695bf1e20dd2238 Mon Sep 17 00:00:00 2001 From: Konstantin Schaper Date: Fri, 30 Oct 2020 12:14:52 +0100 Subject: [PATCH 3/6] add unit test --- .../repository/api/RepositoryServiceTest.java | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) 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 8394e3c582..f21836a670 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 @@ -24,7 +24,15 @@ package sonia.scm.repository.api; -import org.junit.Test; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.Repository; import sonia.scm.repository.spi.HttpScmProtocol; @@ -42,7 +50,9 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.util.IterableUtil.sizeOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +@ExtendWith(MockitoExtension.class) public class RepositoryServiceTest { private final RepositoryServiceProvider provider = mock(RepositoryServiceProvider.class); @@ -50,16 +60,40 @@ public class RepositoryServiceTest { private final EMail eMail = new EMail(new ScmConfiguration()); + @Mock + private Subject subject; + + @BeforeEach + void bindSubject() { + ThreadContext.bind(subject); + } + + @AfterEach + void unbindSubject() { + ThreadContext.unbindSubject(); + } + @Test public void shouldReturnMatchingProtocolsFromProvider() { + when(subject.getPrincipal()).thenReturn("Hitchhiker"); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); Stream supportedProtocols = repositoryService.getSupportedProtocols(); assertThat(sizeOf(supportedProtocols.collect(Collectors.toList()))).isEqualTo(1); } + @Test + public void shouldFilterOutNonAnonymousEnabledProtocolsForAnonymousUser() { + when(subject.getPrincipal()).thenReturn(SCMContext.USER_ANONYMOUS); + RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Stream.of(new DummyScmProtocolProvider(), new DummyScmProtocolProvider(false)).collect(Collectors.toSet()), null, eMail); + Stream supportedProtocols = repositoryService.getSupportedProtocols(); + + assertThat(sizeOf(supportedProtocols.collect(Collectors.toList()))).isEqualTo(1); + } + @Test public void shouldFindKnownProtocol() { + when(subject.getPrincipal()).thenReturn("Hitchhiker"); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class); @@ -69,22 +103,43 @@ public class RepositoryServiceTest { @Test public void shouldFailForUnknownProtocol() { + when(subject.getPrincipal()).thenReturn("Hitchhiker"); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); assertThrows(IllegalArgumentException.class, () -> repositoryService.getProtocol(UnknownScmProtocol.class)); } private static class DummyHttpProtocol extends HttpScmProtocol { - public DummyHttpProtocol(Repository repository) { + + private final boolean anonymousEnabled; + + public DummyHttpProtocol(Repository repository, boolean anonymousEnabled) { super(repository, ""); + this.anonymousEnabled = anonymousEnabled; } @Override public void serve(HttpServletRequest request, HttpServletResponse response, Repository repository, ServletConfig config) { } + + @Override + public boolean isAnonymousEnabled() { + return anonymousEnabled; + } } private static class DummyScmProtocolProvider implements ScmProtocolProvider { + + private final boolean anonymousEnabled; + + public DummyScmProtocolProvider() { + this(true); + } + + public DummyScmProtocolProvider(boolean anonymousEnabled) { + this.anonymousEnabled = anonymousEnabled; + } + @Override public String getType() { return "git"; @@ -92,9 +147,10 @@ public class RepositoryServiceTest { @Override public ScmProtocol get(Repository repository) { - return new DummyHttpProtocol(repository); + return new DummyHttpProtocol(repository, anonymousEnabled); } } - private interface UnknownScmProtocol extends ScmProtocol {} + private interface UnknownScmProtocol extends ScmProtocol { + } } From 8af822a1e966fcd49a5e5f21708d15f71844fb7f Mon Sep 17 00:00:00 2001 From: Konstantin Schaper Date: Fri, 30 Oct 2020 15:25:14 +0100 Subject: [PATCH 4/6] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cbd392b0f..8413f3cb38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Fixed - Do not expose subversion commit with id 0 ([#1395](https://github.com/scm-manager/scm-manager/pull/1395)) +- Disable cloning repositories via ssh for anonymous users ([#1403](https://github.com/scm-manager/scm-manager/pull/1403)) ## [2.8.0] - 2020-10-27 ### Added From afbac9aa7b0b618e4caffbb829c33e97c795e7ea Mon Sep 17 00:00:00 2001 From: Konstantin Schaper Date: Fri, 30 Oct 2020 15:26:19 +0100 Subject: [PATCH 5/6] remove unused imports --- .../scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java | 2 -- 1 file changed, 2 deletions(-) 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 f5930da3c9..31901e4259 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 @@ -39,14 +39,12 @@ 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 sonia.scm.security.Authentications; import sonia.scm.web.EdisonHalAppender; import sonia.scm.web.api.RepositoryToHalMapper; import javax.inject.Inject; import java.util.List; import java.util.Set; -import java.util.stream.Stream; import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; From aa9ac3b020e15e632fc223ced4262dc1304c6f10 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 3 Nov 2020 08:32:14 +0100 Subject: [PATCH 6/6] Remove unnecessary public modifier of jUnit5 test --- .../scm/repository/api/RepositoryServiceTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 f21836a670..c702cc64de 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 @@ -53,7 +53,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class RepositoryServiceTest { +class RepositoryServiceTest { private final RepositoryServiceProvider provider = mock(RepositoryServiceProvider.class); private final Repository repository = new Repository("", "git", "space", "repo"); @@ -74,7 +74,7 @@ public class RepositoryServiceTest { } @Test - public void shouldReturnMatchingProtocolsFromProvider() { + void shouldReturnMatchingProtocolsFromProvider() { when(subject.getPrincipal()).thenReturn("Hitchhiker"); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); Stream supportedProtocols = repositoryService.getSupportedProtocols(); @@ -83,7 +83,7 @@ public class RepositoryServiceTest { } @Test - public void shouldFilterOutNonAnonymousEnabledProtocolsForAnonymousUser() { + void shouldFilterOutNonAnonymousEnabledProtocolsForAnonymousUser() { when(subject.getPrincipal()).thenReturn(SCMContext.USER_ANONYMOUS); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Stream.of(new DummyScmProtocolProvider(), new DummyScmProtocolProvider(false)).collect(Collectors.toSet()), null, eMail); Stream supportedProtocols = repositoryService.getSupportedProtocols(); @@ -92,7 +92,7 @@ public class RepositoryServiceTest { } @Test - public void shouldFindKnownProtocol() { + void shouldFindKnownProtocol() { when(subject.getPrincipal()).thenReturn("Hitchhiker"); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail); @@ -102,7 +102,7 @@ public class RepositoryServiceTest { } @Test - public void shouldFailForUnknownProtocol() { + void shouldFailForUnknownProtocol() { when(subject.getPrincipal()).thenReturn("Hitchhiker"); RepositoryService repositoryService = new RepositoryService(null, provider, repository, null, Collections.singleton(new DummyScmProtocolProvider()), null, eMail);