From d72ee2fe3e0391bdd5651c1de3f46e16bc4cb8ab Mon Sep 17 00:00:00 2001 From: Sven Strittmatter Date: Fri, 15 Mar 2024 20:11:05 +0100 Subject: [PATCH] #121 Add Tests to Verify Presence of Proxy Auth Header Signed-off-by: Sven Strittmatter --- .../service/GenericDefectDojoServiceTest.java | 352 +++++++++++++----- .../service/WireMockBaseTestCase.java | 5 +- 2 files changed, 256 insertions(+), 101 deletions(-) diff --git a/src/test/java/io/securecodebox/persistence/defectdojo/service/GenericDefectDojoServiceTest.java b/src/test/java/io/securecodebox/persistence/defectdojo/service/GenericDefectDojoServiceTest.java index a67d2c6..bb413bb 100644 --- a/src/test/java/io/securecodebox/persistence/defectdojo/service/GenericDefectDojoServiceTest.java +++ b/src/test/java/io/securecodebox/persistence/defectdojo/service/GenericDefectDojoServiceTest.java @@ -1,13 +1,15 @@ package io.securecodebox.persistence.defectdojo.service; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import io.securecodebox.persistence.defectdojo.exception.PersistenceException; +import io.securecodebox.persistence.defectdojo.config.ClientConfig; +import io.securecodebox.persistence.defectdojo.http.ProxyConfig; import io.securecodebox.persistence.defectdojo.model.Model; import io.securecodebox.persistence.defectdojo.model.PaginatedResult; -import lombok.*; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; @@ -22,7 +24,8 @@ /** * Tests for {@link GenericDefectDojoService} */ -final class GenericDefectDojoServiceTest extends WireMockBaseTestCase { +final class GenericDefectDojoServiceTest { + private static final class TestModel implements Model { @JsonProperty private long id; @@ -37,7 +40,14 @@ public boolean equalsQueryString(@NonNull Map queryParams) { } } - private final GenericDefectDojoService sut = new GenericDefectDojoService<>(conf()) { + private static final class TestableGenericDefectDojoService extends GenericDefectDojoService { + private TestableGenericDefectDojoService(ClientConfig clientConfig) { + super(clientConfig); + } + + private TestableGenericDefectDojoService(@NonNull ClientConfig clientConfig, @NonNull ProxyConfig proxyConfig) { + super(clientConfig, proxyConfig); + } @Override protected String getUrlPath() { @@ -50,15 +60,12 @@ protected Class getModelClass() { } @Override + @SneakyThrows protected PaginatedResult deserializeList(@NonNull String response) { - try { - return this.objectMapper.readValue(response, new TypeReference<>() { - }); - } catch (JsonProcessingException e) { - throw new PersistenceException("Can't process JSON response!", e); - } + return this.objectMapper.readValue(response, new TypeReference<>() { + }); } - }; + } private static final String JSON_SINGLE_OBJECT = """ {"id": 42, "name": "foo"} @@ -75,118 +82,267 @@ protected PaginatedResult deserializeList(@NonNull String response) { } """; + private final ClientConfig clientConfig = new ClientConfig("https://defectdojo.example.com:8080","api-key"); + private final TestableGenericDefectDojoService sut = new TestableGenericDefectDojoService(clientConfig); + @Test void createBaseUrl() { - assertThat(sut.createBaseUrl(), is(URI.create("http://localhost:8888/api/v2/snafu/"))); + assertThat(sut.createBaseUrl(), is(URI.create("https://defectdojo.example.com:8080/api/v2/snafu/"))); } - @Test - void get_containsAuthHeaderInRequest() { - stubFor(get(urlPathEqualTo("/api/v2/snafu/42")) - .willReturn(ok() - .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) - .withBody(JSON_SINGLE_OBJECT))); + @Nested + class AuthenticationHeaderWithoutProxyConfig extends WireMockBaseTestCase { + private final TestableGenericDefectDojoService sut = new TestableGenericDefectDojoService( + conf(), ProxyConfig.NULL + ); - sut.get(42L); + @Test + void get_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/42")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) + .withBody(JSON_SINGLE_OBJECT))); - verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/42")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.get(42L); - @Test - void search_containsAuthHeaderInRequest() { - stubFor(get(urlPathEqualTo("/api/v2/snafu/")) - .willReturn(ok() - .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) - .withBody(JSON_MULTIPLE_OBJECT))); + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/42")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.search(); + @Test + void search_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); - verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.search(); - @Test - void searchWithQueryParams_containsAuthHeaderInRequest() { - stubFor(get(urlPathEqualTo("/api/v2/snafu/")) - .willReturn(ok() - .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) - .withBody(JSON_MULTIPLE_OBJECT))); + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.search(Collections.emptyMap()); + @Test + void searchWithQueryParams_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); - verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.search(Collections.emptyMap()); - @Test - void searchUniqueWithObject_containsAuthHeaderInRequest() { - stubFor(get(urlPathEqualTo("/api/v2/snafu/")) - .willReturn(ok() - .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) - .withBody(JSON_MULTIPLE_OBJECT))); + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.searchUnique(new TestModel()); + @Test + void searchUniqueWithObject_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); - verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.searchUnique(new TestModel()); - @Test - void searchUniqueWithQueryParams_containsAuthHeaderInRequest() { - stubFor(get(urlPathEqualTo("/api/v2/snafu/")) - .willReturn(ok() - .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) - .withBody(JSON_MULTIPLE_OBJECT))); + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.searchUnique(Collections.emptyMap()); + @Test + void searchUniqueWithQueryParams_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); - verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.searchUnique(Collections.emptyMap()); - @Test - void create_containsAuthHeaderInRequest() { - stubFor(post(urlPathEqualTo("/api/v2/snafu/")) - .willReturn(created() - .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) - .withBody(JSON_SINGLE_OBJECT))); + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.create(new TestModel()); + @Test + void create_containsAuthHeaderInRequest() { + stubFor(post(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(created() + .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) + .withBody(JSON_SINGLE_OBJECT))); - verify(postRequestedFor(urlPathEqualTo("/api/v2/snafu/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.create(new TestModel()); - @Test - void delete_containsAuthHeaderInRequest() { - stubFor(delete(urlPathEqualTo("/api/v2/snafu/42/")) - .willReturn(ok())); + verify(postRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.delete(42); + @Test + void delete_containsAuthHeaderInRequest() { + stubFor(delete(urlPathEqualTo("/api/v2/snafu/42/")) + .willReturn(ok())); - verify(deleteRequestedFor(urlPathEqualTo("/api/v2/snafu/42/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) - ); - } + sut.delete(42); - @Test - void update_containsAuthHeaderInRequest() { - stubFor(put(urlPathEqualTo("/api/v2/snafu/42/")) - .willReturn(ok() - .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) - .withBody(JSON_SINGLE_OBJECT))); + verify(deleteRequestedFor(urlPathEqualTo("/api/v2/snafu/42/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } - sut.update(new TestModel(), 42); + @Test + void update_containsAuthHeaderInRequest() { + stubFor(put(urlPathEqualTo("/api/v2/snafu/42/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) + .withBody(JSON_SINGLE_OBJECT))); - verify(putRequestedFor(urlPathEqualTo("/api/v2/snafu/42/")) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token not-required-for-tests")) + sut.update(new TestModel(), 42); + + verify(putRequestedFor(urlPathEqualTo("/api/v2/snafu/42/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withoutHeader(HttpHeaders.PROXY_AUTHORIZATION) + ); + } + } + + @Nested + class AuthenticationHeaderWithProxyConfig extends WireMockBaseTestCase { + private final ProxyConfig proxyConfig = ProxyConfig.builder() + .user("alf") + .password("test1234") + .host("proxy.owasp.org") + .port(8080) + .build(); + private final TestableGenericDefectDojoService sut = new TestableGenericDefectDojoService( + conf(), proxyConfig ); + + @Test + void get_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/42")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) + .withBody(JSON_SINGLE_OBJECT))); + + sut.get(42L); + + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/42")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void search_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); + + sut.search(); + + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void searchWithQueryParams_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); + + sut.search(Collections.emptyMap()); + + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void searchUniqueWithObject_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); + + sut.searchUnique(new TestModel()); + + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void searchUniqueWithQueryParams_containsAuthHeaderInRequest() { + stubFor(get(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_MULTIPLE_OBJECT.length())) + .withBody(JSON_MULTIPLE_OBJECT))); + + sut.searchUnique(Collections.emptyMap()); + + verify(getRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void create_containsAuthHeaderInRequest() { + stubFor(post(urlPathEqualTo("/api/v2/snafu/")) + .willReturn(created() + .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) + .withBody(JSON_SINGLE_OBJECT))); + + sut.create(new TestModel()); + + verify(postRequestedFor(urlPathEqualTo("/api/v2/snafu/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void delete_containsAuthHeaderInRequest() { + stubFor(delete(urlPathEqualTo("/api/v2/snafu/42/")) + .willReturn(ok())); + + sut.delete(42); + + verify(deleteRequestedFor(urlPathEqualTo("/api/v2/snafu/42/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } + + @Test + void update_containsAuthHeaderInRequest() { + stubFor(put(urlPathEqualTo("/api/v2/snafu/42/")) + .willReturn(ok() + .withHeaders(responseHeaders(JSON_SINGLE_OBJECT.length())) + .withBody(JSON_SINGLE_OBJECT))); + + sut.update(new TestModel(), 42); + + verify(putRequestedFor(urlPathEqualTo("/api/v2/snafu/42/")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Token " + API_KEY)) + .withHeader(HttpHeaders.PROXY_AUTHORIZATION, equalTo("Basic YWxmOnRlc3QxMjM0")) + ); + } } } diff --git a/src/test/java/io/securecodebox/persistence/defectdojo/service/WireMockBaseTestCase.java b/src/test/java/io/securecodebox/persistence/defectdojo/service/WireMockBaseTestCase.java index c0918f1..af999e0 100644 --- a/src/test/java/io/securecodebox/persistence/defectdojo/service/WireMockBaseTestCase.java +++ b/src/test/java/io/securecodebox/persistence/defectdojo/service/WireMockBaseTestCase.java @@ -25,6 +25,7 @@ @WireMockTest(httpPort = WireMockBaseTestCase.PORT) abstract class WireMockBaseTestCase { static final int PORT = 8888; + static final String API_KEY = "not-required-for-tests"; static final String EMPTY_SEARCH_RESULT_RESPONSE_FIXTURE = """ { "count": 0, @@ -36,9 +37,7 @@ abstract class WireMockBaseTestCase { """; private static final String FIXTURE_BASE_PACKAGE = "io/securecodebox/persistence/defectdojo/service"; - private final ClientConfig conf = new ClientConfig( - String.format("http://localhost:%d/", PORT), - "not-required-for-tests"); + private final ClientConfig conf = new ClientConfig(String.format("http://localhost:%d/", PORT), API_KEY); String readFixtureFile(String fixtureFile) throws IOException { final var fixtureFilePath = FIXTURE_BASE_PACKAGE + "/" + fixtureFile;