From 639a66b54b2a99c67af64d37d318ce43e9e45a50 Mon Sep 17 00:00:00 2001 From: Ozan Gunalp Date: Tue, 20 Feb 2024 18:43:22 +0100 Subject: [PATCH 01/34] Bump Smallrye Reactive Messaging from 4.16.2 to 4.17.0 Resolves split package on health package issue #38711 (cherry picked from commit e530ff1f7573eb74578ad10b03a0044e2b96c9eb) --- bom/application/pom.xml | 2 +- .../deployment/SmallRyeReactiveMessagingProcessor.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 9cf453c2a4347..217d749cdb162 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -62,7 +62,7 @@ 1.0.13 3.0.1 3.9.0 - 4.16.2 + 4.17.0 2.5.0 2.1.2 2.1.1 diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java index eada958ba8648..b5134c2c1ca48 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java @@ -81,9 +81,9 @@ import io.smallrye.reactive.messaging.EmitterConfiguration; import io.smallrye.reactive.messaging.Invoker; import io.smallrye.reactive.messaging.annotations.Blocking; -import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingLivenessCheck; -import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingReadinessCheck; -import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingStartupCheck; +import io.smallrye.reactive.messaging.extension.health.SmallRyeReactiveMessagingLivenessCheck; +import io.smallrye.reactive.messaging.extension.health.SmallRyeReactiveMessagingReadinessCheck; +import io.smallrye.reactive.messaging.extension.health.SmallRyeReactiveMessagingStartupCheck; import io.smallrye.reactive.messaging.providers.extension.ChannelConfiguration; public class SmallRyeReactiveMessagingProcessor { From b283d469f717b3c421333571ee483b740e224d64 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Tue, 27 Feb 2024 09:16:24 +0100 Subject: [PATCH 02/34] Upgrade to testcontainers 1.19.6 (cherry picked from commit 0a7e34fbe3fd03a71c373356c0013fb20e334b5c) --- bom/application/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 217d749cdb162..047434cb4cd96 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -207,8 +207,8 @@ 1.11.3 2.5.8.Final 0.1.18.Final - 1.19.4 - 3.3.4 + 1.19.6 + 3.3.5 2.0.0 1.4.4 From 5463707996079a25634847c77e71f64997554258 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Thu, 22 Feb 2024 22:05:05 +0000 Subject: [PATCH 03/34] Clarify that `quarkus.profile` cannot be set from a profile aware file (cherry picked from commit abf03b2176fca673e3a0b02622254f557316459b) --- docs/src/main/asciidoc/config-reference.adoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/config-reference.adoc b/docs/src/main/asciidoc/config-reference.adoc index 1980740a757c3..c51b2fc52f6b2 100644 --- a/docs/src/main/asciidoc/config-reference.adoc +++ b/docs/src/main/asciidoc/config-reference.adoc @@ -377,7 +377,12 @@ Properties in the profile aware file have priority over profile aware properties [WARNING] ==== -The profile aware file must be present in the exact same location as the main `application.properties` file. +Do not use Profile aware files to set `quarkus.profile` or `quarkus.test.profile`. This will not work because the +profile is required in advance to load the profile aware files. + +A profile aware file is only loaded if the unprofiled `application.properties` is also available in the same location +and the file extension matches between the files. This is required to keep a consistent loading order and pair all the +resources together. ==== === Parent Profile From 6302dee2886fba6eedd898114eff01de091663fb Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Wed, 28 Feb 2024 09:51:38 +0000 Subject: [PATCH 04/34] Update docs/src/main/asciidoc/config-reference.adoc Co-authored-by: George Gastaldi (cherry picked from commit 78906ada2f72159fc348dde6e3178ad9ca80b8b8) --- docs/src/main/asciidoc/config-reference.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/config-reference.adoc b/docs/src/main/asciidoc/config-reference.adoc index c51b2fc52f6b2..c205732044559 100644 --- a/docs/src/main/asciidoc/config-reference.adoc +++ b/docs/src/main/asciidoc/config-reference.adoc @@ -377,7 +377,7 @@ Properties in the profile aware file have priority over profile aware properties [WARNING] ==== -Do not use Profile aware files to set `quarkus.profile` or `quarkus.test.profile`. This will not work because the +Do not use profile aware files to set `quarkus.profile` or `quarkus.test.profile`. This will not work because the profile is required in advance to load the profile aware files. A profile aware file is only loaded if the unprofiled `application.properties` is also available in the same location From 3290977fb2db20128def7fb7f54988eb094bb73f Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 28 Feb 2024 09:23:30 +0200 Subject: [PATCH 05/34] Make sure Response and RestResponse work properly with IPv6 addresses Fixes: #39041 (cherry picked from commit 86fc0b2c8e43ad3b8448c2a36db48b6a17f65636) --- .../resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java | 4 ++-- .../reactive/server/jaxrs/RestResponseBuilderImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java index f1a546ff5c846..7b04ab947a610 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java @@ -28,7 +28,7 @@ public Response.ResponseBuilder location(URI location) { try { String host = req.getRequestHost(); int port = -1; - int index = host.indexOf(":"); + int index = host.lastIndexOf(":"); if (index > -1) { port = Integer.parseInt(host.substring(index + 1)); host = host.substring(0, index); @@ -69,7 +69,7 @@ public Response.ResponseBuilder contentLocation(URI location) { try { String host = req.getRequestHost(); int port = -1; - int index = host.indexOf(":"); + int index = host.lastIndexOf(":"); if (index > -1) { port = Integer.parseInt(host.substring(index + 1)); host = host.substring(0, index); diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java index a62003cbef6a5..43c98757afe21 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java @@ -28,7 +28,7 @@ public RestResponse.ResponseBuilder location(URI location) { try { String host = req.getRequestHost(); int port = -1; - int index = host.indexOf(":"); + int index = host.lastIndexOf(":"); if (index > -1) { port = Integer.parseInt(host.substring(index + 1)); host = host.substring(0, index); @@ -69,7 +69,7 @@ public RestResponse.ResponseBuilder contentLocation(URI location) { try { String host = req.getRequestHost(); int port = -1; - int index = host.indexOf(":"); + int index = host.lastIndexOf(":"); if (index > -1) { port = Integer.parseInt(host.substring(index + 1)); host = host.substring(0, index); From 4e79663af4836a57ba2c52861b01caf52b627d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20ALLEMAND?= Date: Wed, 28 Feb 2024 18:42:12 +0100 Subject: [PATCH 06/34] Doc: add Pulsar in Dev Services Overview (cherry picked from commit e35739fd8d590fe104c2c8ebd12feb8f4ae8a977) --- docs/src/main/asciidoc/dev-services.adoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/src/main/asciidoc/dev-services.adoc b/docs/src/main/asciidoc/dev-services.adoc index 39c49c6c55d39..d8dff1bd729e2 100644 --- a/docs/src/main/asciidoc/dev-services.adoc +++ b/docs/src/main/asciidoc/dev-services.adoc @@ -100,6 +100,12 @@ xref:rabbitmq-dev-services.adoc[RabbitMQ Dev Services Guide]. include::{generated-dir}/config/quarkus-smallrye-reactivemessaging-rabbitmq-config-group-rabbit-mq-dev-services-build-time-config.adoc[opts=optional, leveloffset=+1] +== Pulsar + +The Pulsar Dev Service will be enabled when the `quarkus-smallrye-reactive-messaging-pulsar` extension is present in your application, and +the broker address has not been explicitly configured. More information can be found in the +xref:pulsar-dev-services.adoc[Pulsar Dev Services Guide]. + == Redis The Redis Dev Service will be enabled when the `quarkus-redis-client` extension is present in your application, and From 4c0de7b62147e9f59c3dbd168bf5a72857d1e533 Mon Sep 17 00:00:00 2001 From: The-Huginn Date: Tue, 27 Feb 2024 09:22:50 +0100 Subject: [PATCH 07/34] Remove selector field from generated Job manifest in docs (cherry picked from commit f3f18ebf06d8d47532f5e80d62c5b5cc34a219b4) --- docs/src/main/asciidoc/picocli.adoc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/src/main/asciidoc/picocli.adoc b/docs/src/main/asciidoc/picocli.adoc index 1d0edcf3ec241..4ec8182a34c41 100644 --- a/docs/src/main/asciidoc/picocli.adoc +++ b/docs/src/main/asciidoc/picocli.adoc @@ -337,10 +337,6 @@ metadata: name: app spec: completionMode: NonIndexed - selector: - matchLabels: - app.kubernetes.io/name: app - app.kubernetes.io/version: 0.1-SNAPSHOT suspend: false template: metadata: From 5af6cfd734e142616fa52e653d39dffe4417a163 Mon Sep 17 00:00:00 2001 From: Katia Aresti Date: Wed, 28 Feb 2024 21:49:17 +0100 Subject: [PATCH 08/34] Updates to Infinispan 14.0.25.Final (cherry picked from commit b5dff7af96a59586b34bcd372eaa177f64686a01) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 047434cb4cd96..35ba3e9c57e02 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -141,7 +141,7 @@ 2.2 5.10.1 1.5.0 - 14.0.24.Final + 14.0.25.Final 4.6.5.Final 3.1.5 4.1.106.Final From 0c69481792053919e3b232cb1d1247a5bc43bf1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Wed, 28 Feb 2024 22:59:00 +0100 Subject: [PATCH 09/34] Optionally run DNS lookup to OIDC server on worker thread (cherry picked from commit 6a29b34d8fae6a38452f9444a8c31eebb52bd54d) --- .../client/runtime/OidcClientRecorder.java | 12 ++++-- .../oidc/common/runtime/OidcCommonConfig.java | 7 +++ .../oidc/common/runtime/OidcCommonUtils.java | 43 ++++++++++++++++++- .../resources/application-dev-mode.properties | 2 + .../oidc/runtime/OidcProviderClient.java | 14 ++++-- .../io/quarkus/oidc/runtime/OidcRecorder.java | 7 +-- 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java index f2d1a07153ba7..101e88aab1ae8 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientRecorder.java @@ -122,7 +122,8 @@ protected static Uni createOidcClientUni(OidcClientConfig oidcConfig OidcCommonUtils.setHttpClientOptions(oidcConfig, tlsConfig, options); - WebClient client = WebClient.create(new io.vertx.mutiny.core.Vertx(vertx.get()), options); + var mutinyVertx = new io.vertx.mutiny.core.Vertx(vertx.get()); + WebClient client = WebClient.create(mutinyVertx, options); Map> oidcRequestFilters = OidcCommonUtils.getOidcRequestFilters(); @@ -139,7 +140,8 @@ protected static Uni createOidcClientUni(OidcClientConfig oidcConfig OidcCommonUtils.getOidcEndpointUrl(authServerUriString, oidcConfig.tokenPath), OidcCommonUtils.getOidcEndpointUrl(authServerUriString, oidcConfig.revokePath))); } else { - tokenUrisUni = discoverTokenUris(client, oidcRequestFilters, authServerUriString.toString(), oidcConfig); + tokenUrisUni = discoverTokenUris(client, oidcRequestFilters, authServerUriString.toString(), oidcConfig, + mutinyVertx); } } return tokenUrisUni.onItemOrFailure() @@ -220,9 +222,11 @@ private static void setGrantClientParams(OidcClientConfig oidcConfig, MultiMap g private static Uni discoverTokenUris(WebClient client, Map> oidcRequestFilters, - String authServerUrl, OidcClientConfig oidcConfig) { + String authServerUrl, OidcClientConfig oidcConfig, io.vertx.mutiny.core.Vertx vertx) { final long connectionDelayInMillisecs = OidcCommonUtils.getConnectionDelayInMillis(oidcConfig); - return OidcCommonUtils.discoverMetadata(client, oidcRequestFilters, authServerUrl, connectionDelayInMillisecs) + return OidcCommonUtils + .discoverMetadata(client, oidcRequestFilters, authServerUrl, connectionDelayInMillisecs, vertx, + oidcConfig.useBlockingDnsLookup) .onItem().transform(json -> new OidcConfigurationMetadata(json.getString("token_endpoint"), json.getString("revocation_endpoint"))); } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonConfig.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonConfig.java index b3c9f05ff21d4..678d1d9241ff3 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonConfig.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonConfig.java @@ -74,6 +74,13 @@ public class OidcCommonConfig { @ConfigItem(defaultValue = "10s") public Duration connectionTimeout = Duration.ofSeconds(10); + /** + * Whether DNS lookup should be performed on the worker thread. + * Use this option when you can see logged warnings about blocked Vert.x event loop by HTTP requests to OIDC server. + */ + @ConfigItem(defaultValue = "false") + public boolean useBlockingDnsLookup; + /** * The maximum size of the connection pool used by the WebClient. */ diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java index 9e43d63452b73..b73f623dc8c8f 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java @@ -4,8 +4,10 @@ import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; +import java.net.InetAddress; import java.net.URI; import java.net.URLEncoder; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -20,6 +22,8 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalInt; +import java.util.concurrent.Callable; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -54,8 +58,10 @@ import io.vertx.core.net.KeyStoreOptions; import io.vertx.core.net.ProxyOptions; import io.vertx.mutiny.core.MultiMap; +import io.vertx.mutiny.core.Vertx; import io.vertx.mutiny.core.buffer.Buffer; import io.vertx.mutiny.ext.web.client.HttpRequest; +import io.vertx.mutiny.ext.web.client.HttpResponse; import io.vertx.mutiny.ext.web.client.WebClient; public class OidcCommonUtils { @@ -433,7 +439,7 @@ public static Predicate oidcEndpointNotAvailable() { } public static Uni discoverMetadata(WebClient client, Map> filters, - String authServerUrl, long connectionDelayInMillisecs) { + String authServerUrl, long connectionDelayInMillisecs, Vertx vertx, boolean blockingDnsLookup) { final String discoveryUrl = getDiscoveryUri(authServerUrl); HttpRequest request = client.getAbs(discoveryUrl); if (!filters.isEmpty()) { @@ -443,7 +449,7 @@ public static Uni discoverMetadata(WebClient client, Map { + return sendRequest(vertx, request, blockingDnsLookup).onItem().transform(resp -> { if (resp.statusCode() == 200) { return resp.bodyAsJsonObject(); } else { @@ -531,4 +537,37 @@ public static List getMatchingOidcRequestFilters(Map> sendRequest(io.vertx.core.Vertx vertx, HttpRequest request, + boolean blockingDnsLookup) { + if (blockingDnsLookup) { + return sendRequest(new Vertx(vertx), request, true); + } else { + return request.send(); + } + } + + public static Uni> sendRequest(Vertx vertx, HttpRequest request, boolean blockingDnsLookup) { + if (blockingDnsLookup) { + return vertx.executeBlocking(new Callable() { + @Override + public Void call() { + try { + // cache DNS lookup + InetAddress.getByName(request.host()); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + return null; + } + }).flatMap(new Function>>() { + @Override + public Uni> apply(Void unused) { + return request.send(); + } + }); + } else { + return request.send(); + } + } } diff --git a/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties b/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties index 26c7ff4e26bd1..4aaaec7a7eb44 100644 --- a/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties +++ b/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties @@ -11,3 +11,5 @@ quarkus.log.category."com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyle quarkus.log.category."io.quarkus.oidc.runtime.TenantConfigContext".level=DEBUG quarkus.log.file.enable=true +# use blocking DNS lookup so that we have it tested somewhere +quarkus.oidc.use-blocking-dns-lookup=true \ No newline at end of file diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java index 2f19f90c46ac7..40bc06da33605 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java @@ -84,16 +84,22 @@ public OidcConfigurationMetadata getMetadata() { } public Uni getJsonWebKeySet(OidcRequestContextProperties contextProperties) { - return filter(OidcEndpoint.Type.JWKS, client.getAbs(metadata.getJsonWebKeySetUri()), null, contextProperties).send() + return OidcCommonUtils + .sendRequest(vertx, + filter(OidcEndpoint.Type.JWKS, client.getAbs(metadata.getJsonWebKeySetUri()), null, contextProperties), + oidcConfig.useBlockingDnsLookup) .onItem() .transform(resp -> getJsonWebKeySet(resp)); } public Uni getUserInfo(String token) { LOG.debugf("Get UserInfo on: %s auth: %s", metadata.getUserInfoUri(), OidcConstants.BEARER_SCHEME + " " + token); - return filter(OidcEndpoint.Type.USERINFO, client.getAbs(metadata.getUserInfoUri()), null, null) - .putHeader(AUTHORIZATION_HEADER, OidcConstants.BEARER_SCHEME + " " + token) - .send().onItem().transform(resp -> getUserInfo(resp)); + return OidcCommonUtils + .sendRequest(vertx, + filter(OidcEndpoint.Type.USERINFO, client.getAbs(metadata.getUserInfoUri()), null, null) + .putHeader(AUTHORIZATION_HEADER, OidcConstants.BEARER_SCHEME + " " + token), + oidcConfig.useBlockingDnsLookup) + .onItem().transform(resp -> getUserInfo(resp)); } public Uni introspectToken(String token) { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index 376419f0758bd..71fc623600191 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -470,8 +470,8 @@ protected static Uni createOidcClientUni(OidcTenantConfig oi WebClientOptions options = new WebClientOptions(); OidcCommonUtils.setHttpClientOptions(oidcConfig, tlsConfig, options); - - WebClient client = WebClient.create(new io.vertx.mutiny.core.Vertx(vertx), options); + var mutinyVertx = new io.vertx.mutiny.core.Vertx(vertx); + WebClient client = WebClient.create(mutinyVertx, options); Map> oidcRequestFilters = OidcCommonUtils.getOidcRequestFilters(); @@ -481,7 +481,8 @@ protected static Uni createOidcClientUni(OidcTenantConfig oi } else { final long connectionDelayInMillisecs = OidcCommonUtils.getConnectionDelayInMillis(oidcConfig); metadataUni = OidcCommonUtils - .discoverMetadata(client, oidcRequestFilters, authServerUriString, connectionDelayInMillisecs) + .discoverMetadata(client, oidcRequestFilters, authServerUriString, connectionDelayInMillisecs, mutinyVertx, + oidcConfig.useBlockingDnsLookup) .onItem() .transform(new Function() { @Override From 4a53a8b6cf948380850eba80977ca488c46a2724 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Wed, 20 Dec 2023 14:01:39 -0600 Subject: [PATCH 10/34] Do not fail UPX if compression level is not given (cherry picked from commit d2b25c859ce4a648cccdbfdf6fbacf8be11011e1) --- .../pkg/steps/UpxCompressionBuildStep.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java index 7ab2f19910dd1..237e18cafa344 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -15,7 +16,6 @@ import org.jboss.logging.Logger; import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.pkg.NativeConfig; import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem; import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem; @@ -34,7 +34,6 @@ public class UpxCompressionBuildStep { */ private static final String PATH = "PATH"; - @BuildStep(onlyIf = NativeBuild.class) public void compress(NativeConfig nativeConfig, NativeImageRunnerBuildItem nativeImageRunner, NativeImageBuildItem image, BuildProducer upxCompressedProducer, @@ -70,11 +69,13 @@ public void compress(NativeConfig nativeConfig, NativeImageRunnerBuildItem nativ } private boolean runUpxFromHost(File upx, File executable, NativeConfig nativeConfig) { - String level = getCompressionLevel(nativeConfig.compression().level().getAsInt()); List extraArgs = nativeConfig.compression().additionalArgs().orElse(Collections.emptyList()); - List args = Stream.concat( - Stream.concat(Stream.of(upx.getAbsolutePath(), level), extraArgs.stream()), + List args = Stream.of( + Stream.of(upx.getAbsolutePath()), + nativeConfig.compression().level().stream().mapToObj(this::getCompressionLevel), + extraArgs.stream(), Stream.of(executable.getAbsolutePath())) + .flatMap(Function.identity()) .collect(Collectors.toList()); log.infof("Executing %s", String.join(" ", args)); final ProcessBuilder processBuilder = new ProcessBuilder(args) @@ -104,7 +105,6 @@ private boolean runUpxFromHost(File upx, File executable, NativeConfig nativeCon private boolean runUpxInContainer(NativeImageBuildItem nativeImage, NativeConfig nativeConfig, String effectiveBuilderImage) { - String level = getCompressionLevel(nativeConfig.compression().level().getAsInt()); List extraArgs = nativeConfig.compression().additionalArgs().orElse(Collections.emptyList()); List commandLine = new ArrayList<>(); @@ -140,7 +140,9 @@ private boolean runUpxInContainer(NativeImageBuildItem nativeImage, NativeConfig volumeOutputPath + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH + ":z"); commandLine.add(effectiveBuilderImage); - commandLine.add(level); + if (nativeConfig.compression().level().isPresent()) { + commandLine.add(getCompressionLevel(nativeConfig.compression().level().getAsInt())); + } commandLine.addAll(extraArgs); commandLine.add(nativeImage.getPath().toFile().getName()); From bda50872b2f1396437704d6748ec8e733c9ccf3e Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Thu, 29 Feb 2024 18:48:08 +0100 Subject: [PATCH 11/34] Fix cross-references in the Vert.x Reference Guide The simple `xref:file.adoc` macros were not being processed. (cherry picked from commit 9f48ab5da22a3eca888f15476bc5658b7619becd) --- docs/src/main/asciidoc/vertx-reference.adoc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/main/asciidoc/vertx-reference.adoc b/docs/src/main/asciidoc/vertx-reference.adoc index b7956de6f443b..4db1a5f7b2b05 100644 --- a/docs/src/main/asciidoc/vertx-reference.adoc +++ b/docs/src/main/asciidoc/vertx-reference.adoc @@ -83,7 +83,7 @@ Check the associated documentation to learn how to use them. |AMQP Client |`io.quarkus:quarkus-smallrye-reactive-messaging-amqp` (extension) -|xref:amqp.adoc +|xref:amqp.adoc[Getting Started to SmallRye Reactive Messaging with AMQP] |Circuit Breaker |`io.smallrye.reactive:smallrye-mutiny-vertx-circuit-breaker` (external dependency) @@ -95,15 +95,15 @@ Check the associated documentation to learn how to use them. |DB2 Client |`io.quarkus:quarkus-reactive-db2-client` (extension) -|xref:reactive-sql-clients.adoc +|xref:reactive-sql-clients.adoc[Reactive SQL Clients] |Kafka Client |`io.quarkus:quarkus-smallrye-reactive-messaging-kafka` (extension) -|xref:kafka.adoc +|xref:kafka.adoc[Apache Kafka Reference Guide] |Mail Client |`io.quarkus:quarkus-mailer` (extension) -|xref:mailer.adoc +|xref:mailer.adoc[Sending emails using SMTP] |MQTT Client |`io.quarkus:quarkus-smallrye-reactive-messaging-mqtt` (extension) @@ -111,19 +111,19 @@ Check the associated documentation to learn how to use them. |MS SQL Client |`io.quarkus:quarkus-reactive-mssql-client` (extension) -|xref:reactive-sql-clients.adoc +|xref:reactive-sql-clients.adoc[Reactive SQL Clients] |MySQL Client |`io.quarkus:quarkus-reactive-mysql-client` (extension) -|xref:reactive-sql-clients.adoc +|xref:reactive-sql-clients.adoc[Reactive SQL Clients] |Oracle Client |`io.quarkus:quarkus-reactive-oracle-client` (extension) -|xref:reactive-sql-clients.adoc +|xref:reactive-sql-clients.adoc[Reactive SQL Clients] |PostgreSQL Client |`io.quarkus:quarkus-reactive-pg-client` (extension) -|xref:reactive-sql-clients.adoc +|xref:reactive-sql-clients.adoc[Reactive SQL Clients] |RabbitMQ Client |`io.smallrye.reactive:smallrye-mutiny-vertx-rabbitmq-client` (external dependency) @@ -131,7 +131,7 @@ Check the associated documentation to learn how to use them. |Redis Client |`io.quarkus:quarkus-redis-client` (extension) -|xref:redis.adoc +|xref:redis.adoc[Using the Redis Client] |Web Client |`io.smallrye.reactive:smallrye-mutiny-vertx-web-client` (external dependency) From b2a38714827c87ccb94ca77d2858825f7e4f19c0 Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Thu, 29 Feb 2024 18:58:20 +0100 Subject: [PATCH 12/34] Fix snippet format (cherry picked from commit a3bc395fbb5810c5043a9171d186c192c1804df8) --- docs/src/main/asciidoc/vertx-reference.adoc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/src/main/asciidoc/vertx-reference.adoc b/docs/src/main/asciidoc/vertx-reference.adoc index 4db1a5f7b2b05..0d940abd968b8 100644 --- a/docs/src/main/asciidoc/vertx-reference.adoc +++ b/docs/src/main/asciidoc/vertx-reference.adoc @@ -27,13 +27,14 @@ With this extension, you can retrieve the managed instance of Vert.x using eithe ---- @ApplicationScoped public class MyBean { -// Field injection -@Inject Vertx vertx; -// Constructor injection -MyBean(Vertx vertx) { - // ... -} + // Field injection + @Inject Vertx vertx; + + // Constructor injection + MyBean(Vertx vertx) { + // ... + } } ---- From 9409c29ec336e50590e112018ba87501e5d3a116 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Thu, 29 Feb 2024 10:22:20 +0100 Subject: [PATCH 13/34] Unwrap actual GraphQL data fetching exception if it is wrapped (cherry picked from commit 1002624a1cfdad61f86460a0e9a9960a69faab7d) --- .../graphql/deployment/GraphQLTest.java | 66 +++++++++++++++++++ .../graphql/deployment/TestResource.java | 27 ++++++++ .../datafetcher/AbstractAsyncDataFetcher.java | 4 ++ 3 files changed, 97 insertions(+) diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java index d737d6e94326b..7c96557d16d20 100644 --- a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java @@ -1,5 +1,7 @@ package io.quarkus.smallrye.graphql.deployment; +import static org.hamcrest.CoreMatchers.is; + import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; @@ -215,6 +217,70 @@ public void testGenerics() { "{\"data\":{\"generics\":{\"message\":\"I know it\"}}}")); } + @Test + public void testFailureUniNonBlocking() { + String request = getPayload("{failureUniNonBlocking}"); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("errors[0].extensions.classification", is("DataFetchingException")) + .body("errors[0].message", is("boom")); + } + + @Test + public void testFailureUniBlocking() { + String request = getPayload("{failureUniBlocking}"); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("errors[0].extensions.classification", is("DataFetchingException")) + .body("errors[0].message", is("boom")); + } + + @Test + public void testFailureSyncNonBlocking() { + String request = getPayload("{failureSyncNonBlocking}"); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("errors[0].extensions.classification", is("DataFetchingException")) + .body("errors[0].message", is("boom")); + } + + @Test + public void testFailureSyncBlocking() { + String request = getPayload("{failureSyncBlocking}"); + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(request) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body("errors[0].extensions.classification", is("DataFetchingException")) + .body("errors[0].message", is("boom")); + } + /** * Send a query in JSON that contains raw unescaped line breaks and tabs inside the "query" string, * which technically is forbidden by the JSON spec, but we want to seamlessly support diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/TestResource.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/TestResource.java index be4dd0dbb1fcd..1094d6bdbe5a9 100644 --- a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/TestResource.java +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/TestResource.java @@ -10,7 +10,10 @@ import graphql.schema.GraphQLEnumType; import graphql.schema.GraphQLSchema; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.common.annotation.NonBlocking; import io.smallrye.graphql.api.Context; +import io.smallrye.mutiny.Uni; /** * Just a test endpoint @@ -51,6 +54,30 @@ public TestPojo businesserror() throws BusinessException { throw new BusinessException("Some invalid case"); } + @Query + @NonBlocking + public Uni failureUniNonBlocking() { + return Uni.createFrom().failure(new BusinessException("boom")); + } + + @Query + @Blocking + public Uni failureUniBlocking() { + return Uni.createFrom().failure(new BusinessException("boom")); + } + + @Query + @NonBlocking + public String failureSyncNonBlocking() throws BusinessException { + throw new BusinessException("boom"); + } + + @Query + @Blocking + public String failureSyncBlocking() throws BusinessException { + throw new BusinessException("boom"); + } + @Query public TestPojo systemserror() { throw new RuntimeException("Some system problem"); diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java index e443449ff4580..f5df3abed7ba7 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; import jakarta.validation.ConstraintViolationException; @@ -49,6 +50,9 @@ protected O invokeAndTransform( deactivate(requestContext); }); if (throwable != null) { + if (throwable instanceof ExecutionException && throwable.getCause() != null) { + throwable = throwable.getCause(); + } eventEmitter.fireOnDataFetchError(c, throwable); if (throwable instanceof GraphQLException) { GraphQLException graphQLException = (GraphQLException) throwable; From 87d98a8a688a2151504f13790f9304a353a5dab9 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Fri, 23 Feb 2024 23:48:07 +0000 Subject: [PATCH 14/34] Do not expand configuration for Gradle cache (cherry picked from commit 11bd532b05d3bf17c4d9045a757b2b67f0ea4e4c) --- .../extension/QuarkusPluginExtension.java | 2 +- .../tasks/AbstractQuarkusExtension.java | 35 ++++++++++++++++++- .../io/quarkus/gradle/tasks/BaseConfig.java | 8 ++--- .../java/io/quarkus/gradle/tasks/Deploy.java | 3 +- .../quarkus/gradle/tasks/EffectiveConfig.java | 34 ++++++++++-------- .../tasks/QuarkusBuildDependencies.java | 20 ++++++----- .../gradle/tasks/QuarkusBuildTask.java | 22 ++++++------ .../gradle/tasks/QuarkusGenerateCode.java | 6 ++-- .../io/quarkus/gradle/tasks/QuarkusRun.java | 3 +- .../tasks/QuarkusShowEffectiveConfig.java | 22 ++++++------ .../gradle/tasks/EffectiveConfigTest.java | 18 +++++----- 11 files changed, 106 insertions(+), 67 deletions(-) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java index 1a6c34842d24c..718a53f8798f9 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java @@ -73,7 +73,7 @@ public void beforeTest(Test task) { Map props = task.getSystemProperties(); ApplicationModel appModel = getApplicationModel(TEST); - SmallRyeConfig config = buildEffectiveConfiguration(appModel.getAppArtifact()).config(); + SmallRyeConfig config = buildEffectiveConfiguration(appModel.getAppArtifact()).getConfig(); config.getOptionalValue(TEST.getProfileKey(), String.class) .ifPresent(value -> props.put(TEST.getProfileKey(), value)); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java index f2696a9266f23..85f67e430dfc1 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java @@ -1,12 +1,17 @@ package io.quarkus.gradle.tasks; import static io.quarkus.gradle.tasks.QuarkusGradleUtils.getSourceSet; +import static io.smallrye.common.expression.Expression.Flag.DOUBLE_COLON; +import static io.smallrye.common.expression.Expression.Flag.LENIENT_SYNTAX; +import static io.smallrye.common.expression.Expression.Flag.NO_SMART_BRACES; +import static io.smallrye.common.expression.Expression.Flag.NO_TRIM; import static java.util.Collections.emptyList; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -24,6 +29,7 @@ import io.quarkus.gradle.dsl.Manifest; import io.quarkus.maven.dependency.ResolvedDependency; +import io.smallrye.common.expression.Expression; /** * This base class exists to hide internal properties, make those only available in the {@link io.quarkus.gradle.tasks} @@ -138,7 +144,7 @@ private EffectiveConfig buildEffectiveConfiguration(Map properti * @param appArtifact the application dependency to retrive the quarkus application name and version. * @return a filtered view of the configuration only with quarkus. names. */ - protected Map buildSystemProperties(ResolvedDependency appArtifact) { + protected Map buildSystemProperties(ResolvedDependency appArtifact, Map quarkusProperties) { Map buildSystemProperties = new HashMap<>(); buildSystemProperties.putIfAbsent("quarkus.application.name", appArtifact.getArtifactId()); buildSystemProperties.putIfAbsent("quarkus.application.version", appArtifact.getVersion()); @@ -158,6 +164,33 @@ protected Map buildSystemProperties(ResolvedDependency appArtifa buildSystemProperties.put(entry.getKey(), entry.getValue().toString()); } } + + Set quarkusValues = new HashSet<>(); + quarkusValues.addAll(quarkusProperties.values()); + quarkusValues.addAll(buildSystemProperties.values()); + + for (String value : quarkusValues) { + Expression expression = Expression.compile(value, LENIENT_SYNTAX, NO_TRIM, NO_SMART_BRACES, DOUBLE_COLON); + for (String reference : expression.getReferencedStrings()) { + String expanded = forcedPropertiesProperty.get().get(reference); + if (expanded != null) { + buildSystemProperties.put(reference, expanded); + continue; + } + + expanded = quarkusBuildProperties.get().get(reference); + if (expanded != null) { + buildSystemProperties.put(reference, expanded); + continue; + } + + expanded = (String) project.getProperties().get(reference); + if (expanded != null) { + buildSystemProperties.put(reference, expanded); + } + } + } + return buildSystemProperties; } diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/BaseConfig.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/BaseConfig.java index 90c229c043b9b..5e0d9533e92dc 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/BaseConfig.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/BaseConfig.java @@ -21,20 +21,20 @@ final class BaseConfig { private final Manifest manifest; private final PackageConfig packageConfig; - private final Map configMap; + private final Map values; // Note: EffectiveConfig has all the code to load the configurations from all the sources. BaseConfig(EffectiveConfig config) { manifest = new Manifest(); packageConfig = new PackageConfig(); - ConfigInstantiator.handleObject(packageConfig, config.config()); + ConfigInstantiator.handleObject(packageConfig, config.getConfig()); // populate the Gradle Manifest object manifest.attributes(packageConfig.manifest.attributes); packageConfig.manifest.manifestSections.forEach((section, attribs) -> manifest.attributes(attribs, section)); - configMap = config.configMap(); + values = config.getValues(); } PackageConfig packageConfig() { @@ -53,7 +53,7 @@ Map cachingRelevantProperties(List propertyPatterns) { List patterns = propertyPatterns.stream().map(s -> "^(" + s + ")$").map(Pattern::compile) .collect(Collectors.toList()); Predicate> keyPredicate = e -> patterns.stream().anyMatch(p -> p.matcher(e.getKey()).matches()); - return configMap.entrySet().stream() + return values.entrySet().stream() .filter(keyPredicate) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java index 132d956279771..d5b13e65e4a06 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java @@ -90,9 +90,8 @@ public Deploy() { @TaskAction public void checkRequiredExtensions() { ApplicationModel appModel = resolveAppModelForBuild(); - Map configMap = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).configMap(); Properties sysProps = new Properties(); - sysProps.putAll(configMap); + sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getValues()); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder() .setBaseClassLoader(getClass().getClassLoader()) .setExistingModel(appModel) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java index 1166d55e410d3..1c67c52ac4637 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSourceProvider; @@ -26,6 +27,7 @@ import io.quarkus.runtime.configuration.ConfigUtils; import io.smallrye.config.AbstractLocationConfigSourceLoader; import io.smallrye.config.EnvConfigSource; +import io.smallrye.config.Expressions; import io.smallrye.config.PropertiesConfigSource; import io.smallrye.config.PropertiesConfigSourceProvider; import io.smallrye.config.SmallRyeConfig; @@ -42,9 +44,8 @@ * the Quarkus config objects like {@link PackageConfig}, {@link ClassLoadingConfig} and the underlying {@link SmallRyeConfig}. */ public final class EffectiveConfig { - private final Map fullConfig; - private final SmallRyeConfig config; + private final Map values; private EffectiveConfig(Builder builder) { List configSources = new ArrayList<>(); @@ -81,13 +82,17 @@ private EffectiveConfig(Builder builder) { .addAll(PropertiesConfigSourceProvider.classPathSources(META_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader)); this.config = buildConfig(builder.profile, configSources); - this.fullConfig = generateFullConfigMap(config); + this.values = generateFullConfigMap(config); } - public SmallRyeConfig config() { + public SmallRyeConfig getConfig() { return config; } + public Map getValues() { + return values; + } + private Map asStringMap(Map map) { Map target = new HashMap<>(); map.forEach((k, v) -> { @@ -100,14 +105,19 @@ private Map asStringMap(Map map) { @VisibleForTesting static Map generateFullConfigMap(SmallRyeConfig config) { - Map map = new HashMap<>(); - config.getPropertyNames().forEach(property -> { - String v = config.getConfigValue(property).getValue(); - if (v != null) { - map.put(property, v); + return Expressions.withoutExpansion(new Supplier>() { + @Override + public Map get() { + Map properties = new HashMap<>(); + for (String propertyName : config.getPropertyNames()) { + String value = config.getRawValue(propertyName); + if (value != null) { + properties.put(propertyName, value); + } + } + return unmodifiableMap(properties); } }); - return unmodifiableMap(map); } @VisibleForTesting @@ -126,10 +136,6 @@ static Builder builder() { return new Builder(); } - public Map configMap() { - return fullConfig; - } - static final class Builder { private Map buildProperties = emptyMap(); private Map projectProperties = emptyMap(); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java index a410e1af41270..d18dd3c70636f 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java @@ -24,6 +24,7 @@ import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.DependencyFlags; import io.quarkus.maven.dependency.ResolvedDependency; +import io.smallrye.config.SmallRyeConfig; /** * Collect the Quarkus app dependencies, the contents of the {@code quarkus-app/lib} folder, without making the task @@ -139,22 +140,23 @@ private void jarDependencies(Path libBoot, Path libMain) { } ApplicationModel appModel = resolveAppModelForBuild(); - Map configMap = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).configMap(); + SmallRyeConfig config = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getConfig(); // see https://quarkus.io/guides/class-loading-reference#configuring-class-loading - Set removedArtifacts = java.util.Optional.ofNullable( - configMap.getOrDefault(CLASS_LOADING_REMOVED_ARTIFACTS, null)) + Set removedArtifacts = config.getOptionalValue(CLASS_LOADING_REMOVED_ARTIFACTS, String.class) .map(QuarkusBuildDependencies::dependenciesListToArtifactKeySet) .orElse(Collections.emptySet()); - getLogger().info("Removed artifacts: {}", configMap.getOrDefault(CLASS_LOADING_REMOVED_ARTIFACTS, "(none)")); + getLogger().info("Removed artifacts: {}", + config.getOptionalValue(CLASS_LOADING_REMOVED_ARTIFACTS, String.class).orElse("(none)")); - String parentFirstArtifactsProp = configMap.getOrDefault(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, ""); + String parentFirstArtifactsProp = config.getOptionalValue(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, String.class) + .orElse(""); Set parentFirstArtifacts = dependenciesListToArtifactKeySet(parentFirstArtifactsProp); - getLogger().info("parent first artifacts: {}", configMap.getOrDefault(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, "(none)")); + getLogger().info("parent first artifacts: {}", + config.getOptionalValue(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, String.class).orElse("(none)")); - String optionalDependenciesProp = configMap.getOrDefault(INCLUDED_OPTIONAL_DEPENDENCIES, ""); - boolean filterOptionalDependencies = Boolean - .parseBoolean(configMap.getOrDefault(FILTER_OPTIONAL_DEPENDENCIES, "false")); + String optionalDependenciesProp = config.getOptionalValue(INCLUDED_OPTIONAL_DEPENDENCIES, String.class).orElse(""); + boolean filterOptionalDependencies = config.getOptionalValue(FILTER_OPTIONAL_DEPENDENCIES, Boolean.class).orElse(false); Set optionalDependencies = filterOptionalDependencies ? dependenciesListToArtifactKeySet(optionalDependenciesProp) : Collections.emptySet(); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java index 88196dbba173a..827ace86ae845 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java @@ -29,6 +29,7 @@ import io.quarkus.gradle.tasks.worker.BuildWorker; import io.quarkus.maven.dependency.GACTV; import io.smallrye.config.Expressions; +import io.smallrye.config.SmallRyeConfig; /** * Base class for the {@link QuarkusBuildDependencies}, {@link QuarkusBuildCacheableAppParts}, {@link QuarkusBuild} tasks @@ -207,30 +208,29 @@ void generateBuild() { }); ApplicationModel appModel = resolveAppModelForBuild(); - Map configMap = new HashMap<>(); - EffectiveConfig effectiveConfig = extension().buildEffectiveConfiguration(appModel.getAppArtifact()); - Expressions.withoutExpansion(() -> { - for (Map.Entry entry : effectiveConfig.configMap().entrySet()) { - if (entry.getKey().startsWith("quarkus.")) { - configMap.put(entry.getKey(), effectiveConfig.config().getRawValue(entry.getKey())); - } - } + SmallRyeConfig config = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getConfig(); + Map quarkusProperties = Expressions.withoutExpansion(() -> { + Map values = new HashMap<>(); + config.getValues("quarkus", String.class, String.class) + .forEach((key, value) -> values.put("quarkus." + key, value)); + return values; }); getLogger().info("Starting Quarkus application build for package type {}", packageType); if (getLogger().isEnabled(LogLevel.INFO)) { getLogger().info("Effective properties: {}", - configMap.entrySet().stream() + quarkusProperties.entrySet().stream() .map(Object::toString) .sorted() .collect(Collectors.joining("\n ", "\n ", ""))); } - WorkQueue workQueue = workQueue(configMap, () -> extension().buildForkOptions); + WorkQueue workQueue = workQueue(quarkusProperties, () -> extension().buildForkOptions); workQueue.submit(BuildWorker.class, params -> { - params.getBuildSystemProperties().putAll(extension().buildSystemProperties(appModel.getAppArtifact())); + params.getBuildSystemProperties() + .putAll(extension().buildSystemProperties(appModel.getAppArtifact(), quarkusProperties)); params.getBaseName().set(extension().finalName()); params.getTargetDirectory().set(buildDir.toFile()); params.getAppModel().set(appModel); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java index b01ffdb6732f0..1b1cc049c8bd5 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java @@ -102,17 +102,17 @@ public Set getInputDirectory() { @TaskAction public void generateCode() { ApplicationModel appModel = extension().getApplicationModel(launchMode); - Map configMap = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).configMap(); + Map values = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getValues(); File outputPath = getGeneratedOutputDirectory().get().getAsFile(); getLogger().debug("Will trigger preparing sources for source directories: {} buildDir: {}", sourcesDirectories, buildDir.getAbsolutePath()); - WorkQueue workQueue = workQueue(configMap, () -> extension().codeGenForkOptions); + WorkQueue workQueue = workQueue(values, () -> extension().codeGenForkOptions); workQueue.submit(CodeGenWorker.class, params -> { - params.getBuildSystemProperties().putAll(configMap); + params.getBuildSystemProperties().putAll(values); params.getBaseName().set(extension().finalName()); params.getTargetDirectory().set(buildDir); params.getAppModel().set(appModel); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java index ea37bcb6875d2..5935f9d46caea 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java @@ -100,9 +100,8 @@ public void setJvmArgs(List jvmArgs) { @TaskAction public void runQuarkus() { ApplicationModel appModel = resolveAppModelForBuild(); - Map configMap = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).configMap(); Properties sysProps = new Properties(); - sysProps.putAll(configMap); + sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getValues()); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder() .setBaseClassLoader(getClass().getClassLoader()) .setExistingModel(appModel) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java index cd37b8b1aad86..2115ee6ee6ad6 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java @@ -9,7 +9,6 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; @@ -22,6 +21,7 @@ import org.gradle.api.tasks.options.Option; import io.quarkus.gradle.QuarkusPlugin; +import io.smallrye.config.SmallRyeConfig; /** * Just show the effective configuration and settings. @@ -45,21 +45,21 @@ public Property getSaveConfigProperties() { @TaskAction public void dumpEffectiveConfiguration() { try { - EffectiveConfig effective = extension() + EffectiveConfig effectiveConfig = extension() .buildEffectiveConfiguration(extension().getApplicationModel().getAppArtifact()); - Map configMap = effective.configMap(); + SmallRyeConfig config = effectiveConfig.getConfig(); List sourceNames = new ArrayList<>(); - effective.config().getConfigSources().forEach(configSource -> sourceNames.add(configSource.getName())); + config.getConfigSources().forEach(configSource -> sourceNames.add(configSource.getName())); - String config = configMap.entrySet().stream() - .filter(e -> e.getKey().startsWith("quarkus.")) - .map(e -> format("%s=%s", e.getKey(), e.getValue())).sorted() + String quarkusConfig = config.getValues("quarkus", String.class, String.class) + .entrySet() + .stream() + .map(e -> format("quarkus.%s=%s", e.getKey(), e.getValue())).sorted() .collect(Collectors.joining("\n ", "\n ", "\n")); - - getLogger().lifecycle("Effective Quarkus configuration options: {}", config); + getLogger().lifecycle("Effective Quarkus configuration options: {}", quarkusConfig); String finalName = extension().finalName(); - String packageType = configMap.getOrDefault(QuarkusPlugin.QUARKUS_PACKAGE_TYPE, "fast-jar"); + String packageType = config.getOptionalValue(QuarkusPlugin.QUARKUS_PACKAGE_TYPE, String.class).orElse("fast-jar"); File fastJar = fastJar(); getLogger().lifecycle( "Quarkus package type: {}\n" + @@ -79,7 +79,7 @@ public void dumpEffectiveConfiguration() { if (getSaveConfigProperties().get()) { Properties props = new Properties(); - props.putAll(configMap); + props.putAll(effectiveConfig.getValues()); Path file = buildDir.toPath().resolve(finalName + ".quarkus-build.properties"); try (BufferedWriter writer = newBufferedWriter(file)) { props.store(writer, format("Quarkus build properties with package type %s", packageType)); diff --git a/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/tasks/EffectiveConfigTest.java b/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/tasks/EffectiveConfigTest.java index ad239fe7fc39f..443c8695741b3 100644 --- a/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/tasks/EffectiveConfigTest.java +++ b/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/tasks/EffectiveConfigTest.java @@ -32,21 +32,21 @@ void empty() { // Cannot do an exact match, because `map` contains both the "raw" environment variables AND the // "property-key-ish" entries - i.e. environment appears "twice". - soft.assertThat(effectiveConfig.configMap()).containsAllEntriesOf(expect); + soft.assertThat(effectiveConfig.getValues()).containsAllEntriesOf(expect); } @Test void fromProjectProperties() { EffectiveConfig effectiveConfig = EffectiveConfig.builder().withProjectProperties(Map.of("quarkus.foo", "bar")).build(); - soft.assertThat(effectiveConfig.configMap()).containsEntry("quarkus.foo", "bar"); + soft.assertThat(effectiveConfig.getValues()).containsEntry("quarkus.foo", "bar"); } @Test void fromForcedProperties() { EffectiveConfig effectiveConfig = EffectiveConfig.builder().withTaskProperties(Map.of("quarkus.foo", "bar")).build(); - soft.assertThat(effectiveConfig.configMap()).containsEntry("quarkus.foo", "bar"); + soft.assertThat(effectiveConfig.getValues()).containsEntry("quarkus.foo", "bar"); } @Test @@ -59,13 +59,13 @@ void appPropsOverload() throws Exception { EffectiveConfig effectiveConfig = EffectiveConfig.builder().withSourceDirectories(source).build(); - SmallRyeConfig config = effectiveConfig.config(); + SmallRyeConfig config = effectiveConfig.getConfig(); List sourceNames = new ArrayList<>(); config.getConfigSources().forEach(configSource -> sourceNames.add(configSource.getName())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url1.getPath())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url2.getPath())); // The YAML source is always higher in ordinal than the properties source - soft.assertThat(effectiveConfig.configMap()).containsEntry("quarkus.prop.overload", "from-yaml"); + soft.assertThat(effectiveConfig.getValues()).containsEntry("quarkus.prop.overload", "from-yaml"); } @Test @@ -80,14 +80,14 @@ void appPropsOverloadWrongProfile() throws Exception { EffectiveConfig effectiveConfig = EffectiveConfig.builder().withSourceDirectories(source).build(); - SmallRyeConfig config = effectiveConfig.config(); + SmallRyeConfig config = effectiveConfig.getConfig(); List sourceNames = new ArrayList<>(); config.getConfigSources().forEach(configSource -> sourceNames.add(configSource.getName())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url1.getPath())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url2.getPath())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url3.getPath())); // The YAML source is always higher in ordinal than the properties source - soft.assertThat(effectiveConfig.configMap()).containsEntry("quarkus.prop.overload", "from-yaml"); + soft.assertThat(effectiveConfig.getValues()).containsEntry("quarkus.prop.overload", "from-yaml"); } @Test @@ -106,7 +106,7 @@ void appPropsOverloadProdProfile() throws Exception { EffectiveConfig effectiveConfig = EffectiveConfig.builder().withSourceDirectories(source).build(); - SmallRyeConfig config = effectiveConfig.config(); + SmallRyeConfig config = effectiveConfig.getConfig(); List sourceNames = new ArrayList<>(); config.getConfigSources().forEach(configSource -> sourceNames.add(configSource.getName())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url1.getPath())); @@ -115,6 +115,6 @@ void appPropsOverloadProdProfile() throws Exception { soft.assertThat(sourceNames).anyMatch(s -> s.contains(url4.getPath())); soft.assertThat(sourceNames).anyMatch(s -> s.contains(url5.getPath())); // The YAML source is always higher in ordinal than the properties source, even for profile property names - soft.assertThat(effectiveConfig.configMap()).containsEntry("quarkus.prop.overload", "from-yaml-prod"); + soft.assertThat(effectiveConfig.getValues()).containsEntry("quarkus.prop.overload", "from-yaml-prod"); } } From 885065aa8038673a804447f777eec56d2e466fb2 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 28 Feb 2024 16:11:17 +0000 Subject: [PATCH 15/34] Fix the OIDC token verification failure with the inlined cert chain (cherry picked from commit 105fc0069ad670dc2a65d29894f5e5d7509130fa) --- .../io/quarkus/oidc/OidcTenantConfig.java | 28 +++++++- .../runtime/CertChainPublicKeyResolver.java | 32 ++++++++- .../runtime/X509IdentityProvider.java | 2 +- .../io/quarkus/it/keycloak/AdminResource.java | 8 +++ .../src/main/resources/application.properties | 12 ++-- .../main/resources/truststore-rootcert.p12 | Bin 0 -> 1862 bytes .../src/main/resources/truststore.p12 | Bin 1638 -> 3238 bytes .../BearerTokenAuthorizationTest.java | 66 ++++++++++++++++-- 8 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 integration-tests/oidc-wiremock/src/main/resources/truststore-rootcert.p12 diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java index 4c8cdfc3beb36..a952de400ca48 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java @@ -176,14 +176,32 @@ public void setIncludeClientId(boolean includeClientId) { /** * Configuration of the certificate chain which can be used to verify tokens. - * If the certificate chain trusstore is configured, the tokens can be verified using the certificate + * If the certificate chain truststore is configured, the tokens can be verified using the certificate * chain inlined in the Base64-encoded format as an `x5c` header in the token itself. + *

