From 20338c1bb6733c6ce4040059906f96d7177eecc9 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Thu, 17 Oct 2019 11:53:23 +0200 Subject: [PATCH] Add integration tests for anonymous access --- .../sonia/scm/it/AnonymousAccessITCase.java | 154 +++++++++++++++++- .../sonia/scm/it/utils/RepositoryUtil.java | 8 + .../java/sonia/scm/it/utils/RestUtil.java | 10 +- .../java/sonia/scm/it/utils/ScmTypes.java | 11 +- .../java/sonia/scm/it/utils/TestData.java | 13 +- .../client/api/RepositoryClientFactory.java | 6 + 6 files changed, 192 insertions(+), 10 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java index 0a329b8e1e..1b48da792b 100644 --- a/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java @@ -1,26 +1,170 @@ package sonia.scm.it; import io.restassured.RestAssured; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.TempDirectory; +import sonia.scm.it.utils.RepositoryUtil; import sonia.scm.it.utils.RestUtil; import sonia.scm.it.utils.ScmRequests; +import sonia.scm.it.utils.ScmTypes; +import sonia.scm.it.utils.TestData; +import sonia.scm.repository.client.api.RepositoryClient; +import sonia.scm.repository.client.api.RepositoryClientException; +import javax.json.Json; +import javax.json.JsonArray; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.util.Collections.emptyMap; import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static sonia.scm.it.utils.TestData.JSON_BUILDER; +import static sonia.scm.it.utils.TestData.USER_ANONYMOUS; +import static sonia.scm.it.utils.TestData.WRITE; +import static sonia.scm.it.utils.TestData.getDefaultRepositoryUrl; -public class AnonymousAccessITCase { +@ExtendWith(TempDirectory.class) +class AnonymousAccessITCase { @Test - public void shouldAccessIndexResourceWithoutAuthentication() { + void shouldAccessIndexResourceWithoutAuthentication() { ScmRequests.start() .requestIndexResource() .assertStatusCode(200); } @Test - public void shouldRejectUserResourceWithoutAuthentication() { + void shouldRejectRepositoryResourceWithoutAuthentication() { assertEquals(401, RestAssured.given() .when() - .get(RestUtil.REST_BASE_URL.resolve("users/")) + .get(RestUtil.REST_BASE_URL.resolve("repositories/")) .statusCode()); } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class WithAnonymousAccess { + @BeforeAll + void enableAnonymousAccess() { + setAnonymousAccess(true); + } + + @BeforeEach + void createRepository() { + TestData.createDefault(); + } + + @Test + void shouldGrantAnonymousAccessToRepositoryList() { + assertEquals(200, RestAssured.given() + .when() + .get(RestUtil.REST_BASE_URL.resolve("repositories")) + .statusCode()); + } + + @Nested + class WithoutAnonymousAccessForRepository { + + @ParameterizedTest + @ArgumentsSource(ScmTypes.class) + void shouldGrantAnonymousAccessToRepository(String type) { + assertEquals(401, RestAssured.given() + .when() + .get(getDefaultRepositoryUrl(type)) + .statusCode()); + } + + @ParameterizedTest + @ArgumentsSource(ScmTypes.class) + void shouldNotCloneRepository(String type, @TempDirectory.TempDir Path temporaryFolder) { + assertThrows(RepositoryClientException.class, () -> RepositoryUtil.createAnonymousRepositoryClient(type, Files.createDirectories(temporaryFolder).toFile())); + } + } + + @Nested + class WithAnonymousAccessForRepository { + + @BeforeEach + void grantAnonymousAccessToRepo() { + ScmTypes.availableScmTypes().stream().forEach(type -> TestData.createUserPermission(USER_ANONYMOUS, WRITE, type)); + } + + @ParameterizedTest + @ArgumentsSource(ScmTypes.class) + void shouldGrantAnonymousAccessToRepository(String type) { + assertEquals(200, RestAssured.given() + .when() + .get(getDefaultRepositoryUrl(type)) + .statusCode()); + } + + @ParameterizedTest + @ArgumentsSource(ScmTypes.class) + void shouldCloneRepository(String type, @TempDirectory.TempDir Path temporaryFolder) throws IOException { + RepositoryClient client = RepositoryUtil.createAnonymousRepositoryClient(type, Files.createDirectories(temporaryFolder).toFile()); + assertEquals(1, Objects.requireNonNull(client.getWorkingCopy().list()).length); + } + } + + @AfterAll + void disableAnonymousAccess() { + setAnonymousAccess(false); + } + } + + private static void setAnonymousAccess(boolean anonymousAccessEnabled) { + RestUtil.given("application/vnd.scmm-config+json;v=2") + .body(createConfig(anonymousAccessEnabled)) + + .when() + .put(RestUtil.REST_BASE_URL.toASCIIString() + "config") + + .then() + .statusCode(HttpServletResponse.SC_NO_CONTENT); + } + + private static String createConfig(boolean anonymousAccessEnabled) { + JsonArray emptyArray = Json.createBuilderFactory(emptyMap()).createArrayBuilder().build(); + return JSON_BUILDER + .add("adminGroups", emptyArray) + .add("adminUsers", emptyArray) + .add("anonymousAccessEnabled", anonymousAccessEnabled) + .add("baseUrl", "https://next-scm.cloudogu.com/scm") + .add("dateFormat", "YYYY-MM-DD HH:mm:ss") + .add("disableGroupingGrid", false) + .add("enableProxy", false) + .add("enabledXsrfProtection", true) + .add("forceBaseUrl", false) + .add("loginAttemptLimit", 100) + .add("loginAttemptLimitTimeout", 300) + .add("loginInfoUrl", "https://login-info.scm-manager.org/api/v1/login-info") + .add("namespaceStrategy", "UsernameNamespaceStrategy") + .add("pluginUrl", "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/plugin-snapshot/job/master/lastSuccessfulBuild/artifact/plugins/plugin-center.json") + .add("proxyExcludes", emptyArray) + .addNull("proxyPassword") + .add("proxyPort", 8080) + .add("proxyServer", "proxy.mydomain.com") + .addNull("proxyUser") + .add("realmDescription", "SONIA :: SCM Manager") + .add("skipFailedAuthenticators", false) + .build().toString(); + } } diff --git a/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java b/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java index 427d98f245..752be88dbc 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/RepositoryUtil.java @@ -36,6 +36,14 @@ public class RepositoryUtil { return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, username, password, folder); } + public static RepositoryClient createAnonymousRepositoryClient(String repositoryType, File folder) throws IOException { + String httpProtocolUrl = TestData.callRepository("scmadmin", "scmadmin", repositoryType, HttpStatus.SC_OK) + .extract() + .path("_links.protocol.find{it.name=='http'}.href"); + + return REPOSITORY_CLIENT_FACTORY.create(repositoryType, httpProtocolUrl, folder); + } + public static String addAndCommitRandomFile(RepositoryClient client, String username) throws IOException { String uuid = UUID.randomUUID().toString(); String name = "file-" + uuid + ".uuid"; diff --git a/scm-it/src/test/java/sonia/scm/it/utils/RestUtil.java b/scm-it/src/test/java/sonia/scm/it/utils/RestUtil.java index 645cf06ac8..34c28a2a46 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/RestUtil.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/RestUtil.java @@ -29,9 +29,13 @@ public class RestUtil { } public static RequestSpecification given(String mediaType, String username, String password) { - return RestAssured.given() - .contentType(mediaType) - .accept(mediaType) + return givenAnonymous(mediaType) .auth().preemptive().basic(username, password); } + + public static RequestSpecification givenAnonymous(String mediaType) { + return RestAssured.given() + .contentType(mediaType) + .accept(mediaType); + } } diff --git a/scm-it/src/test/java/sonia/scm/it/utils/ScmTypes.java b/scm-it/src/test/java/sonia/scm/it/utils/ScmTypes.java index 4c9ac0ea44..4b48c89bbc 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/ScmTypes.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/ScmTypes.java @@ -1,11 +1,15 @@ package sonia.scm.it.utils; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; import sonia.scm.util.IOUtil; import java.util.ArrayList; import java.util.Collection; +import java.util.stream.Stream; -public class ScmTypes { +public class ScmTypes implements ArgumentsProvider { public static Collection availableScmTypes() { Collection params = new ArrayList<>(); @@ -18,4 +22,9 @@ public class ScmTypes { return params; } + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return availableScmTypes().stream().map(Arguments::of); + } } diff --git a/scm-it/src/test/java/sonia/scm/it/utils/TestData.java b/scm-it/src/test/java/sonia/scm/it/utils/TestData.java index c7d97a6891..cfefc1171a 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/TestData.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/TestData.java @@ -18,6 +18,7 @@ import java.util.stream.Collectors; import static java.util.Arrays.asList; import static sonia.scm.it.utils.RestUtil.createResourceUrl; import static sonia.scm.it.utils.RestUtil.given; +import static sonia.scm.it.utils.RestUtil.givenAnonymous; import static sonia.scm.it.utils.ScmTypes.availableScmTypes; public class TestData { @@ -25,7 +26,7 @@ public class TestData { private static final Logger LOG = LoggerFactory.getLogger(TestData.class); public static final String USER_SCM_ADMIN = "scmadmin"; - public static final String USER_ANONYMOUS = "anonymous"; + public static final String USER_ANONYMOUS = "_anonymous"; public static final Collection READ = asList("read", "pull"); public static final Collection WRITE = asList("read", "write", "pull", "push"); @@ -147,6 +148,16 @@ public class TestData { .statusCode(expectedStatusCode); } + public static ValidatableResponse callAnonymousRepository(String repositoryType, int expectedStatusCode) { + return givenAnonymous(VndMediaType.REPOSITORY) + + .when() + .get(getDefaultRepositoryUrl(repositoryType)) + + .then() + .statusCode(expectedStatusCode); + } + public static String getDefaultPermissionUrl(String username, String password, String repositoryType) { return given(VndMediaType.REPOSITORY, username, password) .when() diff --git a/scm-test/src/main/java/sonia/scm/repository/client/api/RepositoryClientFactory.java b/scm-test/src/main/java/sonia/scm/repository/client/api/RepositoryClientFactory.java index 17f1818e33..8dec6ec524 100644 --- a/scm-test/src/main/java/sonia/scm/repository/client/api/RepositoryClientFactory.java +++ b/scm-test/src/main/java/sonia/scm/repository/client/api/RepositoryClientFactory.java @@ -123,6 +123,12 @@ public final class RepositoryClientFactory password, workingCopy)); } + public RepositoryClient create(String type, String url, File workingCopy) + throws IOException + { + return new RepositoryClient(getProvider(type).create(url, null, null, workingCopy)); + } + //~--- get methods ---------------------------------------------------------- /**