Add integration tests for anonymous access

This commit is contained in:
Rene Pfeuffer
2019-10-17 11:53:23 +02:00
parent d9cf93b59a
commit 20338c1bb6
6 changed files with 192 additions and 10 deletions

View File

@@ -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();
}
}

View File

@@ -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";

View File

@@ -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);
}
}

View File

@@ -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<String> availableScmTypes() {
Collection<String> params = new ArrayList<>();
@@ -18,4 +22,9 @@ public class ScmTypes {
return params;
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
return availableScmTypes().stream().map(Arguments::of);
}
}

View File

@@ -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<String> READ = asList("read", "pull");
public static final Collection<String> 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()

View File

@@ -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 ----------------------------------------------------------
/**