+ * The certificate chain inlined in the token is verified. + * Signature of every certificate in the chain but the root certificate is verified by the next certificate in the chain. + * Thumbprint of the root certificate in the chain must match a thumbprint of one of the certificates in the truststore. + *

+ * Additionally, a direct trust in the leaf chain certificate which will be used to verify the token signature must + * be established. + * By default, the leaf certificate's thumbprint must match a thumbprint of one of the certificates in the truststore. + * If the truststore does not have the leaf certificate imported, then the leaf certificate must be identified by its Common + * Name. */ @ConfigItem public CertificateChain certificateChain = new CertificateChain(); @ConfigGroup public static class CertificateChain { + /** + * Common name of the leaf certificate. It must be set if the {@link #trustStoreFile} does not have + * this certificate imported. + * + */ + @ConfigItem + public Optional leafCertificateName = Optional.empty(); + /** * Truststore file which keeps thumbprints of the trusted certificates. */ @@ -233,6 +251,14 @@ public Optional getTrustStoreFileType() { public void setTrustStoreFileType(Optional trustStoreFileType) { this.trustStoreFileType = trustStoreFileType; } + + public Optional getLeafCertificateName() { + return leafCertificateName; + } + + public void setLeafCertificateName(String leafCertificateName) { + this.leafCertificateName = Optional.of(leafCertificateName); + } } /** diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java index ae0105fce3bcf..069ad2efb7704 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CertChainPublicKeyResolver.java @@ -3,6 +3,7 @@ import java.security.Key; import java.security.cert.X509Certificate; import java.util.List; +import java.util.Optional; import java.util.Set; import org.jboss.logging.Logger; @@ -12,11 +13,13 @@ import io.quarkus.oidc.OidcTenantConfig.CertificateChain; import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.security.runtime.X509IdentityProvider; import io.vertx.ext.auth.impl.CertificateHelper; public class CertChainPublicKeyResolver implements RefreshableVerificationKeyResolver { private static final Logger LOG = Logger.getLogger(OidcProvider.class); final Set thumbprints; + final Optional expectedLeafCertificateName; public CertChainPublicKeyResolver(CertificateChain chain) { if (chain.trustStorePassword.isEmpty()) { @@ -25,6 +28,7 @@ public CertChainPublicKeyResolver(CertificateChain chain) { } this.thumbprints = TrustStoreUtils.getTrustedCertificateThumbprints(chain.trustStoreFile.get(), chain.trustStorePassword.get(), chain.trustStoreCertAlias, chain.getTrustStoreFileType()); + this.expectedLeafCertificateName = chain.leafCertificateName; } @Override @@ -37,9 +41,29 @@ public Key resolveKey(JsonWebSignature jws, List nestingContex LOG.debug("Token does not have an 'x5c' certificate chain header"); return null; } - String thumbprint = TrustStoreUtils.calculateThumprint(chain.get(0)); - if (!thumbprints.contains(thumbprint)) { - throw new UnresolvableKeyException("Certificate chain thumprint is invalid"); + if (chain.size() == 0) { + LOG.debug("Token 'x5c' certificate chain is empty"); + return null; + } + LOG.debug("Checking a thumbprint of the root chain certificate"); + String rootThumbprint = TrustStoreUtils.calculateThumprint(chain.get(chain.size() - 1)); + if (!thumbprints.contains(rootThumbprint)) { + LOG.error("Thumprint of the root chain certificate is invalid"); + throw new UnresolvableKeyException("Thumprint of the root chain certificate is invalid"); + } + if (expectedLeafCertificateName.isEmpty()) { + LOG.debug("Checking a thumbprint of the leaf chain certificate"); + String thumbprint = TrustStoreUtils.calculateThumprint(chain.get(0)); + if (!thumbprints.contains(thumbprint)) { + LOG.error("Thumprint of the leaf chain certificate is invalid"); + throw new UnresolvableKeyException("Thumprint of the leaf chain certificate is invalid"); + } + } else { + String leafCertificateName = X509IdentityProvider.getCommonName(chain.get(0).getSubjectX500Principal()); + if (!expectedLeafCertificateName.get().equals(leafCertificateName)) { + LOG.errorf("Wrong leaf certificate common name: %s", leafCertificateName); + throw new UnresolvableKeyException("Wrong leaf certificate common name"); + } } //TODO: support revocation lists CertificateHelper.checkValidity(chain, null); @@ -50,6 +74,8 @@ public Key resolveKey(JsonWebSignature jws, List nestingContex root.verify(root.getPublicKey()); } return chain.get(0).getPublicKey(); + } catch (UnresolvableKeyException ex) { + throw ex; } catch (Exception ex) { throw new UnresolvableKeyException("Invalid certificate chain", ex); } diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java index d7bcff7deb67c..63d79961e261b 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java @@ -60,7 +60,7 @@ private Set extractRoles(X509Certificate certificate, Map&LNQU+thDZTr0|Wso1Q3gq`{dQWc@bB20%|E54N$P+QzS#6<4_SJ8X{N z^QIqSz3_PeGJbvEsINt?>p`bfyt(L#mvQGtN1wG*OS1`~6FT z*+=sLB~J!X#%6@SQG$rib9=|MyseFVrIEAJrT*WtNyBd zc+KPna<_%8YdmAO-rQi>?7JBq+1Odgx zg2!gIcZ2(D?;;QlT#`K41vGP**m4K zv&!3S1P6KCzyvThOKnn`kUzM*fknOk=PXaC>OSDWPpX z2tjKugnA#<(L$WkaV@~wDkcfO6I!n2DF#N&>@3xYbok7xZQmZur=mK*%sHLEy%kCS zYChphDX5ZlfNSdYhsp_V@LgObgUwX6*QoyTvzemWxZJO6`57ub_58O6>IByZRJ<*#!@!(E#ok#YX zcNxACg~_DSmO;tAZ2)q|rYT;2`sKKX?Gc)b zoKra#A*gv(nFXoDoS#wCunoTUE(w|r1(Wa+$cFV)QF3Ic%s|KQRzfkBjN=Sir_~;E z>N3y#B7`V1RBPNz*Nhn-FygYiTE1LM0zO#NB+vY;CVo&UIL$t=+B@_sv*HiC=X9_r z)S=UHiOPAlP0^y=XA?K9K9-&dz&Q293PM!+f>=avLlIT2lL{3dXh##?ZXV~wtrC7C@pC4m0+S9_u zc;;>IvwrZ|#ksofds!Z%=MgRxgj)TY@12e zbvw463b;1Ohy-uBV@7D?&O>cao#!aEEH;Y6MbR(Ac`jqfyXW`FXo0o!4iAlp0{ARh zqp5cis&e>c;af#vM}kz;`6KTMgFZIlRPsUqY2(I%$(WL@^IoC*&Wu(%9RZ5$fE$};f)I74cVZ)JwLG?G3Sli|}C0W)3 zU^L(P*`7J~)-;R_Zr}g|30#FNC9O71OfpC00bb}ni)SqkMA-2 zKt1@jFp48R!rV!`zZ3cq6tZ&=4~0hr6gSQPH-h^YW8?LM;J@nkEt7F+?g9cQ5J6;z AH~;_u literal 0 HcmV?d00001 diff --git a/integration-tests/oidc-wiremock/src/main/resources/truststore.p12 b/integration-tests/oidc-wiremock/src/main/resources/truststore.p12 index 81b0be2ede57e8ec7271b59b475d41bc6789a450..b0c1f8bcb4164b99b5efca0cf4ab719bb66d7210 100644 GIT binary patch delta 3196 zcmV-?41@FL45k@CFoF!C0s#Xsf(%Rs2`Yw2hW8Bt2LYgh3_S#b3^_1@3^g!<3^4`? zDuzgg_YDCD2B3lrA~1ps9s&UXFoFyhkw7aHZBXl6c50+W*iu^*?TPTSl7@{Ck$)tA z)xR%yUhlKrnkB!EP#L6EfPxF4lrAvBR-K^?CC#o)KGky{Zq!`0nR>ILCGW44TRvJQpL z(OunF1|XaT0xzeuHzh1WQ8gPEZMJ=Xi>qzsRc&y8n%AXcQ^E;RBT;dELLYEvFM$hV z;{Y)R9DM_1O{tjJHN*4klJYPWHUbd*jsSjV|-Rcp?F}ayNNJ&^> zH477A`QH17Dv;Idl;Ehq_{QtwqHHyPDa$;@hha*cnK?;$$|iuw70@JqGtZ2= z%Lv`nwDWnFeRV}f9_7vfB&hWrI&x* z1S+fY!l3Cl!ZwA#jCts;8q%g6>>QC-ZN=aIrY7s{o8H%=>!hNmyRm3X|8pcJ?1}Ch zd`vze4L>b$mkOESKm@Bh1&bqp%^ErYNP*H353gP2xOqZ03wqT~N3s>_|3JU!_E6rN z907=Zzzy_60@c;9Vsl`(FwmQIp)TBmPn(mv-RrH#!??;Ths#+KrGvgZa3r#e05)TN zFdPpshsNOif(1b%|0YOs+6^jCyyq|G1dZ0E9}J{Sd%+(>svafnX;^lD33|T`9gZ6R z3*P&+fhZ>2DBZMb0&{h zl8#IAz*`8lq>vXSV6y;!$3ex!Moo?-Nz9Ngw&hcHVwFEH<0OPbF{#;UE{$)lH>ENAvP7O1vWR}wt&PW$_j@T`phX@WNrO24xUJk z(asNYMQZatx947vN40>N$bjXWw@k~)P$bPCC#9tq*80=+ zXFV@)()rk|eYI+cp}yrnpmWykk#LEZUM&$}2y*>9A2iRA@dia1vsJgMQDH^TqEL%s zhhAu&jCnno&z4ewjmqAy#OvtFye`q{8 znZIPr6M{!f3a1^S*(zYvL841Dsa=li<7LZ>g z5@R!5(c*{0Ipg67QzG|(y;xo}U*A8HTaQCGGOCh5Apx&{GX0mB%7OseK>pivRv>OS zNyxxSS1$*7esfx?{*9tK<+cF{BeGx_iAYxLs0c!QUbQQhRv3`QDOB}eFIad*h97?p zK8XhMzJHH6iRFOhsDCi}Xi^pDoN+0ZbWip`@M<(5q^0?m^4Hq-jVRD23oVpX)Ypp5 zf=Hg&?2s^jR0>+uZGo8(7FiUHz@{Kd|9_R~c?_P1r zRYqe|GHbC!v>Wm8zJI!!-o3Jd(Rbvv$jB&6rC>yV15JGVJOVA~*53n`Vqa>M0Frc* zq+-fn^b9qjE^JT}q!^NZ%c94R`DjN>*8FdqRlUGd5}uc{S`DY)tl2paBB3KISid1V z7jN`o`mhU<9p{R>Gaov_5HbDQcX$m8Vnvg~DNfV)xi6|FBR!>V4k9|u(|`bmhe6@% zKBuaGbYsG$AASI2bTpZ3I9ZFi>Cog278&S+*f}{C`URv2vI+f1(~R2Uf@pD=Z5~PW zm+k;p2Pn3O>r78noVKyD1dzhLQkmH|3qfZo@!>rr)w{}tCUMqnDHeG10Xsde!3TXD zXN`ty|CojgR-v=CJ+zsf)=ART)cO*nd}F|Wj~sSFWP@0%DW(#qpJB0kPeLYpYD%iP ze^z(6aysvKJzBuuk5p75Knp`JPYQWGGy@&BdIRgJ>J1G_OIy{qoJ9}8C^g*HJq{nJR zTorrtlwgU`h+~n{R?{YRtrB@S*ohj94EVmh`Yf46(fc5{QwCxbfg0!`Q%U;XKKNGm z{7NtCmgAPcmg+9Aj>ZL%fQg2wlnQHqRyym@24E{NZIs-XWjF7fQkk}AKnsjfI_zCjqY~{+s+S)Uy@I-_!%(IC&fCM=#b@T0dJ)8bVkHeJ zcdkuvl0(seqZjqizL`08-FIaj@|Ej8E|p7hkHh54^{|nuK0t?6e?Jufv|bE(EE0j!w;u{|o5{_Vw5snkA~Kc~P_XYZMf@CZYYBnxaI zZ4ce^hLb~wB3{hD^>!dNw3yEh2*I)2JsfXYW>6KW@0#$&hw7-Bf(3AYa;JoKE0fAr zd$LZ5cM?zEmkpg@tkR^B!D!pb`tS7i4OXaqYmTj(@6ueR$_eLvbr4paSLs^HVE*J% zOCyV|XII9?{Hm!={10=z*Q3>I#B$w0- zg{B}14hZH53F)v>3C=>sLS{LH6m2IG@GtiXPD_b%%=%k%M}Q`OYb1&*cu7@$m71w6 zze10P3%?UbnA;xTntAE9ELc32U?#@D#hz)i3wQ?yC0<3r6yH8pA?fI3!72V2RE2;Z z1rSN>gR1!q4#&ed$KB+B6Vv|7xP|t&>sn%D%4WqBzC^F6qoa`s+&K{MIgmZVUD^?A zAzNM+#@-kUrp85o=*)``!a?h}M4;m1;>@p+H?SdE0zB4}E4v|y*Ov1^QJy}i(Mt1Q z)L6&lDC@YudH{*)#p$YnFgs%BG8-%8AX=0R(`{3D}@kkwL+wNilL zFMj+mfQuAhw=mOK2CXDE=o+(pF=cyz(kQiA_2kH-ED*=lGH1Ucry7vB95hR&R~2Ic zxdBf;YV^|*l?||~zphPPLkKa)y3=g7;{&?Ty_sRVn8Pi#!=jINa^U5bdDc-dv$BpT zwpI|tP$27nlqP)ux*y(Y7eWrxym1z$Q@BzY3@wsYFGw37;)$1c3I(=_Z;#;oRgkR}kt!aBq3P)j?F`5shw_DS=(KNj~7rMP{u7jV03T z**#fz0*QbB=y$rn2bPo`Rg-4suU>OO!O)k$)tA zE#Ir{dP%mq_Db*Fuk_m^fPw{J@5ENK5lXY2HQ!nW*<_Q0g*kOQ0IH32$!ayKit&aS zbl1;F;^DO}JjSIQkW>tfN5=d#;`!(jmzfrm)ocs4Fr+cD`YzEoM6&DvBc-Q+F+1j! zb_ByZ@(kG}mAG8c^K!UJMj#*YY%`*N^KgDTHw$gmhrE=lV!57cGP?%`PB1Kh|1})i z96}6I4z16MEsZ={%2k73l|qjgtqEwpyy$>LJqQBNNlCx$FiPmr0-SuFjP?w%`=6N; zY}uQ%!N7`jX%q1RHNm}d=F^8Ia;}dJ_pjsAG2`H3zfIn%#5y<__3hck^J0*H$~aD= z+!p+boB!iBntuUzxhp{7_g19&nweBPBNsD04k(O3`n69mp^TgV2L9cv-wNWVfzXL6 ze4owY%cq0%{)$4u(Ndce%hsnYzS#zYIm%ZzOH7vV)7~UNUnV`_5G9DPczem*JE{}` zK*A?)FNp>}Oc6u9Rv~Gevyi5L7cMOMFgdrhNg-32H{BduZ)QWoQUcl7D0X)DM-${$ zla@XVl9=*~*KoLSy$;(-?&LjR^+g;TyNyc#Xra|kDOefJ-5j+RhV&tMXdhxIwrc$( z-Ed8Sjv#!sk*(vqh^8`qBDqV2t3f-*aSyEm?8y&$t}1!ibus!s!^Fy! z(I(lOu%awYIcI*&f6}slDp25q)9gWT|2;X`5-9X|QK4vayv$|`Za}5niA8Iuv~Zw$ zxis^5hK3egd9!5>Fn@MF26O3j?|v^5lGLCGoxLJT3vCy6H!!ce|)(yotiNVREyEdJKel?^gYIX6o< z#s5g{kjrgjayi z-m}Cc3+rij+`b!y9s8Vj)PYj3DZjrFf+DR?9xk zIpq#!j%WLHQ9v@BEBDHqfVoc6b_f^xGY)2~AYrQnu;>hbZC4&oR% z+pM=9h1l#Unjo9lOjf2}vMAn!XX z@DI64PkTrT?Dg(R540H_l}09=* Date: Fri, 1 Mar 2024 22:18:06 +0300 Subject: [PATCH 16/34] use bcrypt in docs (cherry picked from commit 366a981ff7d95048da33f752849cb998424b0fe4) --- docs/src/main/asciidoc/security-jdbc.adoc | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/src/main/asciidoc/security-jdbc.adoc b/docs/src/main/asciidoc/security-jdbc.adoc index e9447819c373a..c3790610cff5d 100644 --- a/docs/src/main/asciidoc/security-jdbc.adoc +++ b/docs/src/main/asciidoc/security-jdbc.adoc @@ -167,6 +167,8 @@ quarkus.datasource.jdbc.url=jdbc:postgresql:elytron-security-jdbc ---- In our context, we are using PostgreSQL as identity store, and we initialize the database with users and roles. +We will use the salted and hashed version of `password` as a password in this example. +We can use the `BcryptUtil` class to generate passwords in the Modular Crypt Format (MCF). [source,sql] ---- @@ -177,25 +179,20 @@ CREATE TABLE test_user ( role VARCHAR(255) ); -INSERT INTO test_user (id, username, password, role) VALUES (1, 'admin', 'admin', 'admin'); -INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','user', 'user'); +INSERT INTO test_user (id, username, password, role) VALUES (1, 'admin', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'admin'); +INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'user'); ---- -[NOTE] -==== -It is probably useless, but we kindly remind you that you must not store clear-text passwords in production environment ;-). -The `elytron-security-jdbc` extension offers a built-in bcrypt password mapper. -Please refer to the xref:security-getting-started-tutorial.adoc#define-the-user-entity[Define the user entity] section of the Getting started with Security by using Basic authentication and Jakarta Persistence tutorial for practical example. -==== - We can now configure the Elytron JDBC Realm. [source,properties] ---- quarkus.security.jdbc.enabled=true quarkus.security.jdbc.principal-query.sql=SELECT u.password, u.role FROM test_user u WHERE u.username=? <1> -quarkus.security.jdbc.principal-query.clear-password-mapper.enabled=true <2> -quarkus.security.jdbc.principal-query.clear-password-mapper.password-index=1 +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.enabled=true <2> +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.password-index=1 +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.salt-index=-1 +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.iteration-count-index=-1 quarkus.security.jdbc.principal-query.attribute-mappings.0.index=2 <3> quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups ---- @@ -203,7 +200,7 @@ quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups The `elytron-security-jdbc` extension requires at least one principal query to authenticate the user and its identity. <1> We define a parameterized SQL statement (with exactly 1 parameter) which should return the user's password plus any additional information you want to load. -<2> We configure the password mapper with the position of the password field in the `SELECT` fields and other information like salt, hash encoding, etc. +<2> We configure the password mapper with the position of the password field in the `SELECT` fields and other information like salt, hash encoding, etc. Setting the salt and iteration count indexes to `-1` is required for MCF. <3> We use `attribute-mappings` to bind the `SELECT` projection fields (i.e. `u.role` here) to the target Principal representation attributes. [NOTE] @@ -242,21 +239,21 @@ So far so good, now let's try with an allowed user. [source,shell] ---- -$ curl -i -X GET -u admin:admin http://localhost:8080/api/admin +$ curl -i -X GET -u admin:password http://localhost:8080/api/admin HTTP/1.1 200 OK Content-Length: 5 Content-Type: text/plain;charset=UTF-8 admin% ---- -By providing the `admin:admin` credentials, the extension authenticated the user and loaded their roles. +By providing the `admin:password` credentials, the extension authenticated the user and loaded their roles. The `admin` user is authorized to access to the protected resources. The user `admin` should be forbidden to access a resource protected with `@RolesAllowed("user")` because it doesn't have this role. [source,shell] ---- -$ curl -i -X GET -u admin:admin http://localhost:8080/api/users/me +$ curl -i -X GET -u admin:password http://localhost:8080/api/users/me HTTP/1.1 403 Forbidden Content-Length: 34 Content-Type: text/html;charset=UTF-8 @@ -268,7 +265,7 @@ Finally, using the user `user` works and the security context contains the princ [source,shell] ---- -$ curl -i -X GET -u user:user http://localhost:8080/api/users/me +$ curl -i -X GET -u user:password http://localhost:8080/api/users/me HTTP/1.1 200 OK Content-Length: 4 Content-Type: text/plain;charset=UTF-8 @@ -294,8 +291,10 @@ quarkus.datasource.permissions.jdbc.url=jdbc:postgresql:multiple-data-sources-pe quarkus.security.jdbc.enabled=true quarkus.security.jdbc.principal-query.sql=SELECT u.password FROM test_user u WHERE u.username=? -quarkus.security.jdbc.principal-query.clear-password-mapper.enabled=true -quarkus.security.jdbc.principal-query.clear-password-mapper.password-index=1 +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.enabled=true +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.password-index=1 +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.salt-index=-1 +quarkus.security.jdbc.principal-query.bcrypt-password-mapper.iteration-count-index=-1 quarkus.security.jdbc.principal-query.roles.sql=SELECT r.role_name FROM test_role r, test_user_role ur WHERE ur.username=? AND ur.role_id = r.id quarkus.security.jdbc.principal-query.roles.datasource=permissions From f11e04bce1cb18e682f18ada7f833c9f7f6496ea Mon Sep 17 00:00:00 2001 From: Selim Date: Sun, 3 Mar 2024 21:50:30 +0300 Subject: [PATCH 17/34] add BcryptUtil example (cherry picked from commit e3f20046db9750165b33ec181ca65e1ff7b60b2c) --- docs/src/main/asciidoc/security-jdbc.adoc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/src/main/asciidoc/security-jdbc.adoc b/docs/src/main/asciidoc/security-jdbc.adoc index c3790610cff5d..a8718e70ebd10 100644 --- a/docs/src/main/asciidoc/security-jdbc.adoc +++ b/docs/src/main/asciidoc/security-jdbc.adoc @@ -183,6 +183,24 @@ INSERT INTO test_user (id, username, password, role) VALUES (1, 'admin', '$2a$10 INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'user'); ---- +When signing up new users, we can encrypt their password as follows: + +[source,java] +---- +package org.acme.security.jdbc; + +import io.quarkus.elytron.security.common.BcryptUtil; + +public class AccountService { + + public void signupUser(String username, String password) { + String encryptedPassword = BcryptUtil.bcryptHash(password); + + // store user with the encrypted password in the database + } +} +---- + We can now configure the Elytron JDBC Realm. [source,properties] From 121d2378ce5fb4eb098c325691ebfbdcb55579df Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Sat, 27 Jan 2024 12:10:41 +0200 Subject: [PATCH 18/34] fix: cli not recognizing installed plugins (cherry picked from commit 46cf7f5cb924351b1ecf022f01a006f99f54ad58) --- devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java index b1ef86a4788e9..febcb49601869 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java @@ -93,6 +93,7 @@ public int run(String... args) throws Exception { //When running tests the cli should not prompt for user input. boolean interactiveMode = Arrays.stream(args).noneMatch(arg -> arg.equals("--cli-test")); Optional testDir = Arrays.stream(args).dropWhile(arg -> !arg.equals("--cli-test-dir")).skip(1).findFirst(); + boolean noCommand = args.length == 0 || args[0].startsWith("-"); boolean helpCommand = Arrays.stream(args).anyMatch(arg -> arg.equals("--help")); boolean pluginCommand = args.length >= 1 && (args[0].equals("plug") || args[0].equals("plugin")); @@ -103,7 +104,7 @@ public int run(String... args) throws Exception { // If the command already exists and is not a help command (that lists subcommands) or plugin command, then just execute // without dealing with plugins. // The reason that we check if its a plugin command is that plugin commands need PluginManager initialization. - if (existingCommand && !helpCommand && !pluginCommand) { + if (existingCommand && !noCommand && !helpCommand && !pluginCommand) { return cmd.execute(args); } PluginCommandFactory pluginCommandFactory = new PluginCommandFactory(output); @@ -111,14 +112,15 @@ public int run(String... args) throws Exception { pluginManager.syncIfNeeded(); Map plugins = new HashMap<>(pluginManager.getInstalledPlugins()); pluginCommandFactory.populateCommands(cmd, plugins); - missingCommand.ifPresent(m -> { + missingCommand.filter(m -> !plugins.containsKey(m)).ifPresent(m -> { try { + output.info("Command %s is not available, looking for available plugins ...", m); Map installable = pluginManager.getInstallablePlugins(); if (installable.containsKey(m)) { Plugin candidate = installable.get(m); PluginListItem item = new PluginListItem(false, candidate); PluginListTable table = new PluginListTable(List.of(item)); - output.info("Command %s not installed but the following plugin is available:\n%s", m, + output.info("Plugin %s is available:\n%s", m, table.getContent()); if (interactiveMode && Prompt.yesOrNo(true, "Would you like to install it now?", From 5793dae527c993beb25222e64eec5424c368b225 Mon Sep 17 00:00:00 2001 From: w0pp <157312110+w0pp@users.noreply.github.com> Date: Sun, 3 Mar 2024 11:31:38 +0100 Subject: [PATCH 19/34] Fix wrong volume host path being used on Windows (cherry picked from commit a807eb5f6a5d7f5f115fe2311dedb01b74a42414) --- .../io/quarkus/devservices/common/Volumes.java | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java index 39219be41eed6..3b56144a78e32 100644 --- a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java +++ b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java @@ -18,19 +18,12 @@ private Volumes() { public static void addVolumes(GenericContainer container, Map volumes) { for (Map.Entry volume : volumes.entrySet()) { String hostLocation = volume.getKey(); - BindMode bindMode = BindMode.READ_WRITE; if (volume.getKey().startsWith(CLASSPATH)) { - URL url = Thread.currentThread().getContextClassLoader() - .getResource(hostLocation.replaceFirst(CLASSPATH, EMPTY)); - if (url == null) { - throw new IllegalStateException("Classpath resource at '" + hostLocation + "' not found!"); - } - - hostLocation = url.getPath(); - bindMode = BindMode.READ_ONLY; + container.withClasspathResourceMapping(hostLocation.replaceFirst(CLASSPATH, EMPTY), volume.getValue(), + BindMode.READ_ONLY); + } else { + container.withFileSystemBind(hostLocation, volume.getValue(), BindMode.READ_WRITE); } - - container.withFileSystemBind(hostLocation, volume.getValue(), bindMode); } } } From 146819c9c281196e506e560cd97f464ae9905049 Mon Sep 17 00:00:00 2001 From: w0pp <157312110+w0pp@users.noreply.github.com> Date: Mon, 4 Mar 2024 11:10:28 +0100 Subject: [PATCH 20/34] Fix build failing due to unused import (cherry picked from commit 07bfe63e993791b3442d6827dc86cae0543dd822) --- .../src/main/java/io/quarkus/devservices/common/Volumes.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java index 3b56144a78e32..2488bcf52540e 100644 --- a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java +++ b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/Volumes.java @@ -1,6 +1,5 @@ package io.quarkus.devservices.common; -import java.net.URL; import java.util.Map; import org.testcontainers.containers.BindMode; From a1306a78d961209d3e7783a7892f7e7fa3197777 Mon Sep 17 00:00:00 2001 From: barreiro Date: Sat, 2 Mar 2024 18:10:21 +0000 Subject: [PATCH 21/34] Update to Agroal 2.3 (cherry picked from commit 22a463bfdffcb3b57fb12988a20f2d66a8ee5245) --- bom/application/pom.xml | 2 +- .../MultiplePersistenceUnitsInterceptorTest.java | 4 ++++ ...tiplePersistenceUnitsPackageAnnotationsTest.java | 13 +++++++++++-- ...enceUnitsResourceInjectionEntityManagerTest.java | 1 - ...ersistenceUnitsResourceInjectionSessionTest.java | 1 - 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 35ba3e9c57e02..e8ea2b6a5284e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -108,7 +108,7 @@ 7.0.0.Final 7.0.0.Final - 2.1 + 2.3 8.0.0.Final 8.11.4 2.2.21 diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInterceptorTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInterceptorTest.java index d5f73061f69ad..74b7d037bf1c0 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInterceptorTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsInterceptorTest.java @@ -60,8 +60,12 @@ public void initData() throws Exception { transaction.begin(); DefaultEntity entity = new DefaultEntity("default"); defaultSession.persist(entity); + transaction.commit(); + transaction.begin(); User user = new User("user"); usersSession.persist(user); + transaction.commit(); + transaction.begin(); Plane plane = new Plane("plane"); inventorySession.persist(plane); transaction.commit(); diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsPackageAnnotationsTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsPackageAnnotationsTest.java index 86265c074ecc7..5ed881244eb25 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsPackageAnnotationsTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsPackageAnnotationsTest.java @@ -6,6 +6,7 @@ import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; +import jakarta.transaction.UserTransaction; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -39,26 +40,34 @@ public class MultiplePersistenceUnitsPackageAnnotationsTest { @PersistenceUnit("inventory") EntityManager inventoryEntityManager; + @Inject + UserTransaction transaction; + @Test - @Transactional - public void testDefault() { + public void testDefault() throws Exception { + transaction.begin(); SharedEntity defaultEntity = new SharedEntity("default"); defaultEntityManager.persist(defaultEntity); SharedEntity savedDefaultEntity = defaultEntityManager.find(SharedEntity.class, defaultEntity.getId()); assertEquals(defaultEntity.getName(), savedDefaultEntity.getName()); + transaction.commit(); + transaction.begin(); SharedEntity defaultEntity2 = new SharedEntity("default2"); usersEntityManager.persist(defaultEntity2); SharedEntity savedDefaultEntity2 = usersEntityManager.find(SharedEntity.class, defaultEntity2.getId()); assertEquals(defaultEntity2.getName(), savedDefaultEntity2.getName()); + transaction.commit(); + transaction.begin(); SharedEntity defaultEntity3 = new SharedEntity("default3"); inventoryEntityManager.persist(defaultEntity3); SharedEntity savedDefaultEntity3 = inventoryEntityManager.find(SharedEntity.class, defaultEntity3.getId()); assertEquals(defaultEntity3.getName(), savedDefaultEntity3.getName()); + transaction.commit(); } @Test diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionEntityManagerTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionEntityManagerTest.java index d115d67dfb842..52a5fafc14ea1 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionEntityManagerTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionEntityManagerTest.java @@ -58,7 +58,6 @@ public void testUserInInventoryEntityManager() { } @Test - @Transactional public void testAccessBothPersistenceUnits() { testUser(); testPlane(); diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionSessionTest.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionSessionTest.java index b13741e7a9490..1d5e0ab49a7dc 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionSessionTest.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/multiplepersistenceunits/MultiplePersistenceUnitsResourceInjectionSessionTest.java @@ -58,7 +58,6 @@ public void testUserInInventorySession() { } @Test - @Transactional public void testAccessBothPersistenceUnits() { testUser(); testPlane(); From fe74f0ef3a21390ecc95259b207c413d0a9b3d56 Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Mon, 4 Mar 2024 21:38:52 +0100 Subject: [PATCH 22/34] Remove misleading note from jacoco.enabled (cherry picked from commit f83ad85cdc2f5dd97225b29045020cb7420b340d) --- .../src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java b/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java index 86aaf74c3f5fb..307c58c6e7fd9 100644 --- a/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java +++ b/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java @@ -17,8 +17,7 @@ public class JacocoConfig { public static final String TARGET_JACOCO_REPORT = "target/" + JACOCO_REPORT; /** - * Whether or not the jacoco extension is enabled. Disabling it can come in handy when runnig tests in IDEs that do their - * own jacoco instrumentation, e.g. EclEmma in Eclipse. + * Whether or not the jacoco extension is enabled. */ @ConfigItem(defaultValue = "true") public boolean enabled; From 3d1aee8726af19b2af3bd91137f768426859cb89 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 1 Mar 2024 11:44:27 +0200 Subject: [PATCH 23/34] Properly pass annotations to MessageBodyReader in REST Client Fixes: #19849 (cherry picked from commit bd77018404ed7151c9b58fa88fa1dbb5d014630e) --- ...mMessageBodyReaderUsesAnnotationsTest.java | 114 ++++++++++++++++++ .../ClientResponseCompleteRestHandler.java | 2 + .../reactive/client/impl/MultiInvoker.java | 17 ++- .../client/impl/RestClientRequestContext.java | 14 ++- 4 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java new file mode 100644 index 0000000000000..eab0b5aced44b --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java @@ -0,0 +1,114 @@ +package io.quarkus.rest.client.reactive; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Type; +import java.net.URI; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.MessageBodyReader; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class CustomMessageBodyReaderUsesAnnotationsTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest(); + + @TestHTTPResource + URI baseUri; + + private Client client; + + @BeforeEach + public void before() { + client = QuarkusRestClientBuilder.newBuilder() + .baseUri(baseUri) + .register(PersonMessageBodyReader.class) + .build(Client.class); + } + + @Test + public void fromAnnotation() { + Person person = client.fromAnnotation(); + assertEquals("from-annotation", person.name()); + } + + @Test + public void unset() { + Person person = client.unset(); + assertEquals("unset", person.name()); + } + + @Path("test") + public interface Client { + + @GET + @PersonName("from-annotation") + Person fromAnnotation(); + + @GET + Person unset(); + } + + @Path("test") + public static class Endpoint { + + @GET + public Person get() { + return new Person("dummy"); + } + } + + public record Person(String name) { + + } + + @Documented + @Target({ ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + public @interface PersonName { + String value(); + } + + public static class PersonMessageBodyReader implements MessageBodyReader { + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type.equals(Person.class); + } + + @Override + public Person readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) { + PersonName personName = null; + if (annotations != null) { + for (Annotation annotation : annotations) { + if (annotation instanceof PersonName pn) { + personName = pn; + break; + } + } + } + if (personName == null) { + return new Person("unset"); + } + return new Person(personName.value()); + } + } +} diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java index 873bb8fd62d19..36d165192f98b 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java @@ -76,6 +76,7 @@ public static ResponseImpl mapToResponse(RestClientRequestContext context, Object fieldValue = context.readEntity(in, fieldFiller.getFieldType(), MediaType.valueOf(fieldFiller.getMediaType()), + context.getMethodDeclaredAnnotationsSafe(), // FIXME: we have strings, it wants objects, perhaps there's // an Object->String conversion too many (MultivaluedMap) responseContext.getHeaders()); @@ -104,6 +105,7 @@ public static ResponseImpl mapToResponse(RestClientRequestContext context, Object entity = context.readEntity(entityStream, context.getResponseType(), responseContext.getMediaType(), + context.getMethodDeclaredAnnotationsSafe(), // FIXME: we have strings, it wants objects, perhaps there's // an Object->String conversion too many (MultivaluedMap) responseContext.getHeaders()); diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java index 4459e66000227..235a3937d0605 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java @@ -318,7 +318,11 @@ public void handle(Buffer buffer) { if (start < end) { ByteArrayInputStream in = new ByteArrayInputStream(bytes, start, end); - R item = restClientRequestContext.readEntity(in, responseType, mediaType, + R item = restClientRequestContext.readEntity( + in, + responseType, + mediaType, + restClientRequestContext.getMethodDeclaredAnnotationsSafe(), response.getMetadata()); multiRequest.emitter.emit(item); } @@ -326,7 +330,11 @@ public void handle(Buffer buffer) { } } else { ByteArrayInputStream in = new ByteArrayInputStream(bytes); - R item = restClientRequestContext.readEntity(in, responseType, mediaType, + R item = restClientRequestContext.readEntity( + in, + responseType, + mediaType, + restClientRequestContext.getMethodDeclaredAnnotationsSafe(), response.getMetadata()); multiRequest.emitter.emit(item); } @@ -360,7 +368,10 @@ public void handle(Buffer chunk) { ByteArrayInputStream in = new ByteArrayInputStream(chunk.getBytes()); try { - R item = restClientRequestContext.readEntity(in, responseType, response.getMediaType(), + R item = restClientRequestContext.readEntity(in, + responseType, + response.getMediaType(), + restClientRequestContext.getMethodDeclaredAnnotationsSafe(), response.getMetadata()); multiRequest.emit(item); } catch (IOException e) { diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java index a2f7e9fba9102..dca9e4fe33bf7 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java @@ -167,6 +167,14 @@ public Method getInvokedMethod() { return null; } + public Annotation[] getMethodDeclaredAnnotationsSafe() { + Method invokedMethod = getInvokedMethod(); + if (invokedMethod != null) { + return invokedMethod.getDeclaredAnnotations(); + } + return null; + } + @Override protected Throwable unwrapException(Throwable t) { var res = super.unwrapException(t); @@ -190,12 +198,14 @@ protected Throwable unwrapException(Throwable t) { @SuppressWarnings("unchecked") public T readEntity(InputStream in, - GenericType responseType, MediaType mediaType, + GenericType responseType, + MediaType mediaType, + Annotation[] annotations, MultivaluedMap metadata) throws IOException { if (in == null) return null; - return (T) ClientSerialisers.invokeClientReader(null, responseType.getRawType(), responseType.getType(), + return (T) ClientSerialisers.invokeClientReader(annotations, responseType.getRawType(), responseType.getType(), mediaType, properties, this, metadata, restClient.getClientContext().getSerialisers(), in, getReaderInterceptors(), configuration); } From 41f801765611ad70635e3208c42d9b130154e981 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Tue, 5 Mar 2024 08:39:03 +0100 Subject: [PATCH 24/34] Allow setting the SettingsDecrypter when initializing a Maven artifact resolver (cherry picked from commit 95ff1c898a807f8ae3bd009d479ec9544a61ee81) --- .../resolver/maven/BootstrapMavenContext.java | 3 ++- .../maven/BootstrapMavenContextConfig.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java index 5150d282ca810..4f9af82270835 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java @@ -157,6 +157,7 @@ public BootstrapMavenContext(BootstrapMavenContextConfig config) this.remoteRepos = config.remoteRepos; this.remotePluginRepos = config.remotePluginRepos; this.remoteRepoManager = config.remoteRepoManager; + this.settingsDecrypter = config.settingsDecrypter; this.cliOptions = config.cliOptions; this.excludeSisuBeanPackages = config.getExcludeSisuBeanPackages(); this.includeSisuBeanPackages = config.getIncludeSisuBeanPackages(); @@ -299,7 +300,7 @@ public List getRemotePluginRepositories() throws BootstrapMave return remotePluginRepos == null ? remotePluginRepos = resolveRemotePluginRepos() : remotePluginRepos; } - private SettingsDecrypter getSettingsDecrypter() { + public SettingsDecrypter getSettingsDecrypter() { if (settingsDecrypter == null) { initRepoSystemAndManager(); } diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java index 0f44dcc37a8ec..9cef569d8c6f4 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java @@ -9,6 +9,7 @@ import java.util.function.Function; import org.apache.maven.model.Model; +import org.apache.maven.settings.crypto.SettingsDecrypter; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.impl.RemoteRepositoryManager; @@ -28,6 +29,7 @@ public class BootstrapMavenContextConfig remoteRepos; protected List remotePluginRepos; protected RemoteRepositoryManager remoteRepoManager; + protected SettingsDecrypter settingsDecrypter; protected String alternatePomName; protected File userSettings; protected boolean artifactTransferLogging = true; @@ -190,6 +192,18 @@ public T setRemoteRepositoryManager(RemoteRepositoryManager remoteRepoManager) { return (T) this; } + /** + * Settings decryptor + * + * @param settingsDecrypter settings decrypter + * @return + */ + @SuppressWarnings("unchecked") + public T setSettingsDecrypter(SettingsDecrypter settingsDecrypter) { + this.settingsDecrypter = settingsDecrypter; + return (T) this; + } + /** * The meaning of this option is equivalent to alternative POM in Maven, * which can be specified with command line argument '-f'. From 66b05ad62a8c713c5ea053565eebe19571d5c12a Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 4 Mar 2024 11:17:08 +0100 Subject: [PATCH 25/34] Keep the timestamps when copying jars and building JIB layers Note that I was a bit surprised that we are copying some transformed jars in the lib/main directory. Apparently, only jars for which we remove entries. From my tests, the entry removal is reproducible and always produces the exact same binary so I suppose it's good enough. Fixes #39130 (cherry picked from commit e5eb9898704b394c5585bd807de6b6a1444e6142) --- .../pkg/steps/JarResultBuildStep.java | 8 +++- .../image/jib/deployment/JibProcessor.java | 38 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java index fdcbe78e85e25..a81be9578412e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java @@ -926,9 +926,11 @@ private void copyDependency(Set parentFirstArtifacts, OutputTargetB } } if (removedFromThisArchive.isEmpty()) { - Files.copy(resolvedDep, targetPath, StandardCopyOption.REPLACE_EXISTING); + Files.copy(resolvedDep, targetPath, StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES); } else { - //we have removed classes, we need to handle them correctly + // we copy jars for which we remove entries to the same directory + // which seems a bit odd to me filterZipFile(resolvedDep, targetPath, removedFromThisArchive); } } @@ -1251,6 +1253,8 @@ private void filterZipFile(Path resolvedDep, Path targetPath, Set transf } } } + // let's make sure we keep the original timestamp + Files.setLastModifiedTime(targetPath, Files.getLastModifiedTime(resolvedDep)); } } catch (IOException e) { throw new RuntimeException(e); diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index 7eaa9bde7be69..e70d0948e7f2a 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -527,13 +527,14 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag try { Instant now = Instant.now(); + boolean enforceModificationTime = !jibConfig.useCurrentTimestampFileModification; Instant modificationTime = jibConfig.useCurrentTimestampFileModification ? now : Instant.EPOCH; JibContainerBuilder jibContainerBuilder = toJibContainerBuilder(baseJvmImage, jibConfig); if (fastChangingLibPaths.isEmpty()) { // just create a layer with the entire lib structure intact addLayer(jibContainerBuilder, Collections.singletonList(componentsPath.resolve(JarResultBuildStep.LIB)), - workDirInContainer, "fast-jar-lib", isMutableJar, modificationTime); + workDirInContainer, "fast-jar-lib", isMutableJar, enforceModificationTime, modificationTime); } else { // we need to manually create each layer // the idea here is that the fast changing libraries are created in a later layer, thus when they do change, @@ -547,14 +548,9 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag AbsoluteUnixPath libPathInContainer = workDirInContainer.resolve(JarResultBuildStep.LIB) .resolve(JarResultBuildStep.BOOT_LIB) .resolve(lib.getFileName()); - if (appCDSResult.isPresent()) { - // the boot lib jars need to preserve the modification time because otherwise AppCDS won't work - bootLibsLayerBuilder.addEntry(lib, libPathInContainer, - Files.getLastModifiedTime(lib).toInstant()); - } else { - bootLibsLayerBuilder.addEntry(lib, libPathInContainer); - } - + // the boot lib jars need to preserve the modification time because otherwise AppCDS won't work + bootLibsLayerBuilder.addEntry(lib, libPathInContainer, + Files.getLastModifiedTime(lib).toInstant()); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -567,15 +563,15 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag .resolve(JarResultBuildStep.DEPLOYMENT_LIB); addLayer(jibContainerBuilder, Collections.singletonList(deploymentPath), workDirInContainer.resolve(JarResultBuildStep.LIB), - "fast-jar-deployment-libs", true, modificationTime); + "fast-jar-deployment-libs", true, enforceModificationTime, modificationTime); } AbsoluteUnixPath libsMainPath = workDirInContainer.resolve(JarResultBuildStep.LIB) .resolve(JarResultBuildStep.MAIN); addLayer(jibContainerBuilder, nonFastChangingLibPaths, libsMainPath, "fast-jar-normal-libs", - isMutableJar, modificationTime); + isMutableJar, enforceModificationTime, modificationTime); addLayer(jibContainerBuilder, new ArrayList<>(fastChangingLibPaths), libsMainPath, "fast-jar-changing-libs", - isMutableJar, modificationTime); + isMutableJar, enforceModificationTime, modificationTime); } if (appCDSResult.isPresent()) { @@ -599,9 +595,9 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag } addLayer(jibContainerBuilder, Collections.singletonList(componentsPath.resolve(JarResultBuildStep.APP)), - workDirInContainer, "fast-jar-quarkus-app", isMutableJar, modificationTime); + workDirInContainer, "fast-jar-quarkus-app", isMutableJar, enforceModificationTime, modificationTime); addLayer(jibContainerBuilder, Collections.singletonList(componentsPath.resolve(JarResultBuildStep.QUARKUS)), - workDirInContainer, "fast-jar-quarkus", isMutableJar, modificationTime); + workDirInContainer, "fast-jar-quarkus", isMutableJar, enforceModificationTime, modificationTime); if (ContainerImageJibConfig.DEFAULT_WORKING_DIR.equals(jibConfig.workingDirectory)) { // this layer ensures that the working directory is writeable // see https://github.com/GoogleContainerTools/jib/issues/1270 @@ -665,7 +661,7 @@ private boolean containsRunJava(String baseJvmImage) { public JibContainerBuilder addLayer(JibContainerBuilder jibContainerBuilder, List files, AbsoluteUnixPath pathInContainer, String name, boolean isMutableJar, - Instant now) + boolean enforceModificationTime, Instant forcedModificationTime) throws IOException { FileEntriesLayer.Builder layerConfigurationBuilder = FileEntriesLayer.builder().setName(name); @@ -673,7 +669,17 @@ public JibContainerBuilder addLayer(JibContainerBuilder jibContainerBuilder, Lis layerConfigurationBuilder.addEntryRecursive( file, pathInContainer.resolve(file.getFileName()), isMutableJar ? REMOTE_DEV_FOLDER_PERMISSIONS_PROVIDER : DEFAULT_FILE_PERMISSIONS_PROVIDER, - (sourcePath, destinationPath) -> now, + (sourcePath, destinationPath) -> { + if (enforceModificationTime) { + return forcedModificationTime; + } + + try { + return Files.getLastModifiedTime(sourcePath).toInstant(); + } catch (IOException e) { + throw new RuntimeException("Unable to get last modified time for " + sourcePath, e); + } + }, isMutableJar ? REMOTE_DEV_OWNERSHIP_PROVIDER : DEFAULT_OWNERSHIP_PROVIDER); } From 49003b2b9ddbfe4da2db6534f14ee03e4703ab1c Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Thu, 29 Feb 2024 19:30:03 +0100 Subject: [PATCH 26/34] Emphasize the need to add quarkus-junit5-mockito as a dependency to use mock injection (cherry picked from commit 986244bdf7a4a6f8c58fe2d0b3fa08fd83d4093d) --- .../main/asciidoc/getting-started-testing.adoc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc index 9ae069a0b0235..45dbbe6f9ab0e 100644 --- a/docs/src/main/asciidoc/getting-started-testing.adoc +++ b/docs/src/main/asciidoc/getting-started-testing.adoc @@ -843,7 +843,20 @@ So if you need to call methods such as `verify` you should hang on to the mock i ==== Further simplification with `@InjectMock` Building on the features provided by `QuarkusMock`, Quarkus also allows users to effortlessly take advantage of link:https://site.mockito.org/[Mockito] for mocking the beans supported by `QuarkusMock`. -This functionality is available with the `@io.quarkus.test.InjectMock` annotation if the `quarkus-junit5-mockito` dependency is present. + +[IMPORTANT] +==== +This functionality is available with the `@io.quarkus.test.InjectMock` annotation **only if** the `quarkus-junit5-mockito` dependency is present: +[source,xml] +---- + + io.quarkus + quarkus-junit5-mockito + test + +---- + +==== Using `@InjectMock`, the previous example could be written as follows: From 2cc0863a3c79db5f8f813b4fe143e1ca960cd23a Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 26 Feb 2024 11:18:02 +0100 Subject: [PATCH 27/34] Update to Vert.x 4.5.4 and Netty 4.1.107 This update brings significant changes affecting extensions utilizing a (Vert.x) TCP client. With Vert.x version 4.5.4, establishing TLS connections now mandates the use of hostname verification algorithms. This requirement impacts various components including Reactive SQL clients, Redis, RabbitMQ, MQTT, among others. Previously, if not explicitly specified by the protocol, the verification algorithm defaulted to "". This setting essentially skipped the verification process. However, with the new version, explicit configuration is necessary. Consequently, each extension has been adjusted to utilize the most appropriate verification algorithm. It's important to note that these modifications may potentially disrupt existing applications. In such cases, referring to the documentation of the specific extension or component is recommended for proper configuration of the hostname verification algorithm. (cherry picked from commit ee8b09ab442b24bb8ad1b37f3542713162396072) --- bom/application/pom.xml | 4 ++-- docs/src/main/asciidoc/redis-reference.adoc | 2 ++ .../redis/runtime/client/VertxRedisClientFactory.java | 11 ++++++++--- .../redis/runtime/client/config/TlsConfig.java | 10 ++++++---- independent-projects/resteasy-reactive/pom.xml | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index e8ea2b6a5284e..a390079a23fa6 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -121,7 +121,7 @@ 1.0.1.Final 2.2.3.Final 3.5.1.Final - 4.5.3 + 4.5.4 4.5.14 4.4.16 4.1.5 @@ -144,7 +144,7 @@ 14.0.25.Final 4.6.5.Final 3.1.5 - 4.1.106.Final + 4.1.107.Final 1.14.0 1.0.4 3.5.3.Final diff --git a/docs/src/main/asciidoc/redis-reference.adoc b/docs/src/main/asciidoc/redis-reference.adoc index a87fca831ff98..24709b8fe4c51 100644 --- a/docs/src/main/asciidoc/redis-reference.adoc +++ b/docs/src/main/asciidoc/redis-reference.adoc @@ -225,6 +225,8 @@ To use TLS, you need to: 1. Set the `quarkus.redis.tls.enabled=true` property 2. Make sure that your URL starts with `rediss://` (with two `s`) +IMPORTANT: The default hostname verifier is set to `NONE`, meaning it does not verify the host name. You can change this behavior by setting the `quarkus.redis.tls.hostname-verification-algorithm` property, to `HTTPS` for example. + === Configure the authentication The Redis password can be set in the `redis://` URL or with the `quarkus.redis.password` property. diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java index 7c89ffd7e8319..e21e438e23998 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java @@ -116,7 +116,14 @@ private static NetClientOptions toNetClientOptions(RedisClientConfig config) { tcp.alpn().ifPresent(net::setUseAlpn); tcp.applicationLayerProtocols().ifPresent(net::setApplicationLayerProtocols); tcp.connectionTimeout().ifPresent(d -> net.setConnectTimeout((int) d.toMillis())); - tls.hostnameVerificationAlgorithm().ifPresent(net::setHostnameVerificationAlgorithm); + + String verificationAlgorithm = tls.hostnameVerificationAlgorithm(); + if ("NONE".equalsIgnoreCase(verificationAlgorithm)) { + net.setHostnameVerificationAlgorithm(""); + } else { + net.setHostnameVerificationAlgorithm(verificationAlgorithm); + } + tcp.idleTimeout().ifPresent(d -> net.setIdleTimeout((int) d.toSeconds())); tcp.keepAlive().ifPresent(b -> net.setTcpKeepAlive(true)); @@ -163,8 +170,6 @@ private static NetClientOptions toNetClientOptions(RedisClientConfig config) { tcp.quickAck().ifPresent(net::setTcpQuickAck); tcp.writeIdleTimeout().ifPresent(d -> net.setWriteIdleTimeout((int) d.toSeconds())); - tls.hostnameVerificationAlgorithm().ifPresent(net::setHostnameVerificationAlgorithm); - return net; } diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java index 44a4126b52752..e8a6c46a75e95 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java @@ -1,7 +1,5 @@ package io.quarkus.redis.runtime.client.config; -import java.util.Optional; - import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.vertx.core.runtime.config.JksConfiguration; import io.quarkus.vertx.core.runtime.config.PemKeyCertConfiguration; @@ -68,8 +66,12 @@ public interface TlsConfig { /** * The hostname verification algorithm to use in case the server's identity should be checked. - * Should be HTTPS, LDAPS or an empty string. + * Should be {@code HTTPS}, {@code LDAPS} or an {@code NONE} (default). + *

