diff --git a/docs/src/main/asciidoc/tls-registry-reference.adoc b/docs/src/main/asciidoc/tls-registry-reference.adoc index b566976b92cedd..4024783fe2e8af 100644 --- a/docs/src/main/asciidoc/tls-registry-reference.adoc +++ b/docs/src/main/asciidoc/tls-registry-reference.adoc @@ -26,7 +26,7 @@ ifndef::no-reactive-routes[] , or Reactive Routes endif::no-reactive-routes[] . -The TLS Registry extension is automatically included in your project when you use compatible extensions, such as Quarkus REST, gRPC, SmallRye GraphQL Client, or Reactive Routes. + As a result, applications that use the TLS Registry can be ready to handle secure communications out of the box. TLS Registry also provides features like automatic certificate reloading, Let's Encrypt (ACME) integration, Kubernetes Cert-Manager support, and compatibility with various keystore formats, such as PKCS12, PEM, and JKS. @@ -155,9 +155,18 @@ quarkus.grpc.clients.hello.tls-configuration-name=MY_TLS_CONFIGURATION ---- quarkus.smallrye-graphql-client.my-client.tls-configuration-name=MY_TLS_CONFIGURATION ---- -> [NOTE] -> When using the Typesafe GraphQL client with a certificate reloading mechanism (see <>), it is essential to override the bean's scope to `RequestScoped` (or another similar scope). By default, the Typesafe client is an application-scoped bean. -> This guarantees that each new instance of the bean created after a certificate reload will be configured with the latest certificate. + +[NOTE] +==== +When using the Typesafe GraphQL client with a certificate +reloading mechanism (see <>), it is essential to +override the bean's scope to `RequestScoped` (or another similar scope +shorter than application). This is because by default, the Typesafe client is an +application-scoped bean, so shortening the scope guarantees that new instances of the bean +created after a certificate reload will be configured with the latest +certificate. Dynamic clients are `@Dependent` scoped, so you should +inject them into components with an appropriate scope. +==== == Configuring TLS diff --git a/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java b/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java index 012944bdad91f7..59f6cc99488eb7 100644 --- a/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java +++ b/extensions/smallrye-graphql-client/deployment/src/main/java/io/quarkus/smallrye/graphql/client/deployment/SmallRyeGraphQLClientProcessor.java @@ -74,23 +74,37 @@ void setupServiceProviders(BuildProducer services) { .allProvidersFromClassPath("io.smallrye.graphql.client.typesafe.api.TypesafeGraphQLClientBuilder")); services.produce(ServiceProviderBuildItem .allProvidersFromClassPath("io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClientBuilder")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.ArgumentFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.DirectiveFactory")); services.produce( - ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.DirectiveArgumentFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.DocumentFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.EnumFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.FieldFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.FragmentFactory")); + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.ArgumentFactory")); services.produce( - ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.FragmentReferenceFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.InlineFragmentFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.InputObjectFactory")); + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.DirectiveFactory")); services.produce( - ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.InputObjectFieldFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.OperationFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.VariableFactory")); - services.produce(ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.VariableTypeFactory")); + ServiceProviderBuildItem + .allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.DirectiveArgumentFactory")); + services.produce( + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.DocumentFactory")); + services.produce( + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.EnumFactory")); + services.produce( + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.FieldFactory")); + services.produce( + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.FragmentFactory")); + services.produce( + ServiceProviderBuildItem + .allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.FragmentReferenceFactory")); + services.produce(ServiceProviderBuildItem + .allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.InlineFragmentFactory")); + services.produce(ServiceProviderBuildItem + .allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.InputObjectFactory")); + services.produce( + ServiceProviderBuildItem + .allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.InputObjectFieldFactory")); + services.produce( + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.OperationFactory")); + services.produce( + ServiceProviderBuildItem.allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.VariableFactory")); + services.produce(ServiceProviderBuildItem + .allProvidersFromClassPath("io.smallrye.graphql.client.core.factory.VariableTypeFactory")); } @BuildStep diff --git a/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/SSLTestingTools.java b/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/SSLTestingTools.java index 99dac5dfeeb369..90f391bdec3260 100644 --- a/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/SSLTestingTools.java +++ b/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/SSLTestingTools.java @@ -10,7 +10,6 @@ import io.vertx.core.http.ClientAuth; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.net.JksOptions; import io.vertx.core.net.PfxOptions; public class SSLTestingTools { @@ -54,6 +53,6 @@ public HttpServer runServer(String keystorePath, String keystorePassword, } public void close() { - vertx.close(); + vertx.close().toCompletionStage().toCompletableFuture().join(); } } diff --git a/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreDefaultTest.java b/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreDefaultTest.java index e92806f8a3d8a0..1496d91a8ca7ec 100644 --- a/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreDefaultTest.java +++ b/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreDefaultTest.java @@ -1,20 +1,16 @@ package io.quarkus.smallrye.graphql.client.deployment.ssl; -import io.quarkus.arc.Arc; -import io.quarkus.test.QuarkusUnitTest; -import io.quarkus.tls.CertificateUpdatedEvent; -import io.quarkus.tls.TlsConfiguration; -import io.quarkus.tls.TlsConfigurationRegistry; -import io.smallrye.certs.Format; -import io.smallrye.certs.junit5.Certificate; -import io.smallrye.certs.junit5.Certificates; -import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; -import io.smallrye.graphql.client.typesafe.api.GraphQLClientApi; -import io.vertx.core.http.HttpServer; -import io.vertx.core.net.KeyCertOptions; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.UUID; + import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; + import org.assertj.core.api.Assertions; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.graphql.Query; @@ -26,12 +22,18 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; +import io.quarkus.arc.Arc; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.tls.CertificateUpdatedEvent; +import io.quarkus.tls.TlsConfiguration; +import io.quarkus.tls.TlsConfigurationRegistry; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; +import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; +import io.smallrye.graphql.client.typesafe.api.GraphQLClientApi; +import io.vertx.core.http.HttpServer; +import io.vertx.core.net.KeyCertOptions; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "wrong-test-reload", password = "password", formats = Format.PKCS12, client = true), @@ -51,7 +53,7 @@ public class TypesafeGraphQLClientReloadKeystoreDefaultTest { quarkus.tls.key-store.p12.password=password quarkus.smallrye-graphql-client.my-client.url=https://127.0.0.1:%d/ quarkus.tls.trust-all=true - """.formatted(temp.getAbsolutePath() + "/tls.p12", PORT); + """.formatted(temp.getAbsolutePath() + File.separator + "tls.p12", PORT); @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() @@ -99,7 +101,8 @@ static void setupServer() throws Exception { @Test void testReloading() throws IOException { TlsConfiguration def = registry.getDefault().orElseThrow(); - KeyCertOptions keystoreOptionsBefore = (KeyCertOptions) GraphQLClientsConfiguration.getInstance().getClient("my-client").getTlsKeyStoreOptions(); + KeyCertOptions keystoreOptionsBefore = (KeyCertOptions) GraphQLClientsConfiguration.getInstance().getClient("my-client") + .getTlsKeyStoreOptions(); Arc.container().requestContext().activate(); try { myApi.getResult(); @@ -116,7 +119,8 @@ void testReloading() throws IOException { event.fire(new CertificateUpdatedEvent("", def)); Arc.container().requestContext().activate(); try { - assertThat(GraphQLClientsConfiguration.getInstance().getClient("my-client").getTlsKeyStoreOptions()).isNotEqualTo(keystoreOptionsBefore); + assertThat(GraphQLClientsConfiguration.getInstance().getClient("my-client").getTlsKeyStoreOptions()) + .isNotEqualTo(keystoreOptionsBefore); assertThat(myApi.getResult()).isEqualTo(EXPECTED_RESPONSE); } finally { Arc.container().requestContext().terminate(); diff --git a/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreTest.java b/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreTest.java index b64b69fac0a40e..5d90e8cf8f678d 100644 --- a/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreTest.java +++ b/extensions/smallrye-graphql-client/deployment/src/test/java/io/quarkus/smallrye/graphql/client/deployment/ssl/TypesafeGraphQLClientReloadKeystoreTest.java @@ -7,8 +7,6 @@ import java.nio.file.Files; import java.util.UUID; -import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; -import io.vertx.core.net.KeyCertOptions; import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; @@ -32,8 +30,10 @@ import io.smallrye.certs.Format; import io.smallrye.certs.junit5.Certificate; import io.smallrye.certs.junit5.Certificates; +import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; import io.smallrye.graphql.client.typesafe.api.GraphQLClientApi; import io.vertx.core.http.HttpServer; +import io.vertx.core.net.KeyCertOptions; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "wrong-test-reload", password = "password", formats = Format.PKCS12, client = true), @@ -54,7 +54,7 @@ public class TypesafeGraphQLClientReloadKeystoreTest { quarkus.tls.my-tls-client.key-store.p12.password=password quarkus.smallrye-graphql-client.my-client.url=https://127.0.0.1:%d/ quarkus.tls.my-tls-client.trust-all=true - """.formatted(temp.getAbsolutePath() + "/tls.p12", PORT); + """.formatted(temp.getAbsolutePath() + File.separator + "tls.p12", PORT); @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() @@ -102,7 +102,8 @@ static void setupServer() throws Exception { @Test void testReloading() throws IOException { TlsConfiguration tlsClient = registry.get("my-tls-client").orElseThrow(); - KeyCertOptions keystoreOptionsBefore = (KeyCertOptions) GraphQLClientsConfiguration.getInstance().getClient("my-client").getTlsKeyStoreOptions(); + KeyCertOptions keystoreOptionsBefore = (KeyCertOptions) GraphQLClientsConfiguration.getInstance().getClient("my-client") + .getTlsKeyStoreOptions(); Arc.container().requestContext().activate(); try { myApi.getResult(); @@ -119,7 +120,8 @@ void testReloading() throws IOException { event.fire(new CertificateUpdatedEvent("my-tls-client", tlsClient)); Arc.container().requestContext().activate(); try { - assertThat(GraphQLClientsConfiguration.getInstance().getClient("my-client").getTlsKeyStoreOptions()).isNotEqualTo(keystoreOptionsBefore); + assertThat(GraphQLClientsConfiguration.getInstance().getClient("my-client").getTlsKeyStoreOptions()) + .isNotEqualTo(keystoreOptionsBefore); assertThat(myApi.getResult()).isEqualTo(EXPECTED_RESPONSE); } finally { Arc.container().requestContext().terminate(); diff --git a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientCertificateUpdateEventListener.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientCertificateUpdateEventListener.java index 201d56054af228..5d0b5b0aeff434 100644 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientCertificateUpdateEventListener.java +++ b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientCertificateUpdateEventListener.java @@ -1,6 +1,7 @@ package io.quarkus.smallrye.graphql.client.runtime; -import io.smallrye.graphql.client.impl.GraphQLClientConfiguration; +import static io.quarkus.tls.runtime.config.TlsConfig.DEFAULT_NAME; + import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import jakarta.inject.Singleton; @@ -9,10 +10,9 @@ import io.quarkus.tls.CertificateUpdatedEvent; import io.quarkus.tls.TlsConfiguration; +import io.smallrye.graphql.client.impl.GraphQLClientConfiguration; import io.smallrye.graphql.client.impl.GraphQLClientsConfiguration; -import static io.quarkus.tls.runtime.config.TlsConfig.DEFAULT_NAME; - @Singleton public class GraphQLClientCertificateUpdateEventListener { @@ -26,10 +26,12 @@ public void onCertificateUpdate(@Observes CertificateUpdatedEvent event) { TlsConfiguration updatedTlsConfiguration = event.tlsConfiguration(); graphQLClientsConfig.clients .forEach((configKey, clientConfig) -> { - GraphQLClientConfiguration graphQLClientConfiguration = GraphQLClientsConfiguration.getInstance().getClient(configKey); + GraphQLClientConfiguration graphQLClientConfiguration = GraphQLClientsConfiguration.getInstance() + .getClient(configKey); clientConfig.tlsConfigurationName.ifPresentOrElse(tlsConfigurationName -> { if (tlsConfigurationName.equals(updatedTlsConfigurationName)) { - updateConfiguration(updatedTlsConfigurationName, updatedTlsConfiguration, graphQLClientConfiguration, configKey); + updateConfiguration(updatedTlsConfigurationName, updatedTlsConfiguration, + graphQLClientConfiguration, configKey); } }, () -> { if (DEFAULT_NAME.equals(updatedTlsConfigurationName)) { @@ -39,7 +41,8 @@ public void onCertificateUpdate(@Observes CertificateUpdatedEvent event) { }); } - private void updateConfiguration(String tlsBucketName, TlsConfiguration updatedTlsConfiguration, GraphQLClientConfiguration graphQLClientConfiguration, String configKey) { + private void updateConfiguration(String tlsBucketName, TlsConfiguration updatedTlsConfiguration, + GraphQLClientConfiguration graphQLClientConfiguration, String configKey) { LOG.infof("Certificate reloaded for the client '%s' using the TLS configuration (bucket) name '%s'", configKey, tlsBucketName); graphQLClientConfiguration diff --git a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfig.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfig.java index b5286242cee6b8..3bd6c3e1fad036 100644 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfig.java +++ b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/GraphQLClientConfig.java @@ -58,7 +58,7 @@ public class GraphQLClientConfig { * {@code quarkus.smallrye-graphql-client."client-name".tls-bucket-name}. */ @ConfigItem - @Deprecated + @Deprecated(forRemoval = true, since = "3.16.0") public Optional trustStore; /** @@ -69,7 +69,7 @@ public class GraphQLClientConfig { * {@code quarkus.smallrye-graphql-client."client-name".tls-bucket-name}. */ @ConfigItem - @Deprecated + @Deprecated(forRemoval = true, since = "3.16.0") public Optional trustStorePassword; /** @@ -80,7 +80,7 @@ public class GraphQLClientConfig { * {@code quarkus.smallrye-graphql-client."client-name".tls-bucket-name}. */ @ConfigItem - @Deprecated + @Deprecated(forRemoval = true, since = "3.16.0") public Optional trustStoreType; /** @@ -91,7 +91,7 @@ public class GraphQLClientConfig { * {@code quarkus.smallrye-graphql-client."client-name".tls-bucket-name}. */ @ConfigItem - @Deprecated + @Deprecated(forRemoval = true, since = "3.16.0") public Optional keyStore; /** @@ -102,7 +102,7 @@ public class GraphQLClientConfig { * {@code quarkus.smallrye-graphql-client."client-name".tls-bucket-name}. */ @ConfigItem - @Deprecated + @Deprecated(forRemoval = true, since = "3.16.0") public Optional keyStorePassword; /** @@ -114,7 +114,7 @@ public class GraphQLClientConfig { * */ @ConfigItem - @Deprecated + @Deprecated(forRemoval = true, since = "3.16.0") public Optional keyStoreType; /** diff --git a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java index f4c1294f7fba1c..91f3a9aff73c6c 100644 --- a/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java +++ b/extensions/smallrye-graphql-client/runtime/src/main/java/io/quarkus/smallrye/graphql/client/runtime/SmallRyeGraphQLClientRecorder.java @@ -127,7 +127,8 @@ private GraphQLClientConfiguration toSmallRyeNativeConfiguration(GraphQLClientCo transformed.setTlsKeyStoreOptions(tlsConfiguration.getKeyStoreOptions()); transformed.setTlsTrustStoreOptions(tlsConfiguration.getTrustStoreOptions()); transformed.setSslOptions(tlsConfiguration.getSSLOptions()); - tlsConfiguration.getHostnameVerificationAlgorithm().ifPresent(transformed::setHostnameVerificationAlgorithm); + tlsConfiguration.getHostnameVerificationAlgorithm() + .ifPresent(transformed::setHostnameVerificationAlgorithm); transformed.setUsesSni(Boolean.valueOf(tlsConfiguration.usesSni())); }, () -> { // DEPRECATED @@ -166,8 +167,9 @@ private Optional resolveTlsConfigurationForRegistry(GraphQLCli if (Arc.container() != null) { TlsConfigurationRegistry tlsConfigurationRegistry = Arc.container().select(TlsConfigurationRegistry.class).orNull(); if (tlsConfigurationRegistry != null) { - if (tlsConfigurationRegistry.getDefault().isPresent() && (tlsConfigurationRegistry.getDefault().get().getTrustStoreOptions() != null - || tlsConfigurationRegistry.getDefault().get().isTrustAll())) { + if (tlsConfigurationRegistry.getDefault().isPresent() + && (tlsConfigurationRegistry.getDefault().get().getTrustStoreOptions() != null + || tlsConfigurationRegistry.getDefault().get().isTrustAll())) { return tlsConfigurationRegistry.getDefault(); } return TlsConfiguration.from(tlsConfigurationRegistry, quarkusConfig.tlsConfigurationName);