From f55eef68d467bbf8b41605908cc7ab2d53e09871 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:45:13 +0000 Subject: [PATCH 01/13] Bump io.quarkus:quarkus-platform-bom-maven-plugin Bumps [io.quarkus:quarkus-platform-bom-maven-plugin](https://github.com/quarkusio/quarkus-platform-bom-generator) from 0.0.100 to 0.0.101. - [Release notes](https://github.com/quarkusio/quarkus-platform-bom-generator/releases) - [Commits](https://github.com/quarkusio/quarkus-platform-bom-generator/compare/0.0.100...0.0.101) --- updated-dependencies: - dependency-name: io.quarkus:quarkus-platform-bom-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 6e1f5a80895a16465aa06752227a132c40beee5c) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3e14413b69ea7..e041b8d91bff9 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ jdbc:postgresql:hibernate_orm_test 4.5.1 - 0.0.100 + 0.0.101 false false From 5bc610aa74921a6a23fd8f2fd1f621615009dd88 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 11 Dec 2023 14:10:03 +0100 Subject: [PATCH 02/13] Make sure dev mode is properly written in doc It should be `dev mode` with a space and no capitalization. (cherry picked from commit 33a4b2500ed2202e176cf91e8df19359c9b3a98b) --- docs/src/main/asciidoc/security-getting-started-tutorial.adoc | 4 ++-- docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc | 4 ++-- .../main/asciidoc/security-openid-connect-dev-services.adoc | 2 +- .../main/asciidoc/security-openid-connect-multitenancy.adoc | 2 +- docs/src/main/asciidoc/security-openid-connect-providers.adoc | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc index da06f8669bc1a..30d84156ad5fa 100644 --- a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc +++ b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc @@ -462,9 +462,9 @@ As you can see in this code sample, you do not need to start the test container [NOTE] ==== -When you start your application in dev mode, `Dev Services for PostgreSQL` launches a `PostgreSQL` `devmode` container so that you can start developing your application. +When you start your application in dev mode, Dev Services for PostgreSQL launches a PostgreSQL dev mode container so that you can start developing your application. While developing your application, you can add tests one by one and run them using the xref:continuous-testing.adoc[Continuous Testing] feature. -`Dev Services for PostgreSQL` supports testing while you develop by providing a separate `PostgreSQL` test container that does not conflict with the `devmode` container. +Dev Services for PostgreSQL supports testing while you develop by providing a separate PostgreSQL test container that does not conflict with the dev mode container. ==== === Use Curl or a browser to test your application diff --git a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc index f652fa7ca262a..e0796c3b007c0 100644 --- a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc +++ b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc @@ -914,7 +914,7 @@ public class GreetingResourceTest { } ---- -If you recall, when the application was started in devmode, the following could be seen in the CLI window: +If you recall, when the application was started in dev mode, the following could be seen in the CLI window: image::auth0-devmode-started.png[Auth0 DevMode started] @@ -1075,7 +1075,7 @@ Open a browser, access http://localhost:8080/hello and get the name displayed in == Troubleshooting -The steps described in this tutorial should work exactly as the tutorial describes. You might have to clear the browser cookies when accessing the updated Quarkus endpoint if you have already completed the authentication. You might need to restart the Quarkus application manually in devmode but it is not expected. If you need help completing this tutorial, you can get in touch with the Quarkus team. +The steps described in this tutorial should work exactly as the tutorial describes. You might have to clear the browser cookies when accessing the updated Quarkus endpoint if you have already completed the authentication. You might need to restart the Quarkus application manually in dev mode but it is not expected. If you need help completing this tutorial, you can get in touch with the Quarkus team. == Summary diff --git a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc index 0d99374a5b348..dbb796ef7f5c9 100644 --- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc @@ -204,7 +204,7 @@ To make Dev UI more useful for supporting the development of OIDC `web-app` appl It will ensure that all Dev UI options described in <> will be available when your `web-app` application is run in dev mode. The limitation of this approach is that both access and ID tokens returned with the code flow and acquired with Dev UI will be sent to the endpoint as HTTP `Bearer` tokens - which will not work well if your endpoint requires the injection of `IdToken`. However, it will work as expected if your `web-app` application only uses the access token, for example, as a source of roles or to get `UserInfo`, even if it is assumed to be a `service` application in dev mode. -Even a better option is to use a `hybrid` application type in devmode: +Even a better option is to use a `hybrid` application type in dev mode: [source,properties] ---- diff --git a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc index 7c9bb2dd06daf..70894fafbc9ab 100644 --- a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc @@ -270,7 +270,7 @@ public class CustomTenantResolver implements TenantResolver { You can define multiple tenants in your configuration file, just make sure they have a unique alias so that you can map them properly when resolving a tenant from your `TenantResolver` implementation. -However, using a static tenant resolution (configuring tenants in `application.properties` and resolving them with `TenantResolver`) prevents testing the endpoint with `Dev Services for Keycloak` since `Dev Services for Keycloak` has no knowledge of how the requests will be mapped to individual tenants and can not dynamically provide tenant-specific `quarkus.oidc..auth-server-url` values and therefore using `%prod` prefixes with the tenant-specific URLs in `application.properties` will not work in tests or devmode. +However, using a static tenant resolution (configuring tenants in `application.properties` and resolving them with `TenantResolver`) prevents testing the endpoint with `Dev Services for Keycloak` since `Dev Services for Keycloak` has no knowledge of how the requests will be mapped to individual tenants and can not dynamically provide tenant-specific `quarkus.oidc..auth-server-url` values and therefore using `%prod` prefixes with the tenant-specific URLs in `application.properties` will not work in test or dev mode. [NOTE] ==== diff --git a/docs/src/main/asciidoc/security-openid-connect-providers.adoc b/docs/src/main/asciidoc/security-openid-connect-providers.adoc index 2c0d7860d3c40..79a39a1e0aa7a 100644 --- a/docs/src/main/asciidoc/security-openid-connect-providers.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-providers.adoc @@ -662,7 +662,7 @@ The pattern of authenticating with a given provider, where the endpoint uses eit == HTTPS Redirect URL -Some providers will only accept HTTPS-based redirect URLs. Tools such as https://ngrok.com/[ngrok] https://linuxhint.com/set-up-use-ngrok/[can be set up] to help testing such providers with Quarkus endpoints running on localhost in devmode. +Some providers will only accept HTTPS-based redirect URLs. Tools such as https://ngrok.com/[ngrok] https://linuxhint.com/set-up-use-ngrok/[can be set up] to help testing such providers with Quarkus endpoints running on localhost in dev mode. == Rate Limiting From 610141c617798d0af0cff9711e4f8fdaca5ba1dd Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 13 Dec 2023 10:25:40 +0200 Subject: [PATCH 03/13] Use NoStackTraceException in metrics This is done because the stacktrace produced is completely useless and only makes it harder to focus on the real problem Relates to: https://github.com/quarkiverse/quarkus-langchain4j/issues/140 (cherry picked from commit 542e93b2edd46bbbef0bb350ad6f656be00d0c67) --- .../runtime/binder/vertx/VertxMeterBinderAdapter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderAdapter.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderAdapter.java index e3a4b7de2a890..384f10ed0bc43 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderAdapter.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxMeterBinderAdapter.java @@ -10,6 +10,7 @@ import io.vertx.core.datagram.DatagramSocketOptions; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.impl.NoStackTraceException; import io.vertx.core.metrics.MetricsOptions; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.NetServerOptions; @@ -60,7 +61,7 @@ public MetricsOptions newOptions() { @Override public HttpServerMetrics createHttpServerMetrics(HttpServerOptions options, SocketAddress localAddress) { if (httpBinderConfiguration == null) { - throw new IllegalStateException("HttpBinderConfiguration was not found"); + throw new NoStackTraceException("HttpBinderConfiguration was not found"); } if (httpBinderConfiguration.isServerEnabled()) { log.debugf("Create HttpServerMetrics with options %s and address %s", options, localAddress); From df70f9984b4635b68e3e62e18a23b0d9fd5519b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Wed, 13 Dec 2023 15:14:54 +0100 Subject: [PATCH 04/13] Docs: Fix incorrect link reference in Cross-Site Request Forgery Prevention guide (cherry picked from commit 72afeacb8f73efc7db27aead4675eb3c523783b2) --- docs/src/main/asciidoc/security-csrf-prevention.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/security-csrf-prevention.adoc b/docs/src/main/asciidoc/security-csrf-prevention.adoc index 880487683477f..b56e7d49ad941 100644 --- a/docs/src/main/asciidoc/security-csrf-prevention.adoc +++ b/docs/src/main/asciidoc/security-csrf-prevention.adoc @@ -11,7 +11,7 @@ include::_attributes.adoc[] https://owasp.org/www-community/attacks/csrf[Cross-Site Request Forgery (CSRF)] is an attack that forces an end user to execute unwanted actions on a web application in which they are currently authenticated. -Quarkus Security provides a CSRF prevention feature which implements https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie[Double Submit Cookie] and [CSRF Request Header] techniques. +Quarkus Security provides a CSRF prevention feature which implements https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie[Double Submit Cookie] and <> techniques. `Double Submit Cookie` technique requires that the CSRF token sent as `HTTPOnly`, optionally signed, cookie to the client, and directly embedded in a hidden form input of server-side rendered HTML forms, or submitted as a request header value. @@ -139,6 +139,7 @@ You can get `HMAC` signatures created for the generated CSRF tokens and have the quarkus.csrf-reactive.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow ---- +[[csrf-request-header]] == CSRF Request Header If HTML `form` tags are not used and you need to pass CSRF token as a header, then inject the header name and token, for example, into HTMX: From f9870f6202cba88aec81346a3094bf94dc330cdd Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 13 Dec 2023 18:18:55 +0000 Subject: [PATCH 05/13] Do not use CSRF cookie as the next token value (cherry picked from commit 0e4097c26f766c5b4ab1a1a646d6cd202ef96240) --- .../CsrfRequestResponseReactiveFilter.java | 13 ++++---- .../java/io/quarkus/it/csrf/TestResource.java | 30 +++++++++++++++++++ .../src/main/resources/application.properties | 2 +- .../templates/csrfTokenFirstForm.html | 17 +++++++++++ .../templates/csrfTokenSecondForm.html | 17 +++++++++++ .../io/quarkus/it/csrf/CsrfReactiveTest.java | 30 +++++++++++++++++++ 6 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenFirstForm.html create mode 100644 integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenSecondForm.html diff --git a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java index 694b690fe797f..b75764772a912 100644 --- a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java +++ b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java @@ -66,8 +66,6 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi String cookieToken = getCookieToken(routing, config); if (cookieToken != null) { - routing.put(CSRF_TOKEN_KEY, cookieToken); - try { int cookieTokenSize = Base64.getUrlDecoder().decode(cookieToken).length; // HMAC SHA256 output is 32 bytes long @@ -98,10 +96,10 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi // unsafe HTTP method, token is required // Check the header first - String csrfTokenInHeader = requestContext.getHeaderString(config.tokenHeaderName); - if (csrfTokenInHeader != null) { + String csrfTokenHeaderParam = requestContext.getHeaderString(config.tokenHeaderName); + if (csrfTokenHeaderParam != null) { LOG.debugf("CSRF token found in the token header"); - verifyCsrfToken(requestContext, routing, config, cookieToken, csrfTokenInHeader); + verifyCsrfToken(requestContext, routing, config, cookieToken, csrfTokenHeaderParam); return; } @@ -128,9 +126,9 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi ResteasyReactiveRequestContext rrContext = (ResteasyReactiveRequestContext) requestContext .getServerRequestContext(); - String csrfToken = (String) rrContext.getFormParameter(config.formFieldName, true, false); + String csrfTokenFormParam = (String) rrContext.getFormParameter(config.formFieldName, true, false); LOG.debugf("CSRF token found in the form parameter"); - verifyCsrfToken(requestContext, routing, config, cookieToken, csrfToken); + verifyCsrfToken(requestContext, routing, config, cookieToken, csrfTokenFormParam); return; } else if (cookieToken == null) { @@ -159,6 +157,7 @@ private void verifyCsrfToken(ResteasyReactiveContainerRequestContext requestCont requestContext.abortWith(badClientRequest()); return; } else { + routing.put(CSRF_TOKEN_KEY, csrfToken); routing.put(CSRF_TOKEN_VERIFIED, true); return; } diff --git a/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java b/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java index 0f66abf3ed4a1..d3c7b18c47306 100644 --- a/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java +++ b/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java @@ -29,6 +29,12 @@ public class TestResource { @Inject Template csrfTokenForm; + @Inject + Template csrfTokenFirstForm; + + @Inject + Template csrfTokenSecondForm; + @Inject Template csrfTokenHeader; @@ -49,6 +55,14 @@ public TemplateInstance getCsrfTokenForm() { return csrfTokenForm.instance(); } + @GET + @Path("/csrfTokenFirstForm") + @Produces(MediaType.TEXT_HTML) + @Authenticated + public TemplateInstance getCsrfTokenFirstForm() { + return csrfTokenFirstForm.instance(); + } + @GET @Path("/csrfTokenWithFormRead") @Produces(MediaType.TEXT_HTML) @@ -71,6 +85,22 @@ public String postCsrfTokenForm(@FormParam("name") String name, @HeaderParam("X- return name + ":" + routingContext.get("csrf_token_verified", false) + ":tokenHeaderIsSet=" + (csrfHeader != null); } + @POST + @Path("/csrfTokenFirstForm") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.TEXT_HTML) + public TemplateInstance postCsrfTokenFirstForm() { + return csrfTokenSecondForm.instance(); + } + + @POST + @Path("/csrfTokenSecondForm") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.TEXT_PLAIN) + public String postCsrfTokenSecondForm(@FormParam("name") String name, @HeaderParam("X-CSRF-TOKEN") String csrfHeader) { + return name + ":" + routingContext.get("csrf_token_verified", false) + ":tokenHeaderIsSet=" + (csrfHeader != null); + } + @POST @Path("/csrfTokenWithFormRead") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) diff --git a/integration-tests/csrf-reactive/src/main/resources/application.properties b/integration-tests/csrf-reactive/src/main/resources/application.properties index 23f5d88b8c1f0..9a778c50dda03 100644 --- a/integration-tests/csrf-reactive/src/main/resources/application.properties +++ b/integration-tests/csrf-reactive/src/main/resources/application.properties @@ -1,5 +1,5 @@ quarkus.csrf-reactive.cookie-name=csrftoken -quarkus.csrf-reactive.create-token-path=/service/csrfTokenForm,/service/csrfTokenWithFormRead,/service/csrfTokenMultipart,/service/csrfTokenWithHeader +quarkus.csrf-reactive.create-token-path=/service/csrfTokenForm,/service/csrfTokenFirstForm,/service/csrfTokenSecondForm,/service/csrfTokenWithFormRead,/service/csrfTokenMultipart,/service/csrfTokenWithHeader quarkus.csrf-reactive.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow quarkus.http.auth.basic=true diff --git a/integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenFirstForm.html b/integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenFirstForm.html new file mode 100644 index 0000000000000..71dadecdf41e2 --- /dev/null +++ b/integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenFirstForm.html @@ -0,0 +1,17 @@ + + + + +CSRF Token First Form Test + + +

CSRF Test

+ +
+ + +

Your Name:

+

+
+ + diff --git a/integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenSecondForm.html b/integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenSecondForm.html new file mode 100644 index 0000000000000..7c28b45f662a6 --- /dev/null +++ b/integration-tests/csrf-reactive/src/main/resources/templates/csrfTokenSecondForm.html @@ -0,0 +1,17 @@ + + + + +CSRF Token Second Form Test + + +

CSRF Test

+ +
+ + +

Your Name:

+

+
+ + diff --git a/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java b/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java index e6d50f0fc39b7..770d36441e300 100644 --- a/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java +++ b/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java @@ -61,6 +61,36 @@ public void testCsrfTokenInForm() throws Exception { } } + @Test + public void testCsrfTokenTwoForms() throws Exception { + try (final WebClient webClient = createWebClient()) { + webClient.addRequestHeader("Authorization", basicAuth("alice", "alice")); + HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenFirstForm"); + + assertEquals("CSRF Token First Form Test", htmlPage.getTitleText()); + + HtmlForm loginForm = htmlPage.getForms().get(0); + + loginForm.getInputByName("name").setValueAttribute("alice"); + + assertNotNull(webClient.getCookieManager().getCookie("csrftoken")); + + htmlPage = loginForm.getInputByName("submit").click(); + + assertEquals("CSRF Token Second Form Test", htmlPage.getTitleText()); + + loginForm = htmlPage.getForms().get(0); + + loginForm.getInputByName("name").setValueAttribute("alice"); + + TextPage textPage = loginForm.getInputByName("submit").click(); + assertNotNull(webClient.getCookieManager().getCookie("csrftoken")); + assertEquals("alice:true:tokenHeaderIsSet=false", textPage.getContent()); + + webClient.getCookieManager().clearCookies(); + } + } + @Test public void testCsrfTokenWithFormRead() throws Exception { try (final WebClient webClient = createWebClient()) { From 42a4539feb898ce6fc6d8d88a251df93d79f0675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Wed, 13 Dec 2023 13:54:23 +0100 Subject: [PATCH 06/13] Remove the driver property in the documentation for Cloud SQL (cherry picked from commit 7e3b81a4ba80218ec49b957bd606bf9ed2da4c69) --- docs/src/main/asciidoc/deploying-to-google-cloud.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc index 534901043c870..a2259e91ce324 100644 --- a/docs/src/main/asciidoc/deploying-to-google-cloud.adoc +++ b/docs/src/main/asciidoc/deploying-to-google-cloud.adoc @@ -251,7 +251,6 @@ Finally, you need to configure your datasource specifically to use the socket fa ---- quarkus.datasource.db-kind=postgresql quarkus.datasource.jdbc.url=jdbc:postgresql:///mydatabase <1> -quarkus.datasource.jdbc.driver=org.postgresql.Driver quarkus.datasource.username=quarkus quarkus.datasource.password=quarkus quarkus.datasource.jdbc.additional-jdbc-properties.cloudSqlInstance=project-id:gcp-region:instance <2> From ce9fd600fb1c96193389f91e3c7aafc43521555b Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Fri, 15 Dec 2023 13:22:35 +0100 Subject: [PATCH 07/13] docs(rabbitmq): fix typo replace MINE type with MIME type (cherry picked from commit aecf4a6a52e8ea8b97038f22593bf004d01347c9) --- docs/src/main/asciidoc/rabbitmq-reference.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/rabbitmq-reference.adoc b/docs/src/main/asciidoc/rabbitmq-reference.adoc index bd421a5b2b202..d264e5aa97e47 100644 --- a/docs/src/main/asciidoc/rabbitmq-reference.adoc +++ b/docs/src/main/asciidoc/rabbitmq-reference.adoc @@ -714,7 +714,7 @@ Type: _boolean_ | false | `false` Type: _string_ | false | `#` -| [.no-hyphens]#*content-type-override*# | Override the content_type attribute of the incoming message, should be a valid MINE type +| [.no-hyphens]#*content-type-override*# | Override the content_type attribute of the incoming message, should be a valid MIME type Type: _string_ | false | From aa99a21885c190bce88c4bee9d3556b1a5cf58cb Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 15 Dec 2023 12:33:54 +0200 Subject: [PATCH 08/13] Add SequencedCollection to BANNED_INTERFACE_TYPES This is done because this type exists in Java 21 but not Java 17. Fixes: #37768 (cherry picked from commit 674eac22b98f33ce589e66ecfe1f3053e31372c0) --- .../src/main/java/io/quarkus/arc/processor/Types.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java index 27340b943a063..76693f02a196b 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java @@ -84,7 +84,8 @@ public final class Types { // TODO: add a extensible banning mechanism based on predicates if we find that this set needs to grow... private static final Set BANNED_INTERFACE_TYPES = new HashSet<>( Arrays.asList(DotName.createSimple("java.lang.constant.ConstantDesc"), - DotName.createSimple("java.lang.constant.Constable"))); + DotName.createSimple("java.lang.constant.Constable"), + DotName.createSimple("java.util.SequencedCollection"))); private Types() { } From ad057b6b6b1e44275e3e79afbe21e59e73bc9ddd Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Sat, 16 Dec 2023 18:35:55 +0200 Subject: [PATCH 09/13] Take priority into account in ConfigurationImpl This is necessary for REST Client MessageBodyReader and MessageBodyWriter registration (cherry picked from commit e7fcd327371250b7551475a5e008b471d9ea5eb7) --- .../ContextProvidersPriorityTest.java | 26 +++++++++++++++++++ .../common/jaxrs/ConfigurationImpl.java | 6 +++++ 2 files changed, 32 insertions(+) diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ContextProvidersPriorityTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ContextProvidersPriorityTest.java index c10b4923d2f1f..740d1782f26b9 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ContextProvidersPriorityTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ContextProvidersPriorityTest.java @@ -4,6 +4,10 @@ import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; import java.net.URI; import java.util.List; import java.util.Map; @@ -13,17 +17,24 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +import jakarta.ws.rs.Priorities; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.ext.ContextResolver; +import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory; +import org.jboss.resteasy.reactive.server.jackson.JacksonBasicMessageBodyReader; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import com.fasterxml.jackson.databind.ObjectMapper; + import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder; import io.quarkus.rest.client.reactive.TestJacksonBasicMessageBodyReader; import io.quarkus.test.QuarkusUnitTest; @@ -78,6 +89,7 @@ public Map> callClient(String uri) { } } + @RegisterProvider(ErroneousJacksonBasicMessageBodyReader.class) public interface Client { @GET Map> get(); @@ -101,6 +113,20 @@ public ClientHeadersFactory getContext(Class aClass) { } } + @Priority(Priorities.USER + 100) + public static class ErroneousJacksonBasicMessageBodyReader extends JacksonBasicMessageBodyReader { + public ErroneousJacksonBasicMessageBodyReader() { + super(new ObjectMapper()); + } + + @Override + public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + throw new IllegalStateException("should never be called"); + } + } + public static class CustomClientHeadersFactory implements ClientHeadersFactory { private final String value; diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/ConfigurationImpl.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/ConfigurationImpl.java index 72a269f62e419..26af7a3155e4e 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/ConfigurationImpl.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/jaxrs/ConfigurationImpl.java @@ -280,6 +280,9 @@ private void register(Object component, Integer priority) { resourceReader .setMediaTypeStrings( consumes != null ? Arrays.asList(consumes.value()) : WILDCARD_STRING_LIST); + if (priority != null) { + resourceReader.setPriority(priority); + } Type[] args = Types.findParameterizedTypes(componentClass, MessageBodyReader.class); resourceReaders.add(args != null && args.length == 1 ? Types.getRawType(args[0]) : Object.class, resourceReader); @@ -298,6 +301,9 @@ private void register(Object component, Integer priority) { resourceWriter .setMediaTypeStrings( produces != null ? Arrays.asList(produces.value()) : WILDCARD_STRING_LIST); + if (priority != null) { + resourceWriter.setPriority(priority); + } Type[] args = Types.findParameterizedTypes(componentClass, MessageBodyWriter.class); resourceWriters.add(args != null && args.length == 1 ? Types.getRawType(args[0]) : Object.class, resourceWriter); From 958034351df3b37ee04a3f9dbd7d4d5fb1c976dc Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sun, 17 Dec 2023 18:53:04 +0100 Subject: [PATCH 10/13] Only update ~/.docker/config.json if it exists Not having this file is probably due to GitHub infra issues but let's not fail the build for that. (cherry picked from commit 0f388725cea5fb875702e2fef264bc5fdf23b526) --- .github/workflows/ci-actions-incremental.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-actions-incremental.yml b/.github/workflows/ci-actions-incremental.yml index c4e8f1af7e5a2..ce4078e4abf9e 100644 --- a/.github/workflows/ci-actions-incremental.yml +++ b/.github/workflows/ci-actions-incremental.yml @@ -835,7 +835,9 @@ jobs: # We do this so we can get better analytics for the downloaded version of the build images - name: Update Docker Client User Agent run: | - cat <<< $(jq '.HttpHeaders += {"User-Agent": "Quarkus-CI-Docker-Client"}' ~/.docker/config.json) > ~/.docker/config.json + if [ -f ~/.docker/config.json ]; then + cat <<< $(jq '.HttpHeaders += {"User-Agent": "Quarkus-CI-Docker-Client"}' ~/.docker/config.json) > ~/.docker/config.json + fi - name: Build env: TEST_MODULES: ${{matrix.test-modules}} From 8f275232a700a84e7e923d5c82313ea8c57c873f Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Mon, 18 Dec 2023 10:39:33 +0100 Subject: [PATCH 11/13] Dev mode: add null checks to TimestampSet.isRestartNeeded() - we observed NPE in some edge cases during live reload (cherry picked from commit 3fb7924bbf12f6289a0160f807ed83ad80de3363) --- .../deployment/dev/RuntimeUpdatesProcessor.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index 24a1c175a3059..0e44200ee8043 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -1309,12 +1309,14 @@ boolean isRestartNeeded(String changedFile) { } } // Then try to match a new file that was added to a resource root - Boolean ret = watchedFilePaths.get(changedFile); + Boolean ret = watchedFilePaths != null ? watchedFilePaths.get(changedFile) : null; if (ret == null) { - ret = false; - for (Entry, Boolean> e : watchedFilePredicates) { - if (e.getKey().test(changedFile)) { - ret = ret || e.getValue(); + ret = Boolean.FALSE; + if (watchedFilePredicates != null) { + for (Entry, Boolean> e : watchedFilePredicates) { + if (e.getKey().test(changedFile)) { + ret = ret || e.getValue(); + } } } } From 768e2a2f1cc0a3beb8530e70d0f412b99e00d101 Mon Sep 17 00:00:00 2001 From: Frantisek Havel <42615282+fhavel@users.noreply.github.com> Date: Thu, 14 Dec 2023 09:18:58 +0100 Subject: [PATCH 12/13] Query logging is beeing done in io.quarkus.mongodb.panache.common.runtime.MongoOperations (cherry picked from commit f3746cb7d909fb1044fe274ada9d79d967fe7b51) --- docs/src/main/asciidoc/mongodb-panache.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/mongodb-panache.adoc b/docs/src/main/asciidoc/mongodb-panache.adoc index 0acbb0a762fe9..1c2140715c483 100644 --- a/docs/src/main/asciidoc/mongodb-panache.adoc +++ b/docs/src/main/asciidoc/mongodb-panache.adoc @@ -746,7 +746,7 @@ This can be achieved by setting to DEBUG the following log category inside your [source,properties] ---- -quarkus.log.category."io.quarkus.mongodb.panache.runtime".level=DEBUG +quarkus.log.category."io.quarkus.mongodb.panache.common.runtime".level=DEBUG ---- == The PojoCodecProvider: easy object to BSON document conversion. From 27bf076c8a1b7ea44d8debe0a8092aa2ed993f4b Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 18 Dec 2023 14:47:44 +0100 Subject: [PATCH 13/13] CLI - Rework how missing commands are detected We had a problem here as most options are only valid in the context of the root command (for instance -D) so trying to parse them in the context of a subcommand or on their own will most probably fail. Also parsing recursively the command is non-efficient as we do a lot of parsing, especially since it was done twice (this was fixed too). The new approach uses the ParseResult generated once and get the knownledge from there. From my tests, it provides similar results and avoid false positives and running the parsing too many times. This should fix CliHelpTest#testCommandHelp() being flaky as not being able to interpret the options ended up triggering the missing command branch and then the JBang support. (cherry picked from commit fb4a3799d3ea42b8267000455e734486bdef1dcb) --- .../main/java/io/quarkus/cli/QuarkusCli.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 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 b08ebf2ec4c3a..2948d61ed43b5 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/QuarkusCli.java @@ -12,6 +12,7 @@ import java.util.Optional; import java.util.concurrent.Callable; import java.util.function.Supplier; +import java.util.stream.Collectors; import jakarta.inject.Inject; @@ -96,7 +97,9 @@ public int run(String... args) throws Exception { boolean pluginCommand = args.length >= 1 && (args[0].equals("plug") || args[0].equals("plugin")); try { - boolean existingCommand = checkMissingCommand(cmd, args).isEmpty(); + Optional missingCommand = checkMissingCommand(cmd, args); + + boolean existingCommand = missingCommand.isEmpty(); // 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. @@ -108,8 +111,7 @@ public int run(String... args) throws Exception { pluginManager.syncIfNeeded(); Map plugins = new HashMap<>(pluginManager.getInstalledPlugins()); pluginCommandFactory.populateCommands(cmd, plugins); - Optional missing = checkMissingCommand(cmd, args); - missing.ifPresent(m -> { + missingCommand.ifPresent(m -> { try { Map installable = pluginManager.getInstallablePlugins(); if (installable.containsKey(m)) { @@ -119,11 +121,13 @@ public int run(String... args) throws Exception { output.info("Command %s not installed but the following plugin is available:\n%s", m, table.getContent()); if (interactiveMode && Prompt.yesOrNo(true, - "Would you like to install it now ?", + "Would you like to install it now?", args)) { pluginManager.addPlugin(m).ifPresent(added -> plugins.put(added.getName(), added)); pluginCommandFactory.populateCommands(cmd, plugins); } + } else { + output.error("Command %s is missing and can't be installed.", m); } } catch (Exception e) { output.error("Command %s is missing and can't be installed.", m); @@ -136,7 +140,7 @@ public int run(String... args) throws Exception { } /** - * Recursivelly processes the arguments passed to the command and checks wether a subcommand is missing. + * Process the arguments passed and return an identifier of the potentially missing subcommand if any. * * @param root the root command * @param args the arguments passed to the root command @@ -148,17 +152,26 @@ public Optional checkMissingCommand(CommandLine root, String[] args) { } try { - ParseResult result = root.parseArgs(args); - if (args.length == 1) { - return Optional.empty(); - } - CommandLine next = root.getSubcommands().get(args[0]); - if (next == null) { - return Optional.of(args[0]); - } - String[] remaining = new String[args.length - 1]; - System.arraycopy(args, 1, remaining, 0, remaining.length); - return checkMissingCommand(next, remaining).map(nextMissing -> root.getCommandName() + "-" + nextMissing); + ParseResult currentParseResult = root.parseArgs(args); + StringBuilder missingCommand = new StringBuilder(); + + do { + if (missingCommand.length() > 0) { + missingCommand.append("-"); + } + missingCommand.append(currentParseResult.commandSpec().name()); + + List unmatchedSubcommands = currentParseResult.unmatched().stream() + .filter(u -> !u.startsWith("-")).collect(Collectors.toList()); + if (!unmatchedSubcommands.isEmpty()) { + missingCommand.append("-").append(unmatchedSubcommands.get(0)); + return Optional.of(missingCommand.toString()); + } + + currentParseResult = currentParseResult.subcommand(); + } while (currentParseResult != null); + + return Optional.empty(); } catch (UnmatchedArgumentException e) { return Optional.of(args[0]); }