From e2b819bd2c48a3f2ac7aae63a9f933434a1552ff Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Fri, 11 Oct 2024 17:46:22 +0200 Subject: [PATCH 1/3] Move Vert.x client code to the core module --- bom/pom.xml | 5 --- .../client/RequestBodyOutputStreamTest.java | 0 extensions/core/pom.xml | 1 - extensions/core/runtime/pom.xml | 4 -- .../cxf/vertx/http/client/HttpClientPool.java | 0 .../client/VertxHttpClientHTTPConduit.java | 0 .../VertxHttpClientHTTPConduitFactory.java | 0 .../vertx/http/client/VertxHttpException.java | 0 extensions/core/vertx-client/pom.xml | 44 ------------------- 9 files changed, 54 deletions(-) rename extensions/core/{vertx-client => deployment}/src/test/java/io/quarkiverse/cxf/vertx/http/client/RequestBodyOutputStreamTest.java (100%) rename extensions/core/{vertx-client => runtime}/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java (100%) rename extensions/core/{vertx-client => runtime}/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java (100%) rename extensions/core/{vertx-client => runtime}/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java (100%) rename extensions/core/{vertx-client => runtime}/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpException.java (100%) delete mode 100644 extensions/core/vertx-client/pom.xml diff --git a/bom/pom.xml b/bom/pom.xml index f85265353..b79716be6 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -260,11 +260,6 @@ quarkus-cxf-services-sts-deployment ${quarkus-cxf.version} - - io.quarkiverse.cxf - quarkus-cxf-vertx-client - ${quarkus-cxf.version} - io.quarkiverse.cxf quarkus-cxf-woodstox diff --git a/extensions/core/vertx-client/src/test/java/io/quarkiverse/cxf/vertx/http/client/RequestBodyOutputStreamTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/vertx/http/client/RequestBodyOutputStreamTest.java similarity index 100% rename from extensions/core/vertx-client/src/test/java/io/quarkiverse/cxf/vertx/http/client/RequestBodyOutputStreamTest.java rename to extensions/core/deployment/src/test/java/io/quarkiverse/cxf/vertx/http/client/RequestBodyOutputStreamTest.java diff --git a/extensions/core/pom.xml b/extensions/core/pom.xml index e242c277d..3c37fcbf8 100644 --- a/extensions/core/pom.xml +++ b/extensions/core/pom.xml @@ -14,7 +14,6 @@ axiom-api-stub - vertx-client runtime deployment diff --git a/extensions/core/runtime/pom.xml b/extensions/core/runtime/pom.xml index 443bfdf71..d6485a5a0 100644 --- a/extensions/core/runtime/pom.xml +++ b/extensions/core/runtime/pom.xml @@ -38,10 +38,6 @@ io.quarkiverse.cxf quarkus-cxf-axiom-api-stub - - io.quarkiverse.cxf - quarkus-cxf-vertx-client - io.quarkiverse.cxf quarkus-cxf-woodstox diff --git a/extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java similarity index 100% rename from extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java rename to extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java diff --git a/extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java similarity index 100% rename from extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java rename to extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java diff --git a/extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java similarity index 100% rename from extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java rename to extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java diff --git a/extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpException.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpException.java similarity index 100% rename from extensions/core/vertx-client/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpException.java rename to extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpException.java diff --git a/extensions/core/vertx-client/pom.xml b/extensions/core/vertx-client/pom.xml deleted file mode 100644 index 8a8ee9506..000000000 --- a/extensions/core/vertx-client/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - 4.0.0 - - io.quarkiverse.cxf - quarkus-cxf-core - 3.16.0-SNAPSHOT - ../pom.xml - - - quarkus-cxf-vertx-client - Quarkus CXF - Vert.x Web client - A CXF conduit using Vert.x Web client and an underlying HTTP client - - - - io.vertx - vertx-core - - - org.apache.cxf - cxf-rt-transports-http - - - - org.junit.jupiter - junit-jupiter - test - - - org.assertj - assertj-core - ${assertj.version} - test - - - org.jboss.logmanager - jboss-logmanager - test - - - - - From c4f048621f811ec86687ac5a97b07eb8fa1b6ed9 Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Sun, 13 Oct 2024 16:13:45 +0200 Subject: [PATCH 2/3] Simplified Vert.x client --- .../build-and-run-jvm-tests/action.yml | 4 +- .github/actions/run-native-test/action.yml | 4 +- .github/workflows/build.yml | 4 +- .../ws-security-policy/application.properties | 8 + .../reference/extensions/quarkus-cxf.adoc | 17 +- .../ROOT/pages/release-notes/3.16.0.adoc | 44 ++ .../io/quarkiverse/cxf/doc/it/AntoraTest.java | 12 +- .../cxf/deployment/test/BadHostnameTest.java | 116 ++++ .../deployment/test/HostnameVerifierTest.java | 7 +- .../deployment/test/TlsConfigurationTest.java | 139 ++++ .../io/quarkiverse/cxf/CXFClientInfo.java | 6 +- .../io/quarkiverse/cxf/CxfClientConfig.java | 19 +- .../quarkiverse/cxf/CxfTlsConfiguration.java | 45 +- .../cxf/QuarkusHTTPConduitFactory.java | 32 +- .../cxf/QuarkusTLSClientParameters.java | 33 + .../cxf/vertx/http/client/HttpClientPool.java | 623 +----------------- .../client/VertxHttpClientHTTPConduit.java | 32 +- .../policy/SecurityPolicyResource.java | 6 + .../src/main/resources/application.properties | 8 + .../security/policy/TransportPolicyTest.java | 108 ++- 20 files changed, 604 insertions(+), 663 deletions(-) create mode 100644 docs/modules/ROOT/pages/release-notes/3.16.0.adoc create mode 100644 extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/BadHostnameTest.java create mode 100644 extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java create mode 100644 extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusTLSClientParameters.java diff --git a/.github/actions/build-and-run-jvm-tests/action.yml b/.github/actions/build-and-run-jvm-tests/action.yml index f610312f7..bb3d8a455 100644 --- a/.github/actions/build-and-run-jvm-tests/action.yml +++ b/.github/actions/build-and-run-jvm-tests/action.yml @@ -42,10 +42,10 @@ runs: run: ./mvnw -B formatter:validate install -fae -ntp # Same as the previous but only JVM tests and different default ConduitFactory - - name: QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=VertxHttpClientHTTPConduitFactory mvn -B test + - name: QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=URLConnectionHTTPConduitFactory mvn -B test shell: bash env: - QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: VertxHttpClientHTTPConduitFactory + QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: URLConnectionHTTPConduitFactory run: ./mvnw -B clean install -fae -ntp - name: 'Upload generated Antora docs site' diff --git a/.github/actions/run-native-test/action.yml b/.github/actions/run-native-test/action.yml index fdac7ae67..e56d8a4a7 100644 --- a/.github/actions/run-native-test/action.yml +++ b/.github/actions/run-native-test/action.yml @@ -49,10 +49,10 @@ runs: fi # Same as the previous but different default ConduitFactory - - name: Run integration test ${{ inputs.test-module-spec }} with QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=VertxHttpClientHTTPConduitFactory + - name: Run integration test ${{ inputs.test-module-spec }} with QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=URLConnectionHTTPConduitFactory shell: bash env: - QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: VertxHttpClientHTTPConduitFactory + QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: URLConnectionHTTPConduitFactory # Skip native tests for CodeQL Security Scans if: "${{ env.SKIP_NATIVE_TESTS != 'true' }}" run: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf16459b0..9abe51efe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,9 +83,9 @@ jobs: -fae # Same as the previous but different default ConduitFactory - - name: mvn test + - name: QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=URLConnectionHTTPConduitFactory mvn test env: - QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: VertxHttpClientHTTPConduitFactory + QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: URLConnectionHTTPConduitFactory shell: bash run: | ./mvnw -B clean test \ diff --git a/docs/modules/ROOT/examples/ws-security-policy/application.properties b/docs/modules/ROOT/examples/ws-security-policy/application.properties index 74b3e6f68..534649d4c 100644 --- a/docs/modules/ROOT/examples/ws-security-policy/application.properties +++ b/docs/modules/ROOT/examples/ws-security-policy/application.properties @@ -154,6 +154,14 @@ quarkus.cxf.client.helloAllowAll.trust-store = client-truststore.pkcs12 quarkus.cxf.client.helloAllowAll.trust-store-password = client-truststore-password quarkus.cxf.client.helloAllowAll.hostname-verifier = AllowAllHostnameVerifier + +quarkus.tls.helloAllowAll.trust-store.p12.path = client-truststore.pkcs12 +quarkus.tls.helloAllowAll.trust-store.p12.password = client-truststore-password +quarkus.tls.helloAllowAll.hostname-verification-algorithm = NONE +quarkus.cxf.client.helloAllowAllTlsConfig.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello +quarkus.cxf.client.helloAllowAllTlsConfig.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService +quarkus.cxf.client.helloAllowAllTlsConfig.tls-configuration-name = helloAllowAll + quarkus.cxf.client.helloCustomHostnameVerifier.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.helloCustomHostnameVerifier.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloCustomHostnameVerifier.trust-store = client-truststore.pkcs12 diff --git a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc index 54c53bb57..05527c2a1 100644 --- a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc @@ -2002,12 +2002,25 @@ instead. - One of the well known values: `AllowAllHostnameVerifier`, `HttpsURLConnectionDefaultHostnameVerifier` - A fully qualified class name implementing `javax.net.ssl.HostnameVerifier` to look up in the CDI container. -- A bean name prefixed with `++#++` that will be looked up in the CDI container; example: `++#++myHostnameVerifier` If -not specified, then the creation of the `HostnameVerifier` is delegated to CXF, which boils down to +- A bean name prefixed with `++#++` that will be looked up in the CDI container; example: `++#++myHostnameVerifier` + +If not specified, then the creation of the `HostnameVerifier` is delegated to CXF, which boils down to `org.apache.cxf.transport.https.httpclient.DefaultHostnameVerifier` with the default `org.apache.cxf.transport.https.httpclient.PublicSuffixMatcherLoader` as returned from `PublicSuffixMatcherLoader.getDefault()`. +[IMPORTANT] +==== +Setting this option when the conduit factory of this client is set to `VertxHttpClientHTTPConduitFactory` +(default since {quarkus-cxf-project-name} 3.16.0) leads to an exception at runtime. +The `AllowAllHostnameVerifier` value of this option can be replaced by using a +xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-tls-configuration-name[named TLS +configuration] +with +`{link-quarkus-docs-base}/tls-registry-reference#trusting-all-certificates-and-hostname-verification[hostname-verification-algorithm]` +set to `NONE`. Otherwise, there is no way to implement custom hostname verification for Vert.x HTTP client. +==== + *Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__HOSTNAME_VERIFIER+++` + *Since Quarkus CXF*: 2.5.0 diff --git a/docs/modules/ROOT/pages/release-notes/3.16.0.adoc b/docs/modules/ROOT/pages/release-notes/3.16.0.adoc new file mode 100644 index 000000000..fa5affe13 --- /dev/null +++ b/docs/modules/ROOT/pages/release-notes/3.16.0.adoc @@ -0,0 +1,44 @@ += {quarkus-cxf-project-name} 3.16.0 LTS release notes + +This is the first release in the new 3.16 https://quarkus.io/blog/lts-releases/[LTS stream]. +As usual, where we plan to produce patch releases with bug and security fixes for 12 months. + +== Important dependency upgrades + +* Quarkus 3.15.x -> 3.16.0 - https://quarkus.io/blog/quarkus-3-16-0-released/[release notes] + +== New and noteworthy in {quarkus-cxf-project-name} + +=== Vert.x HttpClient based HTTP Conduit is the new default + +The `VertxHttpClientHTTPConduitFactory` was introduced in {quarkus-cxf-project-name} 3.13.0. +Since then, it went through some improvements and testing +so that we are confident enough to make it the default for the xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-http-conduit-factory[quarkus.cxf.client."client-name".http-conduit-factory] and +xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-http-conduit-factory[quarkus.cxf.http-conduit-factory] options. + +==== SSL/TLS configuration + +Prefer the new named TLS configurations for CXF clients. + +==== Hostname verifiers not supported in combination with `VertxHttpClientHTTPConduitFactory` + +Setting `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-hostname-verifier[quarkus.cxf.client."client-name".hostname-verifier]` together with `VertxHttpClientHTTPConduitFactory` leads to an exception at runtime. + +The `AllowAllHostnameVerifier` value of that option can be replaced by using a +xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-tls-configuration-name[named TLS configuration] +with `{link-quarkus-docs-base}/tls-registry-reference#trusting-all-certificates-and-hostname-verification[hostname-verification-algorithm]` +set to `NONE`. + +Otherwise, there is no way to implement custom hostname verification for Vert.x HTTP client. + +==== Force the old default + +There are three options how you can get back to the old default: + +* Set the `QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY` environment variable to `URLConnectionHTTPConduitFactory` +* Set the global xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-http-conduit-factory[quarkus.cxf.http-conduit-factory] +* Set the per client xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-http-conduit-factory[quarkus.cxf.client."client-name".http-conduit-factory] option to `URLConnectionHTTPConduitFactory` + +== Full changelog + +https://github.com/quarkiverse/quarkus-cxf/compare/3.15.0+++...+++3.16.0 diff --git a/docs/src/test/java/io/quarkiverse/cxf/doc/it/AntoraTest.java b/docs/src/test/java/io/quarkiverse/cxf/doc/it/AntoraTest.java index 4d34eb600..69bb99d2f 100644 --- a/docs/src/test/java/io/quarkiverse/cxf/doc/it/AntoraTest.java +++ b/docs/src/test/java/io/quarkiverse/cxf/doc/it/AntoraTest.java @@ -1,6 +1,9 @@ package io.quarkiverse.cxf.doc.it; import java.io.IOException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.TimeoutException; @@ -29,12 +32,19 @@ public void antoraSite() throws TimeoutException, IOException, InterruptedExcept @Test public void externalLinks() { - Set ignorables = Set.of( + Set ignorables1 = Set.of( /* known issue https://github.com/quarkiverse/antora-ui-quarkiverse/issues/86 */ "http://quarkus.io/training", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role", "http://schemas.xmlsoap.org/ws/2005/02/sc/dk/p_sha1", "http://www.w3.org/2009/xmlenc11#aes256-gcm"); + final Set ignorables = new LinkedHashSet<>(ignorables1); + + final ZonedDateTime deadline_2_16_0 = ZonedDateTime.parse("2024-10-31T23:59:59+01:00[Europe/Paris]"); + if (ZonedDateTime.now(ZoneId.of("Europe/Paris")).isBefore(deadline_2_16_0)) { + ignorables.add("https://quarkus.io/blog/quarkus-3-16-0-released/"); + ignorables.add("https://github.com/quarkiverse/quarkus-cxf/compare/3.15.0...3.16.0"); + } AntoraTestUtils.assertExternalLinksValid(err -> ignorables.contains(err.uri())); } diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/BadHostnameTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/BadHostnameTest.java new file mode 100644 index 000000000..a7c96c85c --- /dev/null +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/BadHostnameTest.java @@ -0,0 +1,116 @@ +package io.quarkiverse.cxf.deployment.test; + +import jakarta.inject.Inject; +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; + +import org.assertj.core.api.Assertions; +import org.jboss.logging.Logger; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.cxf.annotation.CXFClient; +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; + +@Certificates(baseDir = "target/classes", // + certificates = @Certificate( // + name = "fake-host", // + password = "secret", // + cn = "fake-host", // + formats = { Format.PKCS12 })) +public class BadHostnameTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloService.class, HelloServiceImpl.class)) + + /* Server TLS */ + .overrideConfigKey("quarkus.tls.key-store.p12.path", "fake-host-keystore.p12") + .overrideConfigKey("quarkus.tls.key-store.p12.password", "secret") + .overrideConfigKey("quarkus.http.insecure-requests", "disabled") + /* Service */ + .overrideConfigKey("quarkus.cxf.endpoint.\"/hello\".implementor", + HelloServiceImpl.class.getName()) + .overrideConfigKey("quarkus.cxf.endpoint.\"/hello\".logging.enabled", "true") + + /* Named TLS configuration for the clients */ + .overrideConfigKey("quarkus.tls.client-pkcs12.trust-store.p12.path", "fake-host-truststore.p12") + .overrideConfigKey("quarkus.tls.client-pkcs12.trust-store.p12.password", "secret") + + /* Client with VertxHttpClientHTTPConduitFactory */ + .overrideConfigKey("quarkus.cxf.client.helloVertx.client-endpoint-url", "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloVertx.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloVertx.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloVertx.http-conduit-factory", "VertxHttpClientHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloVertx.tls-configuration-name", "client-pkcs12") + + /* Client with HttpClientHTTPConduitFactory */ + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.client-endpoint-url", + "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.http-conduit-factory", "HttpClientHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.tls-configuration-name", "client-pkcs12") + + /* Client with URLConnectionHTTPConduitFactory */ + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.client-endpoint-url", + "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.http-conduit-factory", "URLConnectionHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.tls-configuration-name", "client-pkcs12"); + + @CXFClient("helloVertx") + HelloService helloVertx; + + @CXFClient("helloHttpClient") + HelloService helloHttpClient; + + @CXFClient("helloUrlConnection") + HelloService helloUrlConnection; + + @Inject + Logger logger; + + @Test + void vertx() { + Assertions.assertThatThrownBy(() -> helloVertx.hello("Doe")).hasRootCauseMessage( + "No subject alternative DNS name matching localhost found."); + } + + @Test + void httpClient() { + Assertions.assertThatThrownBy(() -> helloHttpClient.hello("Doe")).hasRootCauseMessage( + "No name matching localhost found"); + } + + @Test + void urlConnection() { + Assertions.assertThatThrownBy(() -> helloUrlConnection.hello("Doe")).hasRootCauseMessage( + "The https URL hostname does not match the Common Name (CN) on the server certificate in the client's truststore. Make sure server certificate is correct, or to disable this check (NOT recommended for production) set the CXF client TLS configuration property \"disableCNCheck\" to true."); + } + + @WebService + public interface HelloService { + + @WebMethod + String hello(String person); + + } + + @WebService(serviceName = "HelloService") + public static class HelloServiceImpl implements HelloService { + + @Override + public String hello(String person) { + return "Hello " + person; + } + } + +} diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java index 972fb7adb..1b5d5a139 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java @@ -116,11 +116,8 @@ void customHostnameVerifierVertx() { Assertions.assertThat(customHostnameVerifier.getCheckedHostNames()).isEmpty(); customHostnameVerifier.setReturnVal(false); Assertions.assertThatThrownBy(() -> helloVertx.hello("Doe")).hasRootCauseMessage( - "The https URL hostname localhost does not match the Common Name (CN) on the server certificate in the client's truststore. Make sure server certificate is correct, or to disable this check (NOT recommended for production) set the CXF client TLS configuration property \"disableCNCheck\" to true."); - Assertions.assertThat(customHostnameVerifier.getCheckedHostNames()).containsExactly("localhost"); - customHostnameVerifier.setReturnVal(true); - Assertions.assertThat(helloVertx.hello("Joe")).isEqualTo("Hello Joe"); - Assertions.assertThat(customHostnameVerifier.getCheckedHostNames()).containsExactly("localhost", "localhost"); + "io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit does not support setting a hostname verifier. AllowAllHostnameVerifier can be replaced by using a named TLS configuration with hostname-verification-algorithm set to NONE"); + Assertions.assertThat(customHostnameVerifier.getCheckedHostNames()).isEmpty(); } @Test diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java new file mode 100644 index 000000000..9cdae8576 --- /dev/null +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java @@ -0,0 +1,139 @@ +package io.quarkiverse.cxf.deployment.test; + +import jakarta.inject.Inject; +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; + +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.endpoint.Client; +import org.apache.cxf.frontend.ClientProxy; +import org.apache.cxf.transport.http.HTTPConduit; +import org.assertj.core.api.Assertions; +import org.jboss.logging.Logger; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.cxf.QuarkusTLSClientParameters; +import io.quarkiverse.cxf.annotation.CXFClient; +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; + +@Certificates(baseDir = "target/classes", // + certificates = @Certificate( // + name = "localhost", // + password = "secret", // + formats = { Format.PKCS12 })) +public class TlsConfigurationTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloService.class, HelloServiceImpl.class)) + + /* Server TLS */ + .overrideConfigKey("quarkus.tls.localhost-pkcs12.key-store.p12.path", "localhost-keystore.p12") + .overrideConfigKey("quarkus.tls.localhost-pkcs12.key-store.p12.password", "secret") + .overrideConfigKey("quarkus.http.tls-configuration-name", "localhost-pkcs12") + .overrideConfigKey("quarkus.http.insecure-requests", "disabled") + /* Service */ + .overrideConfigKey("quarkus.cxf.endpoint.\"/hello\".implementor", + HelloServiceImpl.class.getName()) + .overrideConfigKey("quarkus.cxf.endpoint.\"/hello\".logging.enabled", "true") + + /* Named TLS configuration for the clients */ + .overrideConfigKey("quarkus.tls.client-pkcs12.trust-store.p12.path", "target/classes/localhost-truststore.p12") + .overrideConfigKey("quarkus.tls.client-pkcs12.trust-store.p12.password", "secret") + + /* Client with VertxHttpClientHTTPConduitFactory */ + .overrideConfigKey("quarkus.cxf.client.helloVertx.client-endpoint-url", "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloVertx.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloVertx.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloVertx.http-conduit-factory", "VertxHttpClientHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloVertx.tls-configuration-name", "client-pkcs12") + + /* Client with HttpClientHTTPConduitFactory */ + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.client-endpoint-url", + "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.http-conduit-factory", "HttpClientHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloHttpClient.tls-configuration-name", "client-pkcs12") + + /* Client with URLConnectionHTTPConduitFactory */ + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.client-endpoint-url", + "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.http-conduit-factory", "URLConnectionHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloUrlConnection.tls-configuration-name", "client-pkcs12"); + + @CXFClient("helloVertx") + HelloService helloVertx; + + @CXFClient("helloHttpClient") + HelloService helloHttpClient; + + @CXFClient("helloUrlConnection") + HelloService helloUrlConnection; + + @Inject + Logger logger; + + @Test + void vertx() { + Assertions.assertThat(helloVertx.hello("Doe")).isEqualTo("Hello Doe"); + } + + @Test + void httpClient() { + Assertions.assertThat(helloHttpClient.hello("Doe")).isEqualTo("Hello Doe"); + } + + @Test + void urlConnection() { + Assertions.assertThat(helloUrlConnection.hello("Doe")).isEqualTo("Hello Doe"); + } + + @Test + void sameTlsConfiguration() { + + /* The TlsConfigurations must be the same instance, otherwise the identity based caching in HttpClientPool would not work */ + + TLSClientParameters p1 = getTLSClientParameters(helloUrlConnection); + TLSClientParameters p2 = getTLSClientParameters(helloVertx); + + Assertions.assertThat(p1).isNotNull().isInstanceOf(QuarkusTLSClientParameters.class); + Assertions.assertThat(p2).isNotNull().isInstanceOf(QuarkusTLSClientParameters.class); + Assertions.assertThat(((QuarkusTLSClientParameters) p1).getTlsConfiguration()) + .isSameAs(((QuarkusTLSClientParameters) p2).getTlsConfiguration()); + + } + + static TLSClientParameters getTLSClientParameters(HelloService cl) { + final Client client = ClientProxy.getClient(cl); + HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); + return httpConduit.getTlsClientParameters(); + } + + @WebService + public interface HelloService { + + @WebMethod + String hello(String person); + + } + + @WebService(serviceName = "HelloService") + public static class HelloServiceImpl implements HelloService { + + @Override + public String hello(String person) { + return "Hello " + person; + } + } + +} diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java index bfe2f4849..85c61614f 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java @@ -303,7 +303,11 @@ static TlsConfiguration tlsConfiguration(Vertx vertx, CxfClientConfig config, St trustOptions = null; trustStore = null; } - final CxfTlsConfiguration cxfTlsConfiguration = new CxfTlsConfiguration(keyStoreOptions, keyStore, trustOptions, + + final CxfTlsConfiguration cxfTlsConfiguration = new CxfTlsConfiguration( + keyStoreOptions, + keyStore, + trustOptions, trustStore); tlsRegistry.register(registryKey, cxfTlsConfiguration); return cxfTlsConfiguration; diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java index 194948d84..525d7dfe4 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java @@ -532,12 +532,25 @@ public interface CxfClientConfig { * * - One of the well known values: `AllowAllHostnameVerifier`, `HttpsURLConnectionDefaultHostnameVerifier` * - A fully qualified class name implementing `javax.net.ssl.HostnameVerifier` to look up in the CDI container. - * - A bean name prefixed with `++#++` that will be looked up in the CDI container; example: `++#++myHostnameVerifier` If - * not specified, then the creation of the `HostnameVerifier` is delegated to CXF, which boils down to + * - A bean name prefixed with `++#++` that will be looked up in the CDI container; example: `++#++myHostnameVerifier` + * + * If not specified, then the creation of the `HostnameVerifier` is delegated to CXF, which boils down to * `org.apache.cxf.transport.https.httpclient.DefaultHostnameVerifier` with the default * `org.apache.cxf.transport.https.httpclient.PublicSuffixMatcherLoader` as returned from * `PublicSuffixMatcherLoader.getDefault()`. * + * [IMPORTANT] + * ==== + * Setting this option when the conduit factory of this client is set to `VertxHttpClientHTTPConduitFactory` + * (default since {quarkus-cxf-project-name} 3.16.0) leads to an exception at runtime. + * The `AllowAllHostnameVerifier` value of this option can be replaced by using a + * xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-tls-configuration-name[named TLS + * configuration] + * with + * `{link-quarkus-docs-base}/tls-registry-reference#trusting-all-certificates-and-hostname-verification[hostname-verification-algorithm]` + * set to `NONE`. Otherwise, there is no way to implement custom hostname verification for Vert.x HTTP client. + * ==== + * * @asciidoclet * @since 2.5.0 */ @@ -600,7 +613,7 @@ public static HTTPConduitImpl findDefaultHTTPConduitImpl() { if (QuarkusHTTPConduitFactory.defaultHTTPConduitImpl == null) { final String defaultName = System.getenv(QuarkusHTTPConduitFactory.QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY); QuarkusHTTPConduitFactory.defaultHTTPConduitImpl = defaultName == null || defaultName.isEmpty() - ? URLConnectionHTTPConduitFactory + ? VertxHttpClientHTTPConduitFactory : valueOf(defaultName); } return QuarkusHTTPConduitFactory.defaultHTTPConduitImpl; diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfTlsConfiguration.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfTlsConfiguration.java index a83c41f6b..5a1a4dda3 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfTlsConfiguration.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfTlsConfiguration.java @@ -5,18 +5,30 @@ import javax.net.ssl.SSLContext; +import org.apache.cxf.configuration.jsse.TLSClientParameters; + +import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit; import io.quarkus.tls.TlsConfiguration; import io.vertx.core.net.KeyCertOptions; import io.vertx.core.net.SSLOptions; import io.vertx.core.net.TrustOptions; +/** + * A {@link TlsConfiguration} implementation used in case the user uses the legacy way of implementing TLS + * via {@code .trust-store*} and {@code .key-store*} family of options rather than via {@code .tls-configuration-name}. + * {@link CxfTlsConfiguration} is only used for configuring {@link VertxHttpClientHTTPConduit}. + * Other kinds of conduits are configured only via {@link TLSClientParameters}. + */ public class CxfTlsConfiguration implements TlsConfiguration { private final TrustOptions trustOptions; private final KeyStore trustStore; private final KeyCertOptions keyStoreOptions; private final KeyStore keyStore; - public CxfTlsConfiguration(KeyCertOptions keyStoreOptions, KeyStore keyStore, TrustOptions trustOptions, + public CxfTlsConfiguration( + KeyCertOptions keyStoreOptions, + KeyStore keyStore, + TrustOptions trustOptions, KeyStore trustStore) { this.keyStoreOptions = keyStoreOptions; this.keyStore = keyStore; @@ -50,23 +62,44 @@ public SSLContext createSSLContext() throws Exception { } @Override - public SSLOptions getSSLOptions() { - throw new UnsupportedOperationException(getClass().getName() + ".getSSLOptions() is not supported"); + public synchronized SSLOptions getSSLOptions() { + SSLOptions options = new SSLOptions(); + options.setKeyCertOptions(getKeyStoreOptions()); + options.setTrustOptions(getTrustStoreOptions()); + // options.setUseAlpn(config().alpn()); + // options.setSslHandshakeTimeoutUnit(TimeUnit.SECONDS); + // options.setSslHandshakeTimeout(config().handshakeTimeout().toSeconds()); + // options.setEnabledSecureTransportProtocols(config().protocols()); + // + // for (Buffer buffer : crls) { + // options.addCrlValue(buffer); + // } + // + // for (String cipher : config().cipherSuites().orElse(Collections.emptyList())) { + // options.addEnabledCipherSuite(cipher); + // } + + return options; } @Override public boolean isTrustAll() { - throw new UnsupportedOperationException(getClass().getName() + ".isTrustAll() is not supported"); + return false; } @Override public Optional getHostnameVerificationAlgorithm() { - throw new UnsupportedOperationException(getClass().getName() + ".getHostnameVerificationAlgorithm() is not supported"); + /* + * If the user does not use .tls-configuration-name (which is the case when CxfTlsConfiguration is used) + * then there is no way for him to set the hostnameVerificationAlgorithm. + */ + return Optional.empty(); } @Override public boolean usesSni() { - throw new UnsupportedOperationException(getClass().getName() + ".usesSni() is not supported"); + /* There is no way to configure SNI via TLSClientParameters */ + return false; } @Override diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java index 9f2e0c788..5cfa5ec73 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java @@ -8,7 +8,6 @@ import javax.net.ssl.TrustManagerFactory; import org.apache.cxf.Bus; -import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.configuration.security.AuthorizationPolicy; import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy; import org.apache.cxf.service.model.EndpointInfo; @@ -30,6 +29,7 @@ import io.quarkus.tls.runtime.config.TlsBucketConfig; import io.vertx.core.Vertx; import io.vertx.core.net.KeyCertOptions; +import io.vertx.core.net.KeyStoreOptionsBase; /** * A HTTPConduitFactory with some client specific configuration, such as timeouts and SSL. @@ -145,9 +145,15 @@ private HTTPConduit configure(HTTPConduit httpConduit, CXFClientInfo cxfClientIn final String hostnameVerifierName = cxfClientInfo.getHostnameVerifier(); final TlsConfiguration tlsConfig = cxfClientInfo.getTlsConfiguration(); if (hostnameVerifierName != null || tlsConfig != null) { - TLSClientParameters tlsCP = new TLSClientParameters(); + QuarkusTLSClientParameters tlsCP = new QuarkusTLSClientParameters(tlsConfig); if (hostnameVerifierName != null) { + if (httpConduit instanceof VertxHttpClientHTTPConduit) { + throw new IllegalStateException( + VertxHttpClientHTTPConduit.class.getName() + " does not support setting a hostname verifier." + + " AllowAllHostnameVerifier can be replaced by using a named TLS configuration" + + " with hostname-verification-algorithm set to NONE"); + } final Optional wellKnownHostNameVerifierName = WellKnownHostnameVerifier .of(hostnameVerifierName); if (wellKnownHostNameVerifierName.isPresent()) { @@ -170,6 +176,12 @@ private HTTPConduit configure(HTTPConduit httpConduit, CXFClientInfo cxfClientIn } catch (Exception e) { throw new RuntimeException("Could not set up key manager factory", e); } + if (keyStoreOptions instanceof KeyStoreOptionsBase) { + final KeyStoreOptionsBase keyStoreOptionsBase = (KeyStoreOptionsBase) keyStoreOptions; + if (keyStoreOptionsBase.getAlias() != null) { + tlsCP.setCertAlias(keyStoreOptionsBase.getAlias()); + } + } } if (tlsConfig.getTrustStoreOptions() != null) { @@ -185,6 +197,22 @@ private HTTPConduit configure(HTTPConduit httpConduit, CXFClientInfo cxfClientIn final VertxCertificateHolder vertxCertificateHOlder = (VertxCertificateHolder) tlsConfig; final TlsBucketConfig bucketConfig = vertxCertificateHOlder.config(); bucketConfig.cipherSuites().ifPresent(tlsCP::setCipherSuites); + + /* + * We are not able to transfer some TLS config options from VertxCertificateHolder + * to the legacy conduit implementations + */ + if (!(httpConduit instanceof VertxHttpClientHTTPConduit)) { + if (tlsConfig.isTrustAll()) { + throw new IllegalStateException( + httpConduit.getClass().getName() + " does not support quarkus.tls.trust-all = true."); + } + if (tlsConfig.getHostnameVerificationAlgorithm().isPresent()) { + throw new IllegalStateException(httpConduit.getClass().getName() + + " does not support quarkus.tls.hostname-verification-algorithm. Use quarkus.cxf.client.\"client-name\".hostname-verifier instead."); + } + } + } } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusTLSClientParameters.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusTLSClientParameters.java new file mode 100644 index 000000000..d3306c523 --- /dev/null +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusTLSClientParameters.java @@ -0,0 +1,33 @@ +package io.quarkiverse.cxf; + +import java.util.List; + +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.configuration.security.FiltersType; + +import io.quarkus.tls.TlsConfiguration; + +public class QuarkusTLSClientParameters extends TLSClientParameters { + + private final TlsConfiguration tlsConfiguration; + + public QuarkusTLSClientParameters(TlsConfiguration tlsConfiguration) { + super(); + this.tlsConfiguration = tlsConfiguration; + } + + public TlsConfiguration getTlsConfiguration() { + return tlsConfiguration; + } + + @Override + public List getCipherSuites() { + return super.getCipherSuites(); + } + + @Override + public FiltersType getCipherSuitesFilter() { + return super.getCipherSuitesFilter(); + } + +} diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java index 9ce6b9b3a..f750f22cf 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/HttpClientPool.java @@ -1,69 +1,19 @@ package io.quarkiverse.cxf.vertx.http.client; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.security.Provider; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; -import java.util.stream.Stream; - -import javax.net.ssl.ExtendedSSLSession; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.KeyManagerFactorySpi; -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.SNIHostName; -import javax.net.ssl.SNIServerName; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.StandardConstants; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.TrustManagerFactorySpi; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; import org.apache.cxf.common.logging.LogUtils; -import org.apache.cxf.configuration.jsse.SSLUtils; -import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.transport.http.HTTPConduit; -import org.apache.cxf.transport.https.httpclient.DefaultHostnameVerifier; -import org.apache.cxf.transport.https.httpclient.PublicSuffixMatcherLoader; -import io.netty.handler.codec.http2.Http2SecurityUtil; -import io.netty.handler.ssl.ClientAuth; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslProvider; +import io.quarkus.tls.TlsConfiguration; +import io.quarkus.tls.runtime.config.TlsConfigUtils; import io.vertx.core.Vertx; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpVersion; -import io.vertx.core.net.SSLEngineOptions; -import io.vertx.core.spi.tls.DefaultSslContextFactory; -import io.vertx.core.spi.tls.SslContextFactory; public class HttpClientPool { private final Map clients = new ConcurrentHashMap<>(); @@ -79,10 +29,7 @@ public HttpClient getClient(ClientSpec spec) { final HttpClientOptions opts = new HttpClientOptions() .setProtocolVersion(spec.getVersion()); if (spec.isSsl()) { - opts - .setSsl(spec.isSsl()) - .setTrustAll(true) // - .setSslEngineOptions(spec.createSslEngineOptions()); + TlsConfigUtils.configure(opts, spec.tlsConfiguration); } return vertx.createHttpClient(opts); }); @@ -91,96 +38,30 @@ public HttpClient getClient(ClientSpec spec) { public static class ClientSpec { protected static final Logger LOG = LogUtils.getL7dLogger(HTTPConduit.class); private final HttpVersion httpVersion; - private final KeyManager[] keyManagers; - private final TrustManager[] trustManagers; - private final Set cipherSuites; - private final HostnameVerifier hostNameVerifier; - private final boolean ssl; + private final TlsConfiguration tlsConfiguration; private final int hashCode; public ClientSpec( HttpVersion version, - TLSClientParameters params) { + TlsConfiguration tlsConfiguration) { + this.httpVersion = version; + this.tlsConfiguration = tlsConfiguration; int h = 31 + httpVersion.hashCode(); - if (params != null) { - KeyManager[] kms = params.getKeyManagers(); - if (kms == null) { - kms = SSLUtils.getDefaultKeyStoreManagers(LOG); - } - try { - kms = org.apache.cxf.transport.https.SSLUtils.configureKeyManagersWithCertAlias(params, kms); - } catch (GeneralSecurityException e) { - throw new VertxHttpException(e); - } - this.keyManagers = kms; - - TrustManager[] tms = params.getTrustManagers(); - if (tms == null) { - tms = SSLUtils.getDefaultTrustStoreManagers(LOG); - } - this.hostNameVerifier = getHostnameVerifier((TLSClientParameters) params); - this.trustManagers = Stream.of(tms) - .map(t -> new X509TrustManagerWrapper((X509ExtendedTrustManager) t, hostNameVerifier)) - .toArray(size -> new TrustManager[size]); - - String[] css; - try { - css = SSLUtils.getCiphersuitesToInclude( - params.getCipherSuites(), - params.getCipherSuitesFilter(), - SSLContext.getDefault().getDefaultSSLParameters().getCipherSuites(), - Http2SecurityUtil.CIPHERS.toArray(new String[] {}), - LOG); - } catch (NoSuchAlgorithmException e) { - throw new VertxHttpException(e); - } - this.cipherSuites = new LinkedHashSet<>(Arrays.asList(css)); - this.ssl = true; - - h = 31 * h + Arrays.hashCode(keyManagers); - h = 31 * h + Arrays.hashCode(tms); - h = 31 * h + hostNameVerifier.hashCode(); - h = 31 * h + cipherSuites.hashCode(); - h = 31 * h + (ssl ? 1231 : 1237); - } else { - this.keyManagers = null; - this.trustManagers = null; - this.hostNameVerifier = null; - this.cipherSuites = null; - this.ssl = false; + if (this.tlsConfiguration != null) { + h = 31 * h + this.tlsConfiguration.hashCode(); } this.hashCode = h; } - public HttpVersion getVersion() { - return httpVersion; - } - - public SSLEngineOptions createSslEngineOptions() { - return new CxfSSLEngineOptions(new CxfSslContextFactory(toSslContex())); + public boolean isSsl() { + return tlsConfiguration != null; } - SslContext toSslContex() { - - final SslContextFactory builder = new DefaultSslContextFactory(SslProvider.JDK, true) - .forClient(true); - - try { - return builder - // .applicationProtocols(Arrays.asList(params.getApplicationProtocols())) - .enabledCipherSuites(cipherSuites) - // .serverName(null) - .useAlpn(false) - .trustManagerFactory(new CxfTrustManagerFactory(trustManagers)) - .keyMananagerFactory(new CxfKeyManagerFactory(keyManagers)) - .clientAuth(ClientAuth.REQUIRE) // TODO do we need to make this configurable? - .create(); - } catch (SSLException e) { - throw new VertxHttpException(e); - } + public HttpVersion getVersion() { + return httpVersion; } @Override @@ -193,9 +74,7 @@ public boolean equals(Object obj) { return false; ClientSpec other = (ClientSpec) obj; return httpVersion == other.httpVersion - && Objects.equals(cipherSuites, other.cipherSuites) - && Arrays.equals(keyManagers, other.keyManagers) - && Arrays.equals(trustManagers, other.trustManagers); + && Objects.equals(tlsConfiguration, other.tlsConfiguration); } @Override @@ -203,480 +82,6 @@ public int hashCode() { return hashCode; } - public boolean isSsl() { - return ssl; - } - - static HostnameVerifier getHostnameVerifier(TLSClientParameters tlsClientParameters) { - if (tlsClientParameters.getHostnameVerifier() != null) { - return tlsClientParameters.getHostnameVerifier(); - } else if (tlsClientParameters.isUseHttpsURLConnectionDefaultHostnameVerifier()) { - return HttpsURLConnection.getDefaultHostnameVerifier(); - } else if (tlsClientParameters.isDisableCNCheck()) { - return ALLOW_ALL_HOSTNAME_VERIFIER; - } else { - return DEFAULT_HOSTNAME_VERIFIER; - } - } - - private static final HostnameVerifier DEFAULT_HOSTNAME_VERIFIER = new DefaultHostnameVerifier( - PublicSuffixMatcherLoader.getDefault()); - private static final HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER; - static { - final TLSClientParameters params = new TLSClientParameters(); - params.setDisableCNCheck(true); - ALLOW_ALL_HOSTNAME_VERIFIER = org.apache.cxf.transport.https.SSLUtils.getHostnameVerifier(params); - } - - } - - public record CxfSslContextFactory(SslContext create) implements SslContextFactory { - } - - public static class CxfSSLEngineOptions extends SSLEngineOptions { - - private final SslContextFactory sslContextFactory; - - public CxfSSLEngineOptions(SslContextFactory sslContextFactory) { - super(); - this.sslContextFactory = sslContextFactory; - } - - @Override - public SSLEngineOptions copy() { - return new CxfSSLEngineOptions(sslContextFactory); - } - - @Override - public SslContextFactory sslContextFactory() { - return sslContextFactory; - } - - } - - private static final Provider PROVIDER = new Provider("", "0.0", "") { - }; - - static class CxfTrustManagerFactory extends TrustManagerFactory { - - CxfTrustManagerFactory(TrustManager... tm) { - super(new TrustManagerFactorySpi() { - @Override - protected void engineInit(KeyStore keyStore) throws KeyStoreException { - } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { - } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return tm; - } - }, PROVIDER, ""); - } - } - - /* - * The classes below are used by the HttpClient implementation to allow use of the - * HostNameVerifier that is configured. HttpClient does not provide a hook or - * anything to call into the HostNameVerifier after the certs are verified. It - * prefers that the Hostname is verified at the same time as the certificates - * but the only option for hostname is the global on/off system property. Thus, - * we have to provide a X509TrustManagerWrapper that would turn off the - * EndpointIdentificationAlgorithm and then handle the hostname verification - * directly. However, since the peer certs are not yet verified, we also need to wrapper - * the session so the HostnameVerifier things they are. - */ - static class X509TrustManagerWrapper extends X509ExtendedTrustManager { - - private final X509TrustManager delegate; - private final X509ExtendedTrustManager extendedDelegate; - private final HostnameVerifier verifier; - - X509TrustManagerWrapper(X509TrustManager delegate, HostnameVerifier hnv) { - this.delegate = delegate; - this.verifier = hnv; - this.extendedDelegate = delegate instanceof X509ExtendedTrustManager - ? (X509ExtendedTrustManager) delegate - : null; - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException { - delegate.checkClientTrusted(chain, s); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s, Socket socket) - throws CertificateException { - if (extendedDelegate != null) { - extendedDelegate.checkClientTrusted(chain, s, socket); - } else { - delegate.checkClientTrusted(chain, s); - } - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s, SSLEngine sslEngine) - throws CertificateException { - if (extendedDelegate != null) { - extendedDelegate.checkClientTrusted(chain, s, sslEngine); - } else { - delegate.checkClientTrusted(chain, s); - } - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException { - System.out.println("cst1: " + s); - delegate.checkServerTrusted(chain, s); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s, Socket socket) - throws CertificateException { - System.out.println("cst2: " + s); - if (extendedDelegate != null) { - extendedDelegate.checkServerTrusted(chain, s, socket); - } else { - delegate.checkServerTrusted(chain, s); - } - } - - private String getHostName(List names) { - if (names == null) { - return null; - } - for (SNIServerName n : names) { - if (n.getType() != StandardConstants.SNI_HOST_NAME) { - continue; - } - if (n instanceof SNIHostName) { - SNIHostName hostname = (SNIHostName) n; - return hostname.getAsciiName(); - } - } - return null; - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s, SSLEngine engine) - throws CertificateException { - if (extendedDelegate != null) { - extendedDelegate.checkServerTrusted(chain, s, new SSLEngineWrapper(engine)); - //certificates are valid, now check hostnames - SSLSession session = engine.getHandshakeSession(); - List names = null; - if (session instanceof ExtendedSSLSession) { - ExtendedSSLSession extSession = (ExtendedSSLSession) session; - names = extSession.getRequestedServerNames(); - } - - boolean identifiable = false; - String peerHost = session.getPeerHost(); - String hostname = getHostName(names); - session = new SSLSessionWrapper(session, chain); - if (hostname != null && verifier.verify(hostname, session)) { - identifiable = true; - } - if (!identifiable && !verifier.verify(peerHost, session)) { - throw new CertificateException( - "The https URL hostname " + peerHost + " does not match the " - + "Common Name (CN) on the server certificate in the client's truststore. " - + "Make sure server certificate is correct, or to disable this check " - + "(NOT recommended for production) set the CXF client TLS " - + "configuration property \"disableCNCheck\" to true."); - } - } else { - delegate.checkServerTrusted(chain, s); - } - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return delegate.getAcceptedIssuers(); - } - } - - static class SSLEngineWrapper extends SSLEngine { - final SSLEngine delegate; - - SSLEngineWrapper(SSLEngine delegate) { - this.delegate = delegate; - } - - @Override - public SSLParameters getSSLParameters() { - //make sure the hostname verification is not done in the default X509 stuff - //so we can do it later - SSLParameters params = delegate.getSSLParameters(); - params.setEndpointIdentificationAlgorithm(null); - return params; - } - - @Override - public SSLSession getHandshakeSession() { - return delegate.getHandshakeSession(); - } - - @Override - public void beginHandshake() throws SSLException { - delegate.beginHandshake(); - } - - @Override - public void closeInbound() throws SSLException { - delegate.closeInbound(); - } - - @Override - public void closeOutbound() { - delegate.closeOutbound(); - } - - @Override - public Runnable getDelegatedTask() { - return delegate.getDelegatedTask(); - } - - @Override - public boolean getEnableSessionCreation() { - return delegate.getEnableSessionCreation(); - } - - @Override - public String[] getEnabledCipherSuites() { - return delegate.getEnabledCipherSuites(); - } - - @Override - public String[] getEnabledProtocols() { - return delegate.getEnabledProtocols(); - } - - @Override - public HandshakeStatus getHandshakeStatus() { - return delegate.getHandshakeStatus(); - } - - @Override - public boolean getNeedClientAuth() { - return delegate.getNeedClientAuth(); - } - - @Override - public SSLSession getSession() { - return delegate.getSession(); - } - - @Override - public String[] getSupportedCipherSuites() { - return delegate.getSupportedCipherSuites(); - } - - @Override - public String[] getSupportedProtocols() { - return delegate.getSupportedProtocols(); - } - - @Override - public boolean getUseClientMode() { - return delegate.getUseClientMode(); - } - - @Override - public boolean getWantClientAuth() { - return delegate.getWantClientAuth(); - } - - @Override - public boolean isInboundDone() { - return delegate.isInboundDone(); - } - - @Override - public boolean isOutboundDone() { - return delegate.isInboundDone(); - } - - @Override - public void setEnableSessionCreation(boolean arg0) { - delegate.setEnableSessionCreation(arg0); - } - - @Override - public void setEnabledCipherSuites(String[] arg0) { - delegate.setEnabledCipherSuites(arg0); - } - - @Override - public void setEnabledProtocols(String[] arg0) { - delegate.setEnabledProtocols(arg0); - } - - @Override - public void setNeedClientAuth(boolean arg0) { - delegate.setNeedClientAuth(arg0); - } - - @Override - public void setUseClientMode(boolean arg0) { - delegate.setUseClientMode(arg0); - } - - @Override - public void setWantClientAuth(boolean arg0) { - delegate.setWantClientAuth(arg0); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer arg0, ByteBuffer[] arg1, int arg2, int arg3) - throws SSLException { - return null; - } - - @Override - public SSLEngineResult wrap(ByteBuffer[] arg0, int arg1, int arg2, ByteBuffer arg3) - throws SSLException { - return null; - } - } - static class SSLSessionWrapper implements SSLSession { - SSLSession session; - Certificate[] certificates; - - SSLSessionWrapper(SSLSession s, Certificate[] certs) { - this.certificates = certs; - this.session = s; - } - - @Override - public byte[] getId() { - return session.getId(); - } - - @Override - public SSLSessionContext getSessionContext() { - return session.getSessionContext(); - } - - @Override - public long getCreationTime() { - return session.getCreationTime(); - } - - @Override - public long getLastAccessedTime() { - return session.getLastAccessedTime(); - } - - @Override - public void invalidate() { - session.invalidate(); - } - - @Override - public boolean isValid() { - return session.isValid(); - } - - @Override - public void putValue(String s, Object o) { - session.putValue(s, o); - } - - @Override - public Object getValue(String s) { - return session.getValue(s); - } - - @Override - public void removeValue(String s) { - session.removeValue(s); - } - - @Override - public String[] getValueNames() { - return session.getValueNames(); - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - return certificates; - } - - @Override - public Certificate[] getLocalCertificates() { - return session.getLocalCertificates(); - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - return session.getPeerPrincipal(); - } - - @Override - public Principal getLocalPrincipal() { - return session.getLocalPrincipal(); - } - - @Override - public String getCipherSuite() { - return session.getCipherSuite(); - } - - @Override - public String getProtocol() { - return session.getProtocol(); - } - - @Override - public String getPeerHost() { - return session.getPeerHost(); - } - - @Override - public int getPeerPort() { - return session.getPeerPort(); - } - - @Override - public int getPacketBufferSize() { - return session.getPacketBufferSize(); - } - - @Override - public int getApplicationBufferSize() { - return session.getApplicationBufferSize(); - } - - @SuppressWarnings("removal") - @Override - public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { - return session.getPeerCertificateChain(); - } - } - - static class CxfKeyManagerFactory extends KeyManagerFactory { - - CxfKeyManagerFactory(KeyManager... km) { - super(new KeyManagerFactorySpi() { - @Override - protected void engineInit(KeyStore keyStore, char[] pwd) throws KeyStoreException { - } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { - } - - @Override - protected KeyManager[] engineGetKeyManagers() { - return km; - } - }, PROVIDER, ""); - } - } } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java index 5b19e0a47..7a566a833 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java @@ -66,6 +66,7 @@ import org.apache.cxf.version.Version; import org.apache.cxf.ws.addressing.EndpointReferenceType; +import io.quarkiverse.cxf.QuarkusTLSClientParameters; import io.quarkiverse.cxf.vertx.http.client.HttpClientPool.ClientSpec; import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit.RequestBodyEvent.RequestBodyEventType; import io.vertx.core.AsyncResult; @@ -112,7 +113,7 @@ protected void setupConnection(Message message, Address address, HTTPClientPolic final HttpVersion version = getVersion(message, csPolicy); final boolean isHttps = "https".equals(uri.getScheme()); - final TLSClientParameters clientParameters; + final QuarkusTLSClientParameters clientParameters; if (isHttps) { clientParameters = findTLSClientParameters(message); if (clientParameters.getSSLSocketFactory() != null) { @@ -173,7 +174,7 @@ protected void setupConnection(Message message, Address address, HTTPClientPolic final RequestContext requestContext = new RequestContext( uri, requestOptions, - new ClientSpec(version, clientParameters), + new ClientSpec(version, clientParameters != null ? clientParameters.getTlsConfiguration() : null), determineReceiveTimeout(message, csPolicy)); message.put(RequestContext.class, requestContext); @@ -252,15 +253,36 @@ static HttpMethod getMethod(Message message) { return method; } - TLSClientParameters findTLSClientParameters(Message message) { + QuarkusTLSClientParameters findTLSClientParameters(Message message) { TLSClientParameters clientParameters = message.get(TLSClientParameters.class); if (clientParameters == null) { clientParameters = this.tlsClientParameters; } if (clientParameters == null) { - clientParameters = new TLSClientParameters(); + clientParameters = new QuarkusTLSClientParameters(null); } - return clientParameters; + + if (clientParameters.getHostnameVerifier() != null) { + throw new IllegalStateException( + VertxHttpClientHTTPConduit.class.getName() + " does not support setting a hostname verifier." + + " AllowAllHostnameVerifier can be replaced by using a named TLS configuration" + + " with hostname-verification-algorithm set to NONE"); + } + + if (clientParameters instanceof QuarkusTLSClientParameters) { + return (QuarkusTLSClientParameters) clientParameters; + } + throw new IllegalStateException( + VertxHttpClientHTTPConduit.class.getName() + " accepts only " + QuarkusTLSClientParameters.class.getName()); + } + + @Override + public void setTlsClientParameters(TLSClientParameters params) { + if (params != null && !(params instanceof QuarkusTLSClientParameters)) { + throw new IllegalStateException( + VertxHttpClientHTTPConduit.class.getName() + " accepts only " + QuarkusTLSClientParameters.class.getName()); + } + super.setTlsClientParameters(params); } static record RequestContext( diff --git a/integration-tests/ws-security-policy/src/main/java/io/quarkiverse/cxf/it/security/policy/SecurityPolicyResource.java b/integration-tests/ws-security-policy/src/main/java/io/quarkiverse/cxf/it/security/policy/SecurityPolicyResource.java index f0e66690c..b3e0d5c93 100644 --- a/integration-tests/ws-security-policy/src/main/java/io/quarkiverse/cxf/it/security/policy/SecurityPolicyResource.java +++ b/integration-tests/ws-security-policy/src/main/java/io/quarkiverse/cxf/it/security/policy/SecurityPolicyResource.java @@ -30,6 +30,9 @@ public class SecurityPolicyResource { @CXFClient("helloAllowAll") HelloService helloAllowAll; + @CXFClient("helloAllowAllTlsConfig") + HelloService helloAllowAllTlsConfig; + @CXFClient("helloIp") HelloService helloIp; @@ -120,6 +123,9 @@ public Response hello(@PathParam("client") String client, String body) { case "helloAllowAll": service = helloAllowAll; break; + case "helloAllowAllTlsConfig": + service = helloAllowAllTlsConfig; + break; case "helloCustomHostnameVerifier": service = helloCustomHostnameVerifier; break; diff --git a/integration-tests/ws-security-policy/src/main/resources/application.properties b/integration-tests/ws-security-policy/src/main/resources/application.properties index d0c2f61bf..f96beaa83 100644 --- a/integration-tests/ws-security-policy/src/main/resources/application.properties +++ b/integration-tests/ws-security-policy/src/main/resources/application.properties @@ -154,6 +154,14 @@ quarkus.cxf.client.helloAllowAll.trust-store = client-truststore.${keystore.type quarkus.cxf.client.helloAllowAll.trust-store-password = client-truststore-password quarkus.cxf.client.helloAllowAll.hostname-verifier = AllowAllHostnameVerifier + +quarkus.tls.helloAllowAll.trust-store.${keystore.type.short}.path = client-truststore.${keystore.type} +quarkus.tls.helloAllowAll.trust-store.${keystore.type.short}.password = client-truststore-password +quarkus.tls.helloAllowAll.hostname-verification-algorithm = NONE +quarkus.cxf.client.helloAllowAllTlsConfig.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello +quarkus.cxf.client.helloAllowAllTlsConfig.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService +quarkus.cxf.client.helloAllowAllTlsConfig.tls-configuration-name = helloAllowAll + quarkus.cxf.client.helloCustomHostnameVerifier.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.helloCustomHostnameVerifier.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.helloCustomHostnameVerifier.trust-store = client-truststore.${keystore.type} diff --git a/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java b/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java index 9fde08985..dca17d95c 100644 --- a/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java +++ b/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java @@ -33,32 +33,94 @@ void hello() { @Test void helloAllowAll() { - /* - * client calling a service having no policy via https://127.0.0.1 - * Should pass thanks to hostname-verifier = AllowAllHostnameVerifier - */ - RestAssured.given() - .config(PolicyTestUtils.restAssuredConfig()) - .body("Frank") - .post("/cxf/security-policy/helloAllowAll") - .then() - .statusCode(200) - .body(is("Hello Frank!")); + HTTPConduitImpl defaultImpl = HTTPConduitImpl.findDefaultHTTPConduitImpl(); + switch (defaultImpl) { + case VertxHttpClientHTTPConduitFactory: + /* + * client calling a service having no policy via https://127.0.0.1 + * hostname-verifier is not supported by VertxHttpClientHTTPConduitFactory + */ + RestAssured.given() + .config(PolicyTestUtils.restAssuredConfig()) + .body("Frank") + .post("/cxf/security-policy/helloAllowAll") + .then() + .statusCode(500) + .body(Matchers.containsString( + "io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit does not support setting a hostname verifier.")); + /* + * But the same works when the hostname verification is disabled via + * quarkus.tls.helloAllowAll.trust-store.hostname-verification-algorithm = NONE + */ + RestAssured.given() + .config(PolicyTestUtils.restAssuredConfig()) + .body("Frank") + .post("/cxf/security-policy/helloAllowAllTlsConfig") + .then() + .statusCode(200) + .body(is("Hello Frank!")); + break; + case URLConnectionHTTPConduitFactory: + /* + * client calling a service having no policy via https://127.0.0.1 + * Should pass thanks to hostname-verifier = AllowAllHostnameVerifier + */ + RestAssured.given() + .config(PolicyTestUtils.restAssuredConfig()) + .body("Frank") + .post("/cxf/security-policy/helloAllowAll") + .then() + .statusCode(200) + .body(is("Hello Frank!")); + + RestAssured.given() + .config(PolicyTestUtils.restAssuredConfig()) + .body("Frank") + .post("/cxf/security-policy/helloAllowAllTlsConfig") + .then() + .statusCode(500) + .body(Matchers.containsString( + "org.apache.cxf.transport.http.URLConnectionHTTPConduit does not support quarkus.tls.hostname-verification-algorithm. Use quarkus.cxf.client.\"client-name\".hostname-verifier instead.")); + break; + default: + throw new IllegalArgumentException("Unexpected value: " + defaultImpl); + } } @Test void helloCustomHostnameVerifier() { - /* - * client calling a service having no policy via https://127.0.0.1 - * Should pass thanks to hostname-verifier = io.quarkiverse.cxf.it.security.policy.NoopHostnameVerifier - */ - RestAssured.given() - .config(PolicyTestUtils.restAssuredConfig()) - .body("Frank") - .post("/cxf/security-policy/helloCustomHostnameVerifier") - .then() - .statusCode(200) - .body(is("Hello Frank!")); + HTTPConduitImpl defaultImpl = HTTPConduitImpl.findDefaultHTTPConduitImpl(); + switch (defaultImpl) { + case VertxHttpClientHTTPConduitFactory: + /* + * client calling a service having no policy via https://127.0.0.1 + * hostname-verifier is not supported by VertxHttpClientHTTPConduitFactory + */ + RestAssured.given() + .config(PolicyTestUtils.restAssuredConfig()) + .body("Frank") + .post("/cxf/security-policy/helloCustomHostnameVerifier") + .then() + .statusCode(500) + .body(Matchers.containsString( + "io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit does not support setting a hostname verifier.")); + break; + case URLConnectionHTTPConduitFactory: + /* + * client calling a service having no policy via https://127.0.0.1 + * Should pass thanks to hostname-verifier = io.quarkiverse.cxf.it.security.policy.NoopHostnameVerifier + */ + RestAssured.given() + .config(PolicyTestUtils.restAssuredConfig()) + .body("Frank") + .post("/cxf/security-policy/helloCustomHostnameVerifier") + .then() + .statusCode(200) + .body(is("Hello Frank!")); + break; + default: + throw new IllegalArgumentException("Unexpected value: " + defaultImpl); + } } @Test @@ -164,7 +226,7 @@ void helloIp() { HTTPConduitImpl defaultImpl = HTTPConduitImpl.findDefaultHTTPConduitImpl(); switch (defaultImpl) { case VertxHttpClientHTTPConduitFactory: - expectedMessage = "The https URL hostname 127.0.0.1 does not match the Common Name (CN) on the server certificate in the client's truststore"; + expectedMessage = "No subject alternative names present"; break; case URLConnectionHTTPConduitFactory: expectedMessage = "The https URL hostname does not match the Common Name (CN) on the server certificate in the client's truststore"; From bce9c505ec373e4d4000262ee7524cfe21731be9 Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Sun, 13 Oct 2024 18:47:27 +0200 Subject: [PATCH 3/3] Refactor! --- .../cxf/deployment/CxfClientProcessor.java | 2 +- .../test/ClientConduitFactoryTest.java | 14 +- .../test/ClientHttpPolicyDefaultsTest.java | 11 +- .../test/ClientReceiveTimeoutTest.java | 2 +- .../test/GlobalConduitFactoryTest.java | 4 +- .../deployment/test/HostnameVerifierTest.java | 2 +- .../deployment/test/TlsConfigurationTest.java | 16 +- .../VertxWebClientConduitFactoryTest.java | 11 +- .../io/quarkiverse/cxf/CXFClientInfo.java | 1 - .../java/io/quarkiverse/cxf/CXFRecorder.java | 4 +- .../io/quarkiverse/cxf/CxfClientConfig.java | 55 ------ .../io/quarkiverse/cxf/CxfClientProducer.java | 6 +- .../io/quarkiverse/cxf/CxfFixedConfig.java | 1 - .../io/quarkiverse/cxf/HTTPConduitImpl.java | 111 ++++++++++++ .../io/quarkiverse/cxf/HTTPConduitSpec.java | 111 ++++++++++++ .../cxf/QuarkusHTTPConduitFactory.java | 170 ++---------------- .../client/VertxHttpClientHTTPConduit.java | 2 +- .../VertxHttpClientHTTPConduitFactory.java | 51 ------ .../hc5/deployment/Hc5ConduitFactoryTest.java | 10 +- .../cxf/transport/http/hc5/Hc5Recorder.java | 28 +++ .../opentelemetry/it/OpenTelemetryTest.java | 2 +- .../security/policy/TransportPolicyTest.java | 8 +- 22 files changed, 309 insertions(+), 313 deletions(-) create mode 100644 extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java create mode 100644 extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java delete mode 100644 extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java diff --git a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java index e571fdb1b..511c14f2e 100644 --- a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java +++ b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java @@ -37,10 +37,10 @@ import io.quarkiverse.cxf.CXFClientInfo; import io.quarkiverse.cxf.CXFRecorder; import io.quarkiverse.cxf.ClientInjectionPoint; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; import io.quarkiverse.cxf.CxfClientProducer; import io.quarkiverse.cxf.CxfFixedConfig; import io.quarkiverse.cxf.CxfFixedConfig.ClientFixedConfig; +import io.quarkiverse.cxf.HTTPConduitImpl; import io.quarkiverse.cxf.HttpClientHTTPConduitFactory; import io.quarkiverse.cxf.annotation.CXFClient; import io.quarkiverse.cxf.graal.QuarkusCxfFeature; diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientConduitFactoryTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientConduitFactoryTest.java index e9d22f98c..f7ceff1f0 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientConduitFactoryTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientConduitFactoryTest.java @@ -8,7 +8,6 @@ import org.apache.cxf.BusFactory; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; -import org.apache.cxf.transport.http.HTTPConduitFactory; import org.apache.cxf.transport.http.HttpClientHTTPConduit; import org.apache.cxf.transport.http.URLConnectionHTTPConduit; import org.assertj.core.api.Assertions; @@ -18,12 +17,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; -import io.quarkiverse.cxf.URLConnectionHTTPConduitFactory; +import io.quarkiverse.cxf.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitSpec; import io.quarkiverse.cxf.annotation.CXFClient; -import io.quarkiverse.cxf.deployment.test.GlobalConduitFactoryTest.HelloService; import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit; -import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduitFactory; import io.quarkus.test.QuarkusUnitTest; public class ClientConduitFactoryTest { @@ -73,11 +70,10 @@ public class ClientConduitFactoryTest { @Test void conduitFactory() { final Bus bus = BusFactory.getDefaultBus(); - final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class); - final HTTPConduitImpl impl = HTTPConduitImpl.findDefaultHTTPConduitImpl(); - Assertions.assertThat(factory).isInstanceOf(impl.newHTTPConduitFactory().getClass()); + final HTTPConduitSpec registeredImpl = bus.getExtension(HTTPConduitSpec.class); + HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.HTTPConduitImpl.findDefaultHTTPConduitImpl(); + Assertions.assertThat(registeredImpl.resolveDefault()).isEqualTo(defaultImpl); - HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl.findDefaultHTTPConduitImpl(); { final HelloService service = helloDefault; final Client client = ClientProxy.getClient(service); diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientHttpPolicyDefaultsTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientHttpPolicyDefaultsTest.java index 0cc76cc9f..da2c3db75 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientHttpPolicyDefaultsTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientHttpPolicyDefaultsTest.java @@ -14,7 +14,6 @@ import org.apache.cxf.BusFactory; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; -import org.apache.cxf.transport.http.HTTPConduitFactory; import org.apache.cxf.transport.http.URLConnectionHTTPConduit; import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; import org.assertj.core.api.Assertions; @@ -27,10 +26,10 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkiverse.cxf.CxfClientConfig; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitSpec; import io.quarkiverse.cxf.annotation.CXFClient; import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit; -import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduitFactory; import io.quarkus.test.QuarkusUnitTest; public class ClientHttpPolicyDefaultsTest { @@ -85,9 +84,9 @@ public void defaults() throws IllegalArgumentException, IllegalAccessException, @Test void defaultConduitFactory() { final Bus bus = BusFactory.getDefaultBus(); - final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class); - HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl.findDefaultHTTPConduitImpl(); - Assertions.assertThat(factory).isInstanceOf(defaultImpl.newHTTPConduitFactory().getClass()); + final HTTPConduitSpec registeredImpl = bus.getExtension(HTTPConduitSpec.class); + HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.HTTPConduitImpl.findDefaultHTTPConduitImpl(); + Assertions.assertThat(registeredImpl.resolveDefault()).isEqualTo(defaultImpl); final Client client = ClientProxy.getClient(helloService); switch (defaultImpl) { diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientReceiveTimeoutTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientReceiveTimeoutTest.java index 99e9fa5d6..33ebcde6e 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientReceiveTimeoutTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/ClientReceiveTimeoutTest.java @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitImpl; import io.quarkiverse.cxf.HttpClientHTTPConduitFactory; import io.quarkiverse.cxf.URLConnectionHTTPConduitFactory; import io.quarkiverse.cxf.annotation.CXFClient; diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/GlobalConduitFactoryTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/GlobalConduitFactoryTest.java index 64fd958d9..50bcf8b96 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/GlobalConduitFactoryTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/GlobalConduitFactoryTest.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitImpl; import io.quarkiverse.cxf.HttpClientHTTPConduitFactory; import io.quarkiverse.cxf.URLConnectionHTTPConduitFactory; import io.quarkiverse.cxf.annotation.CXFClient; @@ -66,7 +66,7 @@ public class GlobalConduitFactoryTest { void conduitFactory() { final Bus bus = BusFactory.getDefaultBus(); final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class); - Assertions.assertThat(factory).isInstanceOf(URLConnectionHTTPConduitFactory.class); + Assertions.assertThat(factory).isNull(); { final Client client = ClientProxy.getClient(helloGlobal); diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java index 1b5d5a139..1dca7d64d 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/HostnameVerifierTest.java @@ -116,7 +116,7 @@ void customHostnameVerifierVertx() { Assertions.assertThat(customHostnameVerifier.getCheckedHostNames()).isEmpty(); customHostnameVerifier.setReturnVal(false); Assertions.assertThatThrownBy(() -> helloVertx.hello("Doe")).hasRootCauseMessage( - "io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit does not support setting a hostname verifier. AllowAllHostnameVerifier can be replaced by using a named TLS configuration with hostname-verification-algorithm set to NONE"); + "http-conduit-factory = VertxHttpClientHTTPConduitFactory does not support setting a hostname verifier. AllowAllHostnameVerifier can be replaced by using a named TLS configuration with hostname-verification-algorithm set to NONE"); Assertions.assertThat(customHostnameVerifier.getCheckedHostNames()).isEmpty(); } diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java index 9cdae8576..1b12fc363 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/TlsConfigurationTest.java @@ -55,6 +55,12 @@ public class TlsConfigurationTest { .overrideConfigKey("quarkus.cxf.client.helloVertx.http-conduit-factory", "VertxHttpClientHTTPConduitFactory") .overrideConfigKey("quarkus.cxf.client.helloVertx.tls-configuration-name", "client-pkcs12") + .overrideConfigKey("quarkus.cxf.client.helloVertx2.client-endpoint-url", "https://localhost:8444/services/hello") + .overrideConfigKey("quarkus.cxf.client.helloVertx2.logging.enabled", "true") + .overrideConfigKey("quarkus.cxf.client.helloVertx2.service-interface", HelloService.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloVertx2.http-conduit-factory", "VertxHttpClientHTTPConduitFactory") + .overrideConfigKey("quarkus.cxf.client.helloVertx2.tls-configuration-name", "client-pkcs12") + /* Client with HttpClientHTTPConduitFactory */ .overrideConfigKey("quarkus.cxf.client.helloHttpClient.client-endpoint-url", "https://localhost:8444/services/hello") @@ -74,6 +80,9 @@ public class TlsConfigurationTest { @CXFClient("helloVertx") HelloService helloVertx; + @CXFClient("helloVertx2") + HelloService helloVertx2; + @CXFClient("helloHttpClient") HelloService helloHttpClient; @@ -101,9 +110,12 @@ void urlConnection() { @Test void sameTlsConfiguration() { - /* The TlsConfigurations must be the same instance, otherwise the identity based caching in HttpClientPool would not work */ + /* + * The TlsConfigurations must be the same instance, otherwise the identity based caching in HttpClientPool would not + * work + */ - TLSClientParameters p1 = getTLSClientParameters(helloUrlConnection); + TLSClientParameters p1 = getTLSClientParameters(helloVertx2); TLSClientParameters p2 = getTLSClientParameters(helloVertx); Assertions.assertThat(p1).isNotNull().isInstanceOf(QuarkusTLSClientParameters.class); diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/vertx/http/client/deployment/VertxWebClientConduitFactoryTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/vertx/http/client/deployment/VertxWebClientConduitFactoryTest.java index 70f47ffe4..1d7d10e45 100644 --- a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/vertx/http/client/deployment/VertxWebClientConduitFactoryTest.java +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/vertx/http/client/deployment/VertxWebClientConduitFactoryTest.java @@ -8,7 +8,6 @@ import org.apache.cxf.BusFactory; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; -import org.apache.cxf.transport.http.HTTPConduitFactory; import org.apache.cxf.transport.http.URLConnectionHTTPConduit; import org.assertj.core.api.Assertions; import org.jboss.logging.Logger; @@ -17,10 +16,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitSpec; import io.quarkiverse.cxf.annotation.CXFClient; import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit; -import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduitFactory; import io.quarkus.test.QuarkusUnitTest; public class VertxWebClientConduitFactoryTest { @@ -45,9 +44,9 @@ public class VertxWebClientConduitFactoryTest { @Test void conduitFactory() { final Bus bus = BusFactory.getDefaultBus(); - final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class); - HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl.findDefaultHTTPConduitImpl(); - Assertions.assertThat(factory).isInstanceOf(defaultImpl.newHTTPConduitFactory().getClass()); + final HTTPConduitSpec registeredImpl = bus.getExtension(HTTPConduitSpec.class); + HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.HTTPConduitImpl.findDefaultHTTPConduitImpl(); + Assertions.assertThat(registeredImpl.resolveDefault()).isEqualTo(defaultImpl); final Client client = ClientProxy.getClient(helloService); switch (defaultImpl) { diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java index 85c61614f..6e610536b 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java @@ -12,7 +12,6 @@ import org.apache.cxf.transports.http.configuration.ConnectionType; import org.apache.cxf.transports.http.configuration.ProxyServerType; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; import io.quarkus.arc.Arc; import io.quarkus.arc.Unremovable; import io.quarkus.tls.TlsConfiguration; diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFRecorder.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFRecorder.java index 3cab16cd8..51b735688 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFRecorder.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFRecorder.java @@ -7,11 +7,9 @@ import java.util.function.Consumer; import org.apache.cxf.Bus; -import org.apache.cxf.transport.http.HTTPConduitFactory; import org.jboss.logging.Logger; import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueue.Supplier; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; import io.quarkiverse.cxf.annotation.CXFEndpoint; import io.quarkiverse.cxf.transport.CxfHandler; import io.quarkiverse.cxf.transport.VertxDestinationFactory; @@ -281,7 +279,7 @@ public static boolean isHc5Present() { } public RuntimeValue> setBusHTTPConduitFactory(HTTPConduitImpl factory) { - return new RuntimeValue<>(bus -> bus.setExtension(factory.newHTTPConduitFactory(), HTTPConduitFactory.class)); + return new RuntimeValue<>(bus -> bus.setExtension(factory, HTTPConduitSpec.class)); } } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java index 525d7dfe4..9cfacc7d7 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java @@ -5,13 +5,11 @@ import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType; import org.apache.cxf.configuration.jsse.TLSClientParameters; -import org.apache.cxf.transport.http.HTTPConduitFactory; import org.apache.cxf.transports.http.configuration.ConnectionType; import org.apache.cxf.transports.http.configuration.ProxyServerType; import io.quarkiverse.cxf.LoggingConfig.PerClientOrServiceLoggingConfig; import io.quarkus.runtime.annotations.ConfigDocEnum; -import io.quarkus.runtime.annotations.ConfigDocEnumValue; import io.quarkus.runtime.annotations.ConfigGroup; import io.smallrye.config.WithConverter; import io.smallrye.config.WithDefault; @@ -567,59 +565,6 @@ public interface CxfClientConfig { @WithName("schema-validation.enabled-for") public Optional schemaValidationEnabledFor(); - public enum HTTPConduitImpl { - - @ConfigDocEnumValue("QuarkusCXFDefault") - QuarkusCXFDefault { - @Override - public HTTPConduitFactory newHTTPConduitFactory() { - final HTTPConduitImpl impl = findDefaultHTTPConduitImpl(); - return impl.newHTTPConduitFactory(); - } - - }, - @ConfigDocEnumValue("CXFDefault") - CXFDefault { - @Override - public HTTPConduitFactory newHTTPConduitFactory() { - return null; - } - }, - @ConfigDocEnumValue("VertxHttpClientHTTPConduitFactory") - VertxHttpClientHTTPConduitFactory { - @Override - public HTTPConduitFactory newHTTPConduitFactory() { - return new io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduitFactory(); - } - }, - @ConfigDocEnumValue("HttpClientHTTPConduitFactory") - HttpClientHTTPConduitFactory { - @Override - public HTTPConduitFactory newHTTPConduitFactory() { - return new HttpClientHTTPConduitFactory(); - } - }, - @ConfigDocEnumValue("URLConnectionHTTPConduitFactory") - URLConnectionHTTPConduitFactory { - @Override - public HTTPConduitFactory newHTTPConduitFactory() { - return new URLConnectionHTTPConduitFactory(); - } - }; - - public abstract HTTPConduitFactory newHTTPConduitFactory(); - - public static HTTPConduitImpl findDefaultHTTPConduitImpl() { - if (QuarkusHTTPConduitFactory.defaultHTTPConduitImpl == null) { - final String defaultName = System.getenv(QuarkusHTTPConduitFactory.QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY); - QuarkusHTTPConduitFactory.defaultHTTPConduitImpl = defaultName == null || defaultName.isEmpty() - ? VertxHttpClientHTTPConduitFactory - : valueOf(defaultName); - } - return QuarkusHTTPConduitFactory.defaultHTTPConduitImpl; - } - } - public enum WellKnownHostnameVerifier { AllowAllHostnameVerifier { diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java index 14def6163..f4d80e01f 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java @@ -183,16 +183,16 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { customizers.forEach(customizer -> customizer.customize(cxfClientInfo, factory)); final Bus bus = BusFactory.getDefaultBus(); - final HTTPConduitFactory origConduitFactory = bus.getExtension(HTTPConduitFactory.class); + final HTTPConduitSpec origConduitImpl = bus.getExtension(HTTPConduitSpec.class); final QuarkusHTTPConduitFactory conduitFactory = new QuarkusHTTPConduitFactory( - httpClientPool, fixedConfig, cxfClientInfo, - origConduitFactory, + origConduitImpl, authorizationPolicy, vertx); props.put(HTTPConduitFactory.class.getName(), conduitFactory); Object result; + final HTTPConduitFactory origConduitFactory = bus.getExtension(HTTPConduitFactory.class); try { /* * Workaround for https://github.com/quarkiverse/quarkus-cxf/issues/1264 diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java index 8d666aafa..e49a408fb 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfFixedConfig.java @@ -3,7 +3,6 @@ import java.util.Map; import java.util.Optional; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.annotations.ConfigPhase; diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java new file mode 100644 index 000000000..c9a20e9c7 --- /dev/null +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java @@ -0,0 +1,111 @@ +package io.quarkiverse.cxf; + +import java.io.IOException; +import java.util.Objects; + +import org.apache.cxf.Bus; +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.transport.http.HTTPConduit; +import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.cxf.transport.http.HttpClientHTTPConduit; +import org.apache.cxf.transport.http.URLConnectionHTTPConduit; +import org.apache.cxf.ws.addressing.EndpointReferenceType; + +import io.quarkiverse.cxf.vertx.http.client.HttpClientPool; +import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit; +import io.quarkus.runtime.annotations.ConfigDocEnumValue; + +public enum HTTPConduitImpl implements HTTPConduitSpec { + + @ConfigDocEnumValue("QuarkusCXFDefault") + QuarkusCXFDefault { + + private HTTPConduitImpl defaultHTTPConduitImpl; + + @Override + public HTTPConduitImpl resolveDefault() { + HTTPConduitImpl result; + if ((result = defaultHTTPConduitImpl) == null) { + defaultHTTPConduitImpl = result = findDefaultHTTPConduitImpl(); + } + return result; + } + }, + @ConfigDocEnumValue("CXFDefault") + CXFDefault { + @Override + public HTTPConduitImpl resolveDefault() { + /* + * Mimic what is done in org.apache.cxf.transport.http.HTTPTransportFactory.getConduit(EndpointInfo, + * EndpointReferenceType, Bus) + */ + if (Boolean.getBoolean("org.apache.cxf.transport.http.forceURLConnection")) { + return URLConnectionHTTPConduitFactory; + } else { + return HttpClientHTTPConduitFactory; + } + } + }, + @ConfigDocEnumValue("VertxHttpClientHTTPConduitFactory") + VertxHttpClientHTTPConduitFactory { + @Override + public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, + EndpointReferenceType target) throws IOException { + final HttpClientPool httpClientPool = Objects.requireNonNull(b.getExtension(HttpClientPool.class), + "HttpClientPool in org.apache.cxf.Bus"); + return new VertxHttpClientHTTPConduit(b, localInfo, target, httpClientPool); + } + + @Override + public TLSClientParameters createTLSClientParameters(CXFClientInfo cxfClientInfo) { + if (cxfClientInfo.getHostnameVerifier() != null) { + throw new IllegalStateException( + getConduitDescription() + " does not support setting a hostname verifier." + + " AllowAllHostnameVerifier can be replaced by using a named TLS configuration" + + " with hostname-verification-algorithm set to NONE"); + } + return new QuarkusTLSClientParameters(cxfClientInfo.getTlsConfiguration()); + } + + }, + @ConfigDocEnumValue("HttpClientHTTPConduitFactory") + HttpClientHTTPConduitFactory { + @Override + public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, + EndpointReferenceType target) throws IOException { + return new HttpClientHTTPConduit(b, localInfo, target); + } + }, + @ConfigDocEnumValue("URLConnectionHTTPConduitFactory") + URLConnectionHTTPConduitFactory { + @Override + public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, + EndpointReferenceType target) throws IOException { + return new URLConnectionHTTPConduit(b, localInfo, target); + } + }; + + public static HTTPConduitImpl findDefaultHTTPConduitImpl() { + if (QuarkusHTTPConduitFactory.defaultHTTPConduitImpl == null) { + final String defaultName = System.getenv(QuarkusHTTPConduitFactory.QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY); + QuarkusHTTPConduitFactory.defaultHTTPConduitImpl = defaultName == null || defaultName.isEmpty() + ? VertxHttpClientHTTPConduitFactory + : valueOf(defaultName); + } + return QuarkusHTTPConduitFactory.defaultHTTPConduitImpl; + } + + @Override + public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, EndpointReferenceType target) + throws IOException { + throw new IllegalStateException( + "Call " + HTTPConduitImpl.class.getName() + ".resolveDefault() before calling createConduit()"); + } + + @Override + public String getConduitDescription() { + return "http-conduit-factory = " + name(); + } + +} \ No newline at end of file diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java new file mode 100644 index 000000000..7c1c8e0b0 --- /dev/null +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java @@ -0,0 +1,111 @@ +package io.quarkiverse.cxf; + +import java.io.IOException; +import java.util.Optional; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.cxf.Bus; +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.transport.http.HTTPConduit; +import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.cxf.ws.addressing.EndpointReferenceType; + +import io.quarkiverse.cxf.CxfClientConfig.WellKnownHostnameVerifier; +import io.quarkus.tls.TlsConfiguration; +import io.quarkus.tls.runtime.VertxCertificateHolder; +import io.quarkus.tls.runtime.config.TlsBucketConfig; +import io.vertx.core.Vertx; +import io.vertx.core.net.KeyCertOptions; +import io.vertx.core.net.KeyStoreOptionsBase; + +public interface HTTPConduitSpec { + default HTTPConduitSpec resolveDefault() { + return this; + } + + HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, EndpointReferenceType target) + throws IOException; + + default Optional tlsClientParameters(CXFClientInfo cxfClientInfo, Vertx vertx) { + final String hostnameVerifierName = cxfClientInfo.getHostnameVerifier(); + final TlsConfiguration tlsConfig = cxfClientInfo.getTlsConfiguration(); + if (hostnameVerifierName != null || tlsConfig != null) { + TLSClientParameters tlsCP = createTLSClientParameters(cxfClientInfo); + if (hostnameVerifierName != null) { + final Optional wellKnownHostNameVerifierName = WellKnownHostnameVerifier + .of(hostnameVerifierName); + if (wellKnownHostNameVerifierName.isPresent()) { + wellKnownHostNameVerifierName.get().configure(tlsCP); + } else { + final HostnameVerifier hostnameVerifier = CXFRuntimeUtils.getInstance(hostnameVerifierName, true); + if (hostnameVerifier == null) { + throw new RuntimeException("Could not find or instantiate " + hostnameVerifierName); + } + tlsCP.setHostnameVerifier(hostnameVerifier); + } + } + + if (tlsConfig != null) { + final KeyCertOptions keyStoreOptions = tlsConfig.getKeyStoreOptions(); + if (keyStoreOptions != null) { + try { + final KeyManagerFactory kmf = keyStoreOptions.getKeyManagerFactory(vertx); + tlsCP.setKeyManagers(kmf.getKeyManagers()); + } catch (Exception e) { + throw new RuntimeException("Could not set up key manager factory", e); + } + if (keyStoreOptions instanceof KeyStoreOptionsBase) { + final KeyStoreOptionsBase keyStoreOptionsBase = (KeyStoreOptionsBase) keyStoreOptions; + if (keyStoreOptionsBase.getAlias() != null) { + tlsCP.setCertAlias(keyStoreOptionsBase.getAlias()); + } + } + } + + if (tlsConfig.getTrustStoreOptions() != null) { + try { + final TrustManagerFactory tmf = tlsConfig.getTrustStoreOptions().getTrustManagerFactory(vertx); + tlsCP.setTrustManagers(tmf.getTrustManagers()); + } catch (Exception e) { + throw new RuntimeException("Could not set up trust manager factory", e); + } + } + + } + return Optional.of(tlsCP); + } + return Optional.empty(); + } + + default TLSClientParameters createTLSClientParameters(CXFClientInfo cxfClientInfo) { + TlsConfiguration tlsConfig = cxfClientInfo.getTlsConfiguration(); + TLSClientParameters tlsCP = new TLSClientParameters(); + if (tlsConfig instanceof VertxCertificateHolder) { + final VertxCertificateHolder vertxCertificateHOlder = (VertxCertificateHolder) tlsConfig; + final TlsBucketConfig bucketConfig = vertxCertificateHOlder.config(); + bucketConfig.cipherSuites().ifPresent(tlsCP::setCipherSuites); + + /* + * We are not able to transfer some TLS config options from VertxCertificateHolder + * to the legacy conduit implementations + */ + if (tlsConfig.isTrustAll()) { + throw new IllegalStateException( + this.getClass().getName().replace("Factory", "") + + " does not support quarkus.tls.trust-all = true."); + } + if (tlsConfig.getHostnameVerificationAlgorithm().isPresent()) { + throw new IllegalStateException(getConduitDescription() + + " does not support quarkus.tls.hostname-verification-algorithm. Use quarkus.cxf.client.\"client-name\".hostname-verifier instead."); + } + } + return tlsCP; + } + + String getConduitDescription(); + +} \ No newline at end of file diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java index 5cfa5ec73..313cec731 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java @@ -1,11 +1,6 @@ package io.quarkiverse.cxf; import java.io.IOException; -import java.util.Optional; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManagerFactory; import org.apache.cxf.Bus; import org.apache.cxf.configuration.security.AuthorizationPolicy; @@ -14,22 +9,11 @@ import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transport.http.HTTPConduitFactory; import org.apache.cxf.transport.http.HTTPTransportFactory; -import org.apache.cxf.transport.http.HttpClientHTTPConduit; -import org.apache.cxf.transport.http.URLConnectionHTTPConduit; import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.jboss.logging.Logger; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; -import io.quarkiverse.cxf.CxfClientConfig.WellKnownHostnameVerifier; -import io.quarkiverse.cxf.vertx.http.client.HttpClientPool; -import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit; -import io.quarkus.tls.TlsConfiguration; -import io.quarkus.tls.runtime.VertxCertificateHolder; -import io.quarkus.tls.runtime.config.TlsBucketConfig; import io.vertx.core.Vertx; -import io.vertx.core.net.KeyCertOptions; -import io.vertx.core.net.KeyStoreOptionsBase; /** * A HTTPConduitFactory with some client specific configuration, such as timeouts and SSL. @@ -45,180 +29,50 @@ public class QuarkusHTTPConduitFactory implements HTTPConduitFactory { public static final String QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY = "QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY"; static HTTPConduitImpl defaultHTTPConduitImpl; - private final HttpClientPool httpClientPool; private final CxfFixedConfig cxFixedConfig; private final CXFClientInfo cxfClientInfo; - private final HTTPConduitFactory busHTTPConduitFactory; + private final HTTPConduitSpec busHTTPConduitImpl; private final AuthorizationPolicy authorizationPolicy; - private final HTTPConduitImpl defaultHTTPConduitFactory; private final Vertx vertx; public QuarkusHTTPConduitFactory( - HttpClientPool httpClientPool, CxfFixedConfig cxFixedConfig, CXFClientInfo cxfClientInfo, - HTTPConduitFactory busHTTPConduitFactory, + HTTPConduitSpec busHTTPConduitImpl, AuthorizationPolicy authorizationPolicy, Vertx vertx) { super(); - this.httpClientPool = httpClientPool; this.cxFixedConfig = cxFixedConfig; this.cxfClientInfo = cxfClientInfo; - this.busHTTPConduitFactory = busHTTPConduitFactory; + this.busHTTPConduitImpl = busHTTPConduitImpl; this.authorizationPolicy = authorizationPolicy; - this.defaultHTTPConduitFactory = HTTPConduitImpl.findDefaultHTTPConduitImpl(); this.vertx = vertx; } @Override public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, EndpointReferenceType target) throws IOException { - HTTPConduitImpl httpConduitImpl = cxfClientInfo.getHttpConduitImpl(); + HTTPConduitSpec httpConduitImpl = cxfClientInfo.getHttpConduitImpl(); if (httpConduitImpl == null) { httpConduitImpl = cxFixedConfig.httpConduitFactory().orElse(null); } if (httpConduitImpl == null && (CXFRecorder.isHc5Present()) - && busHTTPConduitFactory != null) { - return configure( - busHTTPConduitFactory.createConduit(f, b, localInfo, target), - cxfClientInfo); + && busHTTPConduitImpl != null) { + return configure(f, busHTTPConduitImpl.resolveDefault(), cxfClientInfo, b, localInfo, target); } if (httpConduitImpl == null) { httpConduitImpl = HTTPConduitImpl.QuarkusCXFDefault; } - - final HTTPConduit result; - switch (httpConduitImpl) { - case CXFDefault: { - /* - * Mimic what is done in org.apache.cxf.transport.http.HTTPTransportFactory.getConduit(EndpointInfo, - * EndpointReferenceType, Bus) - */ - if (Boolean.getBoolean("org.apache.cxf.transport.http.forceURLConnection")) { - result = new URLConnectionHTTPConduit(b, localInfo, target); - } else { - result = new HttpClientHTTPConduit(b, localInfo, target); - } - break; - } - case QuarkusCXFDefault: - switch (defaultHTTPConduitFactory) { - case VertxHttpClientHTTPConduitFactory: { - result = new VertxHttpClientHTTPConduit(b, localInfo, target, httpClientPool); - break; - } - case URLConnectionHTTPConduitFactory: { - result = new URLConnectionHTTPConduit(b, localInfo, target); - break; - } - case HttpClientHTTPConduitFactory: { - result = new HttpClientHTTPConduit(b, localInfo, target); - break; - } - default: - throw new IllegalStateException("Unexpected " + HTTPConduitImpl.class.getSimpleName() + " value: " - + defaultHTTPConduitFactory); - } - break; - case VertxHttpClientHTTPConduitFactory: { - result = new VertxHttpClientHTTPConduit(b, localInfo, target, httpClientPool); - break; - } - case URLConnectionHTTPConduitFactory: { - result = new URLConnectionHTTPConduit(b, localInfo, target); - break; - } - case HttpClientHTTPConduitFactory: { - result = new HttpClientHTTPConduit(b, localInfo, target); - break; - } - default: - throw new IllegalStateException("Unexpected " + HTTPConduitImpl.class.getSimpleName() + " value: " - + httpConduitImpl); - } - return configure(result, cxfClientInfo); + return configure(f, httpConduitImpl.resolveDefault(), cxfClientInfo, b, localInfo, target); } - private HTTPConduit configure(HTTPConduit httpConduit, CXFClientInfo cxfClientInfo) throws IOException { - final String hostnameVerifierName = cxfClientInfo.getHostnameVerifier(); - final TlsConfiguration tlsConfig = cxfClientInfo.getTlsConfiguration(); - if (hostnameVerifierName != null || tlsConfig != null) { - QuarkusTLSClientParameters tlsCP = new QuarkusTLSClientParameters(tlsConfig); - - if (hostnameVerifierName != null) { - if (httpConduit instanceof VertxHttpClientHTTPConduit) { - throw new IllegalStateException( - VertxHttpClientHTTPConduit.class.getName() + " does not support setting a hostname verifier." - + " AllowAllHostnameVerifier can be replaced by using a named TLS configuration" - + " with hostname-verification-algorithm set to NONE"); - } - final Optional wellKnownHostNameVerifierName = WellKnownHostnameVerifier - .of(hostnameVerifierName); - if (wellKnownHostNameVerifierName.isPresent()) { - wellKnownHostNameVerifierName.get().configure(tlsCP); - } else { - final HostnameVerifier hostnameVerifier = CXFRuntimeUtils.getInstance(hostnameVerifierName, true); - if (hostnameVerifier == null) { - throw new RuntimeException("Could not find or instantiate " + hostnameVerifierName); - } - tlsCP.setHostnameVerifier(hostnameVerifier); - } - } - - if (tlsConfig != null) { - final KeyCertOptions keyStoreOptions = tlsConfig.getKeyStoreOptions(); - if (keyStoreOptions != null) { - try { - final KeyManagerFactory kmf = keyStoreOptions.getKeyManagerFactory(vertx); - tlsCP.setKeyManagers(kmf.getKeyManagers()); - } catch (Exception e) { - throw new RuntimeException("Could not set up key manager factory", e); - } - if (keyStoreOptions instanceof KeyStoreOptionsBase) { - final KeyStoreOptionsBase keyStoreOptionsBase = (KeyStoreOptionsBase) keyStoreOptions; - if (keyStoreOptionsBase.getAlias() != null) { - tlsCP.setCertAlias(keyStoreOptionsBase.getAlias()); - } - } - } - - if (tlsConfig.getTrustStoreOptions() != null) { - try { - final TrustManagerFactory tmf = tlsConfig.getTrustStoreOptions().getTrustManagerFactory(vertx); - tlsCP.setTrustManagers(tmf.getTrustManagers()); - } catch (Exception e) { - throw new RuntimeException("Could not set up trust manager factory", e); - } - } - - if (tlsConfig instanceof VertxCertificateHolder) { - final VertxCertificateHolder vertxCertificateHOlder = (VertxCertificateHolder) tlsConfig; - final TlsBucketConfig bucketConfig = vertxCertificateHOlder.config(); - bucketConfig.cipherSuites().ifPresent(tlsCP::setCipherSuites); - - /* - * We are not able to transfer some TLS config options from VertxCertificateHolder - * to the legacy conduit implementations - */ - if (!(httpConduit instanceof VertxHttpClientHTTPConduit)) { - if (tlsConfig.isTrustAll()) { - throw new IllegalStateException( - httpConduit.getClass().getName() + " does not support quarkus.tls.trust-all = true."); - } - if (tlsConfig.getHostnameVerificationAlgorithm().isPresent()) { - throw new IllegalStateException(httpConduit.getClass().getName() - + " does not support quarkus.tls.hostname-verification-algorithm. Use quarkus.cxf.client.\"client-name\".hostname-verifier instead."); - } - } - - } - } - - httpConduit.setTlsClientParameters(tlsCP); - } - + private HTTPConduit configure(HTTPTransportFactory f, HTTPConduitSpec httpConduitImpl, CXFClientInfo cxfClientInfo, Bus b, + EndpointInfo localInfo, + EndpointReferenceType target) throws IOException { + final HTTPConduit httpConduit = httpConduitImpl.createConduit(f, b, localInfo, target); + httpConduitImpl.tlsClientParameters(cxfClientInfo, vertx).ifPresent(httpConduit::setTlsClientParameters); final HTTPClientPolicy policy = new HTTPClientPolicy(); httpConduit.setClient(policy); policy.setConnectionTimeout(cxfClientInfo.getConnectionTimeout()); diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java index 7a566a833..24ba711b5 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java @@ -264,7 +264,7 @@ QuarkusTLSClientParameters findTLSClientParameters(Message message) { if (clientParameters.getHostnameVerifier() != null) { throw new IllegalStateException( - VertxHttpClientHTTPConduit.class.getName() + " does not support setting a hostname verifier." + getConduitName() + " does not support setting a hostname verifier." + " AllowAllHostnameVerifier can be replaced by using a named TLS configuration" + " with hostname-verification-algorithm set to NONE"); } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java deleted file mode 100644 index 807f85886..000000000 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduitFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.quarkiverse.cxf.vertx.http.client; - -import java.io.IOException; -import java.util.Objects; - -import org.apache.cxf.Bus; -import org.apache.cxf.common.injection.NoJSR250Annotations; -import org.apache.cxf.service.model.EndpointInfo; -import org.apache.cxf.transport.http.HTTPConduit; -import org.apache.cxf.transport.http.HTTPConduitFactory; -import org.apache.cxf.transport.http.HTTPTransportFactory; -import org.apache.cxf.ws.addressing.EndpointReferenceType; - -/** - * - */ -@NoJSR250Annotations -public class VertxHttpClientHTTPConduitFactory implements HTTPConduitFactory { - - public VertxHttpClientHTTPConduitFactory() { - super(); - } - - @Override - public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, EndpointReferenceType target) - throws IOException { - final HttpClientPool httpClientPool = Objects.requireNonNull(b.getExtension(HttpClientPool.class), - "HttpClientPool in org.apache.cxf.Bus"); - return new VertxHttpClientHTTPConduit(b, localInfo, target, httpClientPool); - } - -} diff --git a/extensions/transports-http-hc5/deployment/src/test/java/io/quarkiverse/cxf/transport/http/hc5/deployment/Hc5ConduitFactoryTest.java b/extensions/transports-http-hc5/deployment/src/test/java/io/quarkiverse/cxf/transport/http/hc5/deployment/Hc5ConduitFactoryTest.java index 3b424ba6f..c8cf209e5 100644 --- a/extensions/transports-http-hc5/deployment/src/test/java/io/quarkiverse/cxf/transport/http/hc5/deployment/Hc5ConduitFactoryTest.java +++ b/extensions/transports-http-hc5/deployment/src/test/java/io/quarkiverse/cxf/transport/http/hc5/deployment/Hc5ConduitFactoryTest.java @@ -4,13 +4,9 @@ import jakarta.jws.WebMethod; import jakarta.jws.WebService; -import org.apache.cxf.Bus; -import org.apache.cxf.BusFactory; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; -import org.apache.cxf.transport.http.HTTPConduitFactory; import org.apache.cxf.transport.http.asyncclient.hc5.AsyncHTTPConduit; -import org.apache.cxf.transport.http.asyncclient.hc5.AsyncHTTPConduitFactory; import org.assertj.core.api.Assertions; import org.jboss.logging.Logger; import org.jboss.shrinkwrap.api.ShrinkWrap; @@ -40,9 +36,9 @@ public class Hc5ConduitFactoryTest { @Test void conduitFactory() { - final Bus bus = BusFactory.getDefaultBus(); - final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class); - Assertions.assertThat(factory).isInstanceOf(AsyncHTTPConduitFactory.class); + // final Bus bus = BusFactory.getDefaultBus(); + // final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class); + // Assertions.assertThat(factory).isInstanceOf(AsyncHTTPConduitFactory.class); final Client client = ClientProxy.getClient(helloService); Assertions.assertThat(client.getConduit()).isInstanceOf(AsyncHTTPConduit.class); diff --git a/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java b/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java index e9fa31d46..65c3a08de 100644 --- a/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java +++ b/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java @@ -1,13 +1,20 @@ package io.quarkiverse.cxf.transport.http.hc5; +import java.io.IOException; import java.util.function.Consumer; import org.apache.cxf.Bus; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transport.http.HTTPConduitFactory; +import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.cxf.transport.http.asyncclient.hc5.AsyncHTTPConduitFactory; import org.apache.cxf.transport.http.asyncclient.hc5.AsyncHttpResponseWrapperFactory; import org.apache.cxf.workqueue.WorkQueueManager; +import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.eclipse.microprofile.context.ManagedExecutor; +import io.quarkiverse.cxf.HTTPConduitSpec; import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.runtime.RuntimeValue; @@ -25,6 +32,7 @@ public class Hc5Recorder { public RuntimeValue> customizeBus() { return new RuntimeValue<>(bus -> { + bus.setExtension(new Hc5HTTPConduitImpl(), HTTPConduitSpec.class); final WorkQueueManager wqm = bus.getExtension(WorkQueueManager.class); InstanceHandle managedExecutorInst = Arc.container().instance(ManagedExecutor.class); if (managedExecutorInst.isAvailable()) { @@ -35,4 +43,24 @@ public RuntimeValue> customizeBus() { bus.setExtension(new QuarkusAsyncHttpResponseWrapperFactory(), AsyncHttpResponseWrapperFactory.class); }); } + + public static class Hc5HTTPConduitImpl implements HTTPConduitSpec { + private AsyncHTTPConduitFactory asyncHTTPConduitFactory; + + @Override + public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo localInfo, EndpointReferenceType target) + throws IOException { + AsyncHTTPConduitFactory factory; + if ((factory = asyncHTTPConduitFactory) == null) { + factory = asyncHTTPConduitFactory = new AsyncHTTPConduitFactory(b); + } + return factory.createConduit(f, b, localInfo, target); + } + + @Override + public String getConduitDescription() { + return "io.quarkiverse.cxf:quarkus-cxf-rt-transports-http-hc5"; + } + + } } diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkiverse/cxf/opentelemetry/it/OpenTelemetryTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkiverse/cxf/opentelemetry/it/OpenTelemetryTest.java index f2d78ab7e..47d6e6735 100644 --- a/integration-tests/opentelemetry/src/test/java/io/quarkiverse/cxf/opentelemetry/it/OpenTelemetryTest.java +++ b/integration-tests/opentelemetry/src/test/java/io/quarkiverse/cxf/opentelemetry/it/OpenTelemetryTest.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test; import io.opentelemetry.api.trace.SpanKind; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitImpl; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import io.restassured.common.mapper.TypeRef; diff --git a/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java b/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java index dca17d95c..2d5a0e572 100644 --- a/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java +++ b/integration-tests/ws-security-policy/src/test/java/io/quarkiverse/cxf/it/security/policy/TransportPolicyTest.java @@ -12,7 +12,7 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl; +import io.quarkiverse.cxf.HTTPConduitImpl; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; @@ -47,7 +47,7 @@ void helloAllowAll() { .then() .statusCode(500) .body(Matchers.containsString( - "io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit does not support setting a hostname verifier.")); + "http-conduit-factory = VertxHttpClientHTTPConduitFactory does not support setting a hostname verifier.")); /* * But the same works when the hostname verification is disabled via * quarkus.tls.helloAllowAll.trust-store.hostname-verification-algorithm = NONE @@ -80,7 +80,7 @@ void helloAllowAll() { .then() .statusCode(500) .body(Matchers.containsString( - "org.apache.cxf.transport.http.URLConnectionHTTPConduit does not support quarkus.tls.hostname-verification-algorithm. Use quarkus.cxf.client.\"client-name\".hostname-verifier instead.")); + "http-conduit-factory = URLConnectionHTTPConduitFactory does not support quarkus.tls.hostname-verification-algorithm. Use quarkus.cxf.client.\"client-name\".hostname-verifier instead.")); break; default: throw new IllegalArgumentException("Unexpected value: " + defaultImpl); @@ -103,7 +103,7 @@ void helloCustomHostnameVerifier() { .then() .statusCode(500) .body(Matchers.containsString( - "io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit does not support setting a hostname verifier.")); + "http-conduit-factory = VertxHttpClientHTTPConduitFactory does not support setting a hostname verifier. AllowAllHostnameVerifier can be replaced by using a named TLS configuration with hostname-verification-algorithm set to NONE")); break; case URLConnectionHTTPConduitFactory: /*