From dcfaba904d94ed9cf37ab59d3477c212a0ce38bc Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Wed, 18 Jun 2025 16:49:14 -0400 Subject: [PATCH] NO-ISSUE: Improving IT to check for duplicated headers Signed-off-by: Ricardo Zanini --- .../src/main/openapi/slack-openapi.json | 97 +++++++++++++++++++ .../src/main/resources/application.properties | 7 +- .../security/CheckHeadersBearerTokenTest.java | 64 ++++++++++++ .../it/security/WiremockSlackServer.java | 44 +++++++++ 4 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 client/integration-tests/security/src/main/openapi/slack-openapi.json create mode 100644 client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/CheckHeadersBearerTokenTest.java create mode 100644 client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/WiremockSlackServer.java diff --git a/client/integration-tests/security/src/main/openapi/slack-openapi.json b/client/integration-tests/security/src/main/openapi/slack-openapi.json new file mode 100644 index 000000000..b3643ff6a --- /dev/null +++ b/client/integration-tests/security/src/main/openapi/slack-openapi.json @@ -0,0 +1,97 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Slack Actions for Slack API", + "description": "Slack Actions Slack API", + "version": "0.0.1" + }, + "paths": { + "/api/chat.postMessage": { + "post": { + "tags": [ + "Message" + ], + "summary": "Send slack message", + "description": "Send slack message to selected public channel", + "operationId": "sendSlackMessage", + "requestBody": { + "description": "Input parameters for the action sendSlackMessage", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendMessageRequest" + } + } + } + }, + "responses": { + "default": { + "description": "Send Message Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendMessageResponse" + } + } + } + } + }, + "deprecated": false, + "security": [ + { + "bearerAuth": [] + } + ] + } + } + }, + "components": { + "securitySchemes": { + "basicAuth": { + "type": "http", + "scheme": "basic" + }, + "bearerAuth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "ErrorCollection": { + "type": "object" + }, + "SendMessageRequest": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "channel": { + "type": "string" + } + } + }, + "SendMessageResponse": { + "type": "object", + "properties": { + "ok": { + "type": "boolean", + "description": "Indicates if the message was posted.", + "readOnly": true + }, + "channel": { + "type": "string", + "description": "The ID of the channel the message was posted.", + "readOnly": true + }, + "response_metadata": { + "type": "object", + "description": "The metadata of response details", + "readOnly": true + } + } + } + } + } +} \ No newline at end of file diff --git a/client/integration-tests/security/src/main/resources/application.properties b/client/integration-tests/security/src/main/resources/application.properties index 5422a5e76..e4bb70b7a 100644 --- a/client/integration-tests/security/src/main/resources/application.properties +++ b/client/integration-tests/security/src/main/resources/application.properties @@ -78,4 +78,9 @@ quarkus.oidc-client.service5_oauth2.grant.type=client quarkus.oidc-client.service5_oauth2.credentials.client-secret.method=basic quarkus.oidc-client.service5_oauth2.credentials.client-secret.value=secret -quarkus.keycloak.devservices.enabled=false \ No newline at end of file +quarkus.keycloak.devservices.enabled=false + +# Slack OpenAPI +quarkus.openapi-generator.codegen.spec.slack_openapi_json.base-package=org.acme.openapi.slack +quarkus.openapi-generator.slack_openapi_json.auth.bearerAuth.bearer-token=12345-TOKEN + diff --git a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/CheckHeadersBearerTokenTest.java b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/CheckHeadersBearerTokenTest.java new file mode 100644 index 000000000..32bea0529 --- /dev/null +++ b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/CheckHeadersBearerTokenTest.java @@ -0,0 +1,64 @@ +package io.quarkiverse.openapi.generator.it.security; + +import java.util.List; + +import jakarta.inject.Inject; + +import org.acme.openapi.slack.api.MessageApi; +import org.acme.openapi.slack.model.SendMessageRequest; +import org.acme.openapi.slack.model.SendMessageResponse; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTestResource(WiremockSlackServer.class) +@QuarkusTest +public class CheckHeadersBearerTokenTest { + + // injected by quarkus test resource + WireMockServer slackServer; + + // generated by OpenAPI + @RestClient + @Inject + MessageApi messageApi; + + @Test + void testNoDuplicateHeaders() { + SendMessageRequest sendMessageRequest = new SendMessageRequest(); + sendMessageRequest.setChannel("#test"); + sendMessageRequest.setText("Hello World"); + SendMessageResponse response = messageApi.sendSlackMessage(sendMessageRequest); + Assertions.assertNotNull(response, "Expected a non-null response"); + + List requests = slackServer.findAll( + WireMock.postRequestedFor(WireMock.urlEqualTo("/api/chat.postMessage"))); + Assertions.assertEquals(1, requests.size(), "Expected exactly one HTTP request"); + + LoggedRequest req = requests.get(0); + + String contentLength = req.getHeader("Content-Length"); + Assertions.assertNotNull(contentLength, "Content-Length header should be present"); + Assertions.assertTrue( + Integer.parseInt(contentLength) > 0, + "Content-Length should be a positive integer"); + + for (String headerName : req.getAllHeaderKeys()) { + List values = req.getHeaders().getHeader(headerName).values(); + Assertions.assertEquals( + 1, + values.size(), + () -> String.format( + "Header '%s' is duplicated; values: %s", + headerName, values)); + } + } + +} diff --git a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/WiremockSlackServer.java b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/WiremockSlackServer.java new file mode 100644 index 000000000..6f2ca85e4 --- /dev/null +++ b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/WiremockSlackServer.java @@ -0,0 +1,44 @@ +package io.quarkiverse.openapi.generator.it.security; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +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 WiremockSlackServer implements QuarkusTestResourceLifecycleManager { + public static final String URL_KEY = "quarkus.rest-client.slack_openapi_json.url"; + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort()); + wireMockServer.start(); + + wireMockServer.stubFor(post(urlPathEqualTo("/api/chat.postMessage")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + "{\"ok\": \"true\", \"channel\": \"#test\", \"response_metadata\": {}}"))); + + return Map.of(URL_KEY, wireMockServer.baseUrl()); + } + + @Override + public void inject(TestInjector testInjector) { + testInjector.injectIntoFields(wireMockServer, f -> f.getName().equals("slackServer")); + } + + @Override + public void stop() { + if (null != wireMockServer) { + wireMockServer.stop(); + } + } +}