+ * If set to {@code NONE}, it does not verify the hostname. + *

*/ - Optional hostnameVerificationAlgorithm(); + @WithDefault("NONE") + String hostnameVerificationAlgorithm(); } diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 585fea1730e62..f2797fbbf7b36 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -61,7 +61,7 @@ 3.2.5 2.5.7 2.1.2 - 4.5.3 + 4.5.4 5.4.0 1.0.0.Final 2.16.1 From a6e7b1453585b4da5b672a31799be1ba46a9d9a5 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 26 Feb 2024 11:46:09 +0100 Subject: [PATCH 28/34] Update the Mutiny bindings to use Vert.x 4.5.4. (cherry picked from commit 11824c7a71db8251d9cc07e9b0a28ed1d2537337) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a390079a23fa6..e8bdecd453dde 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -61,7 +61,7 @@ 2.1.0 1.0.13 3.0.1 - 3.9.0 + 3.10.0 4.17.0 2.5.0 2.1.2 From 7107dbc9f5ce88a79d309c862e8f511ffc5ee821 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 26 Feb 2024 11:52:26 +0100 Subject: [PATCH 29/34] Change TLS Hostname Verifier in Reactive SQL Client default value (do not break) The update to Vert.x 4.5.4 necessitates adjustments in extensions leveraging the Vert.x TCP client, notably impacting reactive SQL clients. TLS connections now require explicit configuration of hostname verification algorithms. Previously, in the absence of explicit specification by the protocol, the verification algorithm defaulted to "". Although this default remains unchanged in this commit, a MicroProfile Config limitation (https://github.com/eclipse/microprofile-config/issues/446) highlights that "" is not a valid value in the Quarkus configuration. Consequently, "NONE" has been adopted as an alternative. This enables users to explicitly set the hostname verification algorithm to "NONE" to bypass the verification process. (cherry picked from commit db683324f83cdc98510700ecde17374363ae5e38) --- docs/src/main/asciidoc/amqp-reference.adoc | 2 +- .../runtime/DataSourceReactiveRuntimeConfig.java | 6 ++++-- .../db2/client/runtime/DB2PoolRecorder.java | 8 +++++--- .../mssql/client/runtime/MSSQLPoolRecorder.java | 8 +++++--- .../mysql/client/runtime/MySQLPoolRecorder.java | 12 ++++++++---- .../reactive/pg/client/runtime/PgPoolRecorder.java | 13 ++++++++----- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/docs/src/main/asciidoc/amqp-reference.adoc b/docs/src/main/asciidoc/amqp-reference.adoc index 85355b4b98e0d..10cdff6374bae 100644 --- a/docs/src/main/asciidoc/amqp-reference.adoc +++ b/docs/src/main/asciidoc/amqp-reference.adoc @@ -448,7 +448,7 @@ public AmqpClientOptions getNamedOptions() { .setPemKeyCertOptions(keycert) .setPemTrustOptions(trust) .addEnabledSaslMechanism("EXTERNAL") - .setHostnameVerificationAlgorithm("") + .setHostnameVerificationAlgorithm("") // Disables the hostname verification. Defaults is "HTTPS" .setConnectTimeout(30000) .setReconnectInterval(5000) .setContainerId("my-container"); diff --git a/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/DataSourceReactiveRuntimeConfig.java b/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/DataSourceReactiveRuntimeConfig.java index 6d336c2e0875e..555ecb3b82b77 100644 --- a/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/DataSourceReactiveRuntimeConfig.java +++ b/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/DataSourceReactiveRuntimeConfig.java @@ -111,9 +111,11 @@ public interface DataSourceReactiveRuntimeConfig { /** * The hostname verification algorithm to use in case the server's identity should be checked. - * Should be HTTPS, LDAPS or an empty string. + * Should be {@code HTTPS}, {@code LDAPS} or {@code NONE}. + * {@code NONE} is the default value and disables the verification. */ - Optional hostnameVerificationAlgorithm(); + @WithDefault("NONE") + String hostnameVerificationAlgorithm(); /** * The maximum time a connection remains unused in the pool before it is closed. diff --git a/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java b/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java index 85e16402885f6..134ccf43fa381 100644 --- a/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java +++ b/extensions/reactive-db2-client/runtime/src/main/java/io/quarkus/reactive/db2/client/runtime/DB2PoolRecorder.java @@ -217,9 +217,11 @@ private DB2ConnectOptions toConnectOptions(String dataSourceName, DataSourceRunt connectOptions.setReconnectInterval(dataSourceReactiveRuntimeConfig.reconnectInterval().toMillis()); - if (dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().isPresent()) { - connectOptions.setHostnameVerificationAlgorithm( - dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().get()); + var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm(); + if ("NONE".equalsIgnoreCase(algo)) { + connectOptions.setHostnameVerificationAlgorithm(""); + } else { + connectOptions.setHostnameVerificationAlgorithm(algo); } dataSourceReactiveRuntimeConfig.additionalProperties().forEach(connectOptions::addProperty); diff --git a/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java b/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java index b3c2c8cf1da72..bce9406f67eaf 100644 --- a/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java +++ b/extensions/reactive-mssql-client/runtime/src/main/java/io/quarkus/reactive/mssql/client/runtime/MSSQLPoolRecorder.java @@ -220,9 +220,11 @@ private MSSQLConnectOptions toMSSQLConnectOptions(String dataSourceName, DataSou configureJksKeyCertOptions(mssqlConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificateJks()); configurePfxKeyCertOptions(mssqlConnectOptions, dataSourceReactiveRuntimeConfig.keyCertificatePfx()); - if (dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().isPresent()) { - mssqlConnectOptions.setHostnameVerificationAlgorithm( - dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().get()); + var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm(); + if ("NONE".equalsIgnoreCase(algo)) { + mssqlConnectOptions.setHostnameVerificationAlgorithm(""); + } else { + mssqlConnectOptions.setHostnameVerificationAlgorithm(algo); } dataSourceReactiveRuntimeConfig.additionalProperties().forEach(mssqlConnectOptions::addProperty); diff --git a/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java b/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java index fda8b5372f2a1..51c911af4836b 100644 --- a/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java +++ b/extensions/reactive-mysql-client/runtime/src/main/java/io/quarkus/reactive/mysql/client/runtime/MySQLPoolRecorder.java @@ -215,8 +215,8 @@ private List toMySQLConnectOptions(String dataSourceName, mysqlConnectOptions.setSslMode(sslMode); // If sslMode is verify-identity, we also need a hostname verification algorithm - if (sslMode == SslMode.VERIFY_IDENTITY && (!dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm() - .isPresent() || "".equals(dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().get()))) { + var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm(); + if ("NONE".equalsIgnoreCase(algo) && sslMode == SslMode.VERIFY_IDENTITY) { throw new IllegalArgumentException( "quarkus.datasource.reactive.hostname-verification-algorithm must be specified under verify-identity sslmode"); } @@ -236,8 +236,12 @@ private List toMySQLConnectOptions(String dataSourceName, mysqlConnectOptions.setReconnectInterval(dataSourceReactiveRuntimeConfig.reconnectInterval().toMillis()); - dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().ifPresent( - mysqlConnectOptions::setHostnameVerificationAlgorithm); + var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm(); + if ("NONE".equalsIgnoreCase(algo)) { + mysqlConnectOptions.setHostnameVerificationAlgorithm(""); + } else { + mysqlConnectOptions.setHostnameVerificationAlgorithm(algo); + } dataSourceReactiveMySQLConfig.authenticationPlugin().ifPresent(mysqlConnectOptions::setAuthenticationPlugin); diff --git a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java index 4afab742f0163..554614a412d1f 100644 --- a/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java +++ b/extensions/reactive-pg-client/runtime/src/main/java/io/quarkus/reactive/pg/client/runtime/PgPoolRecorder.java @@ -202,10 +202,9 @@ private List toPgConnectOptions(String dataSourceName, DataSou final SslMode sslMode = dataSourceReactivePostgreSQLConfig.sslMode().get(); pgConnectOptions.setSslMode(sslMode); + var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm(); // If sslMode is verify-full, we also need a hostname verification algorithm - if (sslMode == SslMode.VERIFY_FULL - && (!dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().isPresent() - || "".equals(dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().get()))) { + if ("NONE".equalsIgnoreCase(algo) && sslMode == SslMode.VERIFY_FULL) { throw new IllegalArgumentException( "quarkus.datasource.reactive.hostname-verification-algorithm must be specified under verify-full sslmode"); } @@ -227,8 +226,12 @@ private List toPgConnectOptions(String dataSourceName, DataSou pgConnectOptions.setReconnectInterval(dataSourceReactiveRuntimeConfig.reconnectInterval().toMillis()); - dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().ifPresent( - pgConnectOptions::setHostnameVerificationAlgorithm); + var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm(); + if ("NONE".equalsIgnoreCase(algo)) { + pgConnectOptions.setHostnameVerificationAlgorithm(""); + } else { + pgConnectOptions.setHostnameVerificationAlgorithm(algo); + } dataSourceReactiveRuntimeConfig.additionalProperties().forEach(pgConnectOptions::addProperty); From 32a21d68dc99a2cf236ebd6b559fe997111bad29 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 1 Mar 2024 14:21:48 +0100 Subject: [PATCH 30/34] Update SmallRye Reactive Messaging to 4.18.0 This update addresses the breaking changes introduced by Vert.x 4.5.4 for MQTT and RabbitMQ integration regarding hostname verification. To maintain backward compatibility, hostname verification is still disabled by default. However, to provide users with flexibility and security options, this version introduces connector options to activate hostname verification without using the programmatic customization. (cherry picked from commit 6e4dee732ab2cb7ab4542c85cbcc3273833d30e5) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index e8bdecd453dde..4a6fde61c0362 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -62,7 +62,7 @@ 1.0.13 3.0.1 3.10.0 - 4.17.0 + 4.18.0 2.5.0 2.1.2 2.1.1 From 0d028bbab8b458253fa980fcd88e24c4ea7b9546 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 1 Mar 2024 14:54:11 +0100 Subject: [PATCH 31/34] Update Quarkus HTTP to 5.2.0.Final This version aligns the Vert.x and Netty versions. (cherry picked from commit 261d756f530f582ec6719eff6ce6c297d24cfeb8) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 4a6fde61c0362..263db5eef26e3 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -34,7 +34,7 @@ 1.32.0 1.32.0-alpha 1.21.0-alpha - 5.1.0.Final + 5.2.0.Final 1.12.2 2.1.12 0.22.0 From bca80eb395196c18d66a50e8e9c33a4b3e4ffff9 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 5 Mar 2024 10:04:17 +0100 Subject: [PATCH 32/34] ArC: fix BeanConfiguratorBase#read() - fixes #39169 (cherry picked from commit 3aecefb09afff6a9e8243070cd8e038419479ef8) --- .../SyntheticBeanBuildItemProxyTest.java | 42 +++++++++++++------ .../arc/processor/BeanConfiguratorBase.java | 1 + 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/synthetic/SyntheticBeanBuildItemProxyTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/synthetic/SyntheticBeanBuildItemProxyTest.java index 50275150a2162..6ce4b6c9d5477 100644 --- a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/synthetic/SyntheticBeanBuildItemProxyTest.java +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/synthetic/SyntheticBeanBuildItemProxyTest.java @@ -1,10 +1,11 @@ package io.quarkus.arc.test.synthetic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Method; +import java.util.List; import java.util.function.Consumer; import jakarta.enterprise.context.ApplicationScoped; @@ -14,6 +15,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem.ExtendedBeanConfigurator; import io.quarkus.builder.BuildChainBuilder; @@ -46,20 +48,31 @@ public void execute(BuildContext context) { // We need to use reflection due to some class loading problems Object recorderProxy = bytecodeRecorder.getRecordingProxy(TestRecorder.class); try { - Method test = recorderProxy.getClass().getDeclaredMethod("test"); - Object proxy = test.invoke(recorderProxy); - ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem.configure(SynthBean.class) + Method test = recorderProxy.getClass().getDeclaredMethod("test", String.class); + + Object proxy1 = test.invoke(recorderProxy, "ok"); + ExtendedBeanConfigurator configurator1 = SyntheticBeanBuildItem.configure(SynthBean.class) .scope(ApplicationScoped.class) + .identifier("ok") .unremovable(); // No creator assertThrows(IllegalStateException.class, - () -> configurator.done()); + () -> configurator1.done()); // Not a returned proxy assertThrows(IllegalArgumentException.class, - () -> configurator.runtimeProxy(new SynthBean())); - context.produce(configurator - .runtimeProxy(proxy) + () -> configurator1.runtimeProxy(new SynthBean())); + context.produce(configurator1 + .runtimeProxy(proxy1) + .done()); + + // Register a synthetic bean with same types and qualifiers but different identifier + context.produce(SyntheticBeanBuildItem.configure(SynthBean.class) + .scope(ApplicationScoped.class) + .identifier("nok") + .unremovable() + .runtimeProxy(test.invoke(recorderProxy, "nok")) .done()); + } catch (Exception e) { throw new RuntimeException(e); } @@ -73,9 +86,9 @@ public void execute(BuildContext context) { @Recorder public static class TestRecorder { - public SynthBean test() { + public SynthBean test(String val) { SynthBean bean = new SynthBean(); - bean.setValue("ok"); + bean.setValue(val); return bean; } @@ -83,9 +96,12 @@ public SynthBean test() { @Test public void testBeans() { - SynthBean bean = Arc.container().instance(SynthBean.class).get(); - assertNotNull(bean); - assertEquals("ok", bean.getValue()); + List> beans = Arc.container().listAll(SynthBean.class); + assertEquals(2, beans.size()); + for (InstanceHandle handle : beans) { + String val = handle.get().getValue(); + assertTrue("ok".equals(val) || "nok".equals(val)); + } } @Vetoed diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java index 7f56930b8be3a..87cfaaca00bfd 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java @@ -72,6 +72,7 @@ protected BeanConfiguratorBase(DotName implClazz) { */ public THIS read(BeanConfiguratorBase base) { super.read(base); + identifier = base.identifier; types.clear(); types.addAll(base.types); qualifiers.clear(); From c7e39eeff9e132f661ffc2b8deb5a372c7e2066f Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Wed, 28 Feb 2024 14:02:55 +0100 Subject: [PATCH 33/34] Skip analysis of plugin executions with phases post quarkus:dev preparing for dev mode launch (cherry picked from commit 5563998ace94f31e61036b8ab6a9384bac0980f4) --- devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index 2781edf9f02f8..bdcd48eeff5d3 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -588,6 +588,13 @@ private String handleAutoCompile() throws MojoExecutionException { continue; } for (PluginExecution e : p.getExecutions()) { + if (e.getPhase() != null && !PRE_DEV_MODE_PHASES.contains(e.getPhase())) { + // skip executions with phases post quarkus:dev, such as install, deploy, site, etc + if (getLog().isDebugEnabled()) { + getLog().debug("Skipping " + e.getId() + " of " + p.getId()); + } + continue; + } String goalPrefix = null; if (!e.getGoals().isEmpty()) { goalPrefix = getMojoDescriptor(p, e.getGoals().get(0)).getPluginDescriptor().getGoalPrefix(); From b47f807319f8d856370dfb4054a460e206b2f5a6 Mon Sep 17 00:00:00 2001 From: Paulo Casaes Date: Fri, 1 Mar 2024 11:15:41 -0800 Subject: [PATCH 34/34] Do not fail on resolve kafka streams topics when topics check disabled Resolves https://github.com/quarkusio/quarkus/issues/39120 (cherry picked from commit 809c48c43b649e68cf99d2e96b5ec45f10c26f0e) --- .../kafka/streams/runtime/KafkaStreamsProducer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java b/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java index f03ddabeb9bda..604017f831591 100644 --- a/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java +++ b/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java @@ -109,18 +109,22 @@ public KafkaStreamsProducer(KafkaStreamsSupport kafkaStreamsSupport, KafkaStream this.executorService = executorService; this.topicsTimeout = runtimeConfig.topicsTimeout; - this.trimmedTopics = runtimeConfig.getTrimmedTopics(); + this.trimmedTopics = isTopicsCheckEnabled() ? runtimeConfig.getTrimmedTopics() : Collections.emptyList(); this.streamsConfig = new StreamsConfig(kafkaStreamsProperties); this.kafkaStreams = initializeKafkaStreams(streamsConfig, topology.get(), kafkaClientSupplier, stateListener, globalStateRestoreListener, uncaughtExceptionHandlerListener); this.kafkaStreamsTopologyManager = new KafkaStreamsTopologyManager(kafkaAdminClient); } + private boolean isTopicsCheckEnabled() { + return topicsTimeout.compareTo(Duration.ZERO) > 0; + } + public void onStartup(@Observes StartupEvent event, Event kafkaStreamsEvent) { if (kafkaStreams != null) { kafkaStreamsEvent.fire(kafkaStreams); executorService.execute(() -> { - if (topicsTimeout.compareTo(Duration.ZERO) > 0) { + if (isTopicsCheckEnabled()) { try { waitForTopicsToBeCreated(kafkaAdminClient, trimmedTopics, topicsTimeout); } catch (InterruptedException e) {