From 0fbc6fd91729cba615cb32b1b3136d537ae0ae74 Mon Sep 17 00:00:00 2001 From: Simon Sternal Date: Fri, 17 Nov 2023 16:17:56 +0100 Subject: [PATCH 1/6] Fix API key cookie authentication (#568) --- .../ApiKeyAuthenticationProvider.java | 4 +- .../ApiKeyAuthenticationProviderTest.java | 49 +++++++++++++++---- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java b/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java index 56bed1eaa..2b70ad589 100644 --- a/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java +++ b/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java @@ -6,6 +6,7 @@ import jakarta.ws.rs.client.ClientRequestContext; import jakarta.ws.rs.core.Cookie; +import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.UriBuilder; import org.slf4j.Logger; @@ -44,7 +45,8 @@ public void filter(ClientRequestContext requestContext) throws IOException { requestContext.setUri(UriBuilder.fromUri(requestContext.getUri()).queryParam(apiKeyName, getApiKey()).build()); break; case cookie: - requestContext.getCookies().put(apiKeyName, new Cookie.Builder(apiKeyName).value(getApiKey()).build()); + final Cookie cookie = new Cookie.Builder(apiKeyName).value(getApiKey()).build(); + requestContext.getHeaders().add(HttpHeaders.COOKIE, cookie); break; case header: if (requestContext.getHeaderString("Authorization") != null diff --git a/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProviderTest.java b/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProviderTest.java index f7417c623..992cf65d6 100644 --- a/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProviderTest.java +++ b/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProviderTest.java @@ -7,12 +7,16 @@ import java.io.IOException; import java.net.URI; -import java.util.HashMap; -import java.util.Map; +import java.util.List; +import java.util.Objects; import java.util.Optional; import jakarta.ws.rs.core.Cookie; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MultivaluedMap; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.jboss.resteasy.specimpl.MultivaluedTreeMap; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -87,16 +91,36 @@ void filterQueryCase() throws IOException { } @Test - void filterCookieCase() throws IOException { - Map cookies = new HashMap<>(); - doReturn(cookies).when(requestContext).getCookies(); + void filterCookieCaseEmpty() throws IOException { + final MultivaluedMap headers = new MultivaluedTreeMap<>(); + doReturn(headers).when(requestContext).getHeaders(); provider = new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.cookie, API_KEY_NAME, generatorConfig); provider.filter(requestContext); - Cookie cookie = cookies.get(API_KEY_NAME); - assertThat(cookie).isNotNull(); - assertThat(cookie.getName()).isEqualTo(API_KEY_NAME); - assertThat(cookie.getValue()).isEqualTo(API_KEY_VALUE); + final List cookies = headers.get(HttpHeaders.COOKIE); + assertThat(cookies) + .singleElement() + .satisfies(cookie -> assertCookie(cookie, API_KEY_NAME, API_KEY_VALUE)); + } + + @Test + void filterCookieCaseExisting() throws IOException { + final MultivaluedMap headers = new MultivaluedTreeMap<>(); + final String existingCookieName = "quarkus"; + final String existingCookieValue = "rocks"; + final Cookie existingCookie = new Cookie.Builder(existingCookieName) + .value(existingCookieValue) + .build(); + headers.add(HttpHeaders.COOKIE, existingCookie); + doReturn(headers).when(requestContext).getHeaders(); + provider = new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.cookie, API_KEY_NAME, + generatorConfig); + provider.filter(requestContext); + final List cookies = headers.get(HttpHeaders.COOKIE); + assertThat(cookies) + .satisfiesExactlyInAnyOrder( + cookie -> assertCookie(cookie, existingCookieName, existingCookieValue), + cookie -> assertCookie(cookie, API_KEY_NAME, API_KEY_VALUE)); } @Test @@ -107,4 +131,11 @@ void tokenPropagationNotSupported() { .hasMessageContaining("quarkus.openapi-generator.%s.auth.%s.token-propagation", OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME); } + + private void assertCookie(final Object cookie, final String name, final String value) { + assertThat(cookie) + .asInstanceOf(InstanceOfAssertFactories.type(Cookie.class)) + .matches(c -> Objects.equals(c.getName(), name)) + .matches(c -> Objects.equals(c.getValue(), value)); + } } From cc7b26d5c60e7ee123e93c7b0501822cfdc3d943 Mon Sep 17 00:00:00 2001 From: Simon Sternal Date: Fri, 1 Dec 2023 13:27:01 +0100 Subject: [PATCH 2/6] Add integration test for key cookie authentication --- .../cookie-authentication/pom.xml | 106 ++++++++++++++++++ .../main/openapi/cookie-authentication.json | 46 ++++++++ .../src/main/resources/application.properties | 2 + .../it/CookieAuthenticationTest.java | 43 +++++++ .../it/WiremockCookieAuthentication.java | 41 +++++++ 5 files changed, 238 insertions(+) create mode 100644 integration-tests/cookie-authentication/pom.xml create mode 100644 integration-tests/cookie-authentication/src/main/openapi/cookie-authentication.json create mode 100644 integration-tests/cookie-authentication/src/main/resources/application.properties create mode 100644 integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/CookieAuthenticationTest.java create mode 100644 integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java diff --git a/integration-tests/cookie-authentication/pom.xml b/integration-tests/cookie-authentication/pom.xml new file mode 100644 index 000000000..681023e55 --- /dev/null +++ b/integration-tests/cookie-authentication/pom.xml @@ -0,0 +1,106 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-cookie-authentication + Quarkus - Openapi Generator - Integration Tests - Cookie Authentication + Use cookie authentication + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + com.github.tomakehurst + wiremock-jre8 + test + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + + + + diff --git a/integration-tests/cookie-authentication/src/main/openapi/cookie-authentication.json b/integration-tests/cookie-authentication/src/main/openapi/cookie-authentication.json new file mode 100644 index 000000000..e926a9280 --- /dev/null +++ b/integration-tests/cookie-authentication/src/main/openapi/cookie-authentication.json @@ -0,0 +1,46 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Quarkus - Openapi Generator - Integration Tests - Cookie Authentication", + "version": "v1" + }, + "servers": [ + { + "url": "http://localhost:8080" + } + ], + "security": [ + { + "cookie": [] + } + ], + "tags": [ + { + "name": "Test" + } + ], + "paths": { + "/v1/test": { + "get": { + "tags": [ + "Test" + ], + "operationId": "doTest", + "responses": { + "204": { + "description": "Test succeeded" + } + } + } + } + }, + "components": { + "securitySchemes": { + "cookie": { + "type": "apiKey", + "name": "TASKLIST-SESSION", + "in": "cookie" + } + } + } +} diff --git a/integration-tests/cookie-authentication/src/main/resources/application.properties b/integration-tests/cookie-authentication/src/main/resources/application.properties new file mode 100644 index 000000000..517251c08 --- /dev/null +++ b/integration-tests/cookie-authentication/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.openapi-generator.codegen.spec.cookie_authentication_json.mutiny=true +quarkus.openapi-generator.cookie_authentication_json.auth.cookie.api-key=Quarkus diff --git a/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/CookieAuthenticationTest.java b/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/CookieAuthenticationTest.java new file mode 100644 index 000000000..eb53e89f7 --- /dev/null +++ b/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/CookieAuthenticationTest.java @@ -0,0 +1,43 @@ +package io.quarkiverse.openapi.generator.it; + +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; + +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.openapi.quarkus.cookie_authentication_json.api.TestApi; + +import com.github.tomakehurst.wiremock.WireMockServer; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(WiremockCookieAuthentication.class) +@Tag("resteasy-reactive") +class CookieAuthenticationTest { + // Injected by Quarkus test resource + WireMockServer wireMockServer; + + @RestClient + @Inject + TestApi testApi; + + @Test + void apiIsBeingGenerated() { + final Response response = testApi.doTest() + .await() + .atMost(Duration.ofSeconds(5L)); + assertThat(response) + .extracting(Response::getStatus) + .isEqualTo(204); + wireMockServer.verify(getRequestedFor(urlEqualTo("/v1/test"))); + } +} diff --git a/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java b/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java new file mode 100644 index 000000000..bb27b83c7 --- /dev/null +++ b/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java @@ -0,0 +1,41 @@ +package io.quarkiverse.openapi.generator.it; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; + +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class WiremockCookieAuthentication implements QuarkusTestResourceLifecycleManager { + private static final String URL_KEY = "quarkus.rest-client.cookie_authentication_json.url"; + + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort()); + wireMockServer.start(); + + wireMockServer.stubFor(get(urlPathEqualTo("/v1/test")) + .willReturn(aResponse().withStatus(204))); + + return Map.of(URL_KEY, wireMockServer.baseUrl()); + } + + @Override + public void inject(final TestInjector testInjector) { + testInjector.injectIntoFields(wireMockServer, f -> f.getName().equals("wireMockServer")); + } + + @Override + public void stop() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } +} From 55f0879fcb7eee3a2dfbaae6dbab15016b7a3342 Mon Sep 17 00:00:00 2001 From: Simon Sternal Date: Fri, 1 Dec 2023 13:28:26 +0100 Subject: [PATCH 3/6] Apply review changes --- .../generator/providers/ApiKeyAuthenticationProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java b/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java index 2b70ad589..e949fb3ff 100644 --- a/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java +++ b/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java @@ -45,8 +45,7 @@ public void filter(ClientRequestContext requestContext) throws IOException { requestContext.setUri(UriBuilder.fromUri(requestContext.getUri()).queryParam(apiKeyName, getApiKey()).build()); break; case cookie: - final Cookie cookie = new Cookie.Builder(apiKeyName).value(getApiKey()).build(); - requestContext.getHeaders().add(HttpHeaders.COOKIE, cookie); + requestContext.getHeaders().add(HttpHeaders.COOKIE, new Cookie.Builder(apiKeyName).value(getApiKey()).build()); break; case header: if (requestContext.getHeaderString("Authorization") != null From 926ea1ffae0283d4ed77a5b6917e8aecb41d0454 Mon Sep 17 00:00:00 2001 From: Simon Sternal Date: Mon, 11 Dec 2023 10:34:06 +0100 Subject: [PATCH 4/6] Add new integration maven module --- integration-tests/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 870fb57c1..95172fd44 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -14,6 +14,7 @@ beanparam change-directory circuit-breaker + cookie-authentication custom-templates enum-property exclude From 88bd80ae0ab0990ed26cef3329d75d4085ac7c32 Mon Sep 17 00:00:00 2001 From: Simon Sternal <82364651+ssternal@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:09:16 +0100 Subject: [PATCH 5/6] Apply suggested changes (hopefully) fixing IT Co-authored-by: nmirasch --- integration-tests/cookie-authentication/pom.xml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/integration-tests/cookie-authentication/pom.xml b/integration-tests/cookie-authentication/pom.xml index 681023e55..52fa21115 100644 --- a/integration-tests/cookie-authentication/pom.xml +++ b/integration-tests/cookie-authentication/pom.xml @@ -27,14 +27,7 @@ quarkus-junit5 test - - io.quarkus - quarkus-resteasy-reactive - - - io.quarkus - quarkus-resteasy-reactive-jackson - + com.github.tomakehurst wiremock-jre8 From 7c73dcdc0148e5b9386b94a7e88c4326d3186ef2 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:41:29 -0300 Subject: [PATCH 6/6] removed the final to avoid the 'Confusing overloading of methods' Co-authored-by: nmirasch --- .../openapi/generator/it/WiremockCookieAuthentication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java b/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java index bb27b83c7..321ab06a1 100644 --- a/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java +++ b/integration-tests/cookie-authentication/src/test/java/io/quarkiverse/openapi/generator/it/WiremockCookieAuthentication.java @@ -28,7 +28,7 @@ public Map start() { } @Override - public void inject(final TestInjector testInjector) { + public void inject(TestInjector testInjector) { testInjector.injectIntoFields(wireMockServer, f -> f.getName().equals("wireMockServer")); }