From 591964112e0aea7a5b4d27a71d32b1f36177d043 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 28 Jun 2023 15:45:06 +0200 Subject: [PATCH 01/12] Add .mvn/.gradle-enterprise/ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b96f120069f92..0a31845fb9f79 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ nb-configuration.xml .sdkmanrc .envrc .jekyll-cache +.mvn/.gradle-enterprise/ From e42b4742ae64f01bb9d537893a077c806490b402 Mon Sep 17 00:00:00 2001 From: polarctos Date: Thu, 27 Apr 2023 00:29:01 +0200 Subject: [PATCH 02/12] Fail on attempt to set custom hostname verifier Both custom HostnameVerifier and SSLContext are currently not supported by resteasy-reactive-client. This is documented as known limitations. This change aligns the implementation to the already present state for SSLContext. It makes the builder fail when HostnameVerifier is tried to be configured. The benefit is that it fails as early as currently possible and not only when the custom hostname verification would be needed later on. Switch both to UnsupportedOperationException. On the rest-client-reactive settings a custom hostname verifier is currently not supported. Adapted the tests accordingly as the builder fails early now throwing UnsupportedOperationException. (cherry picked from commit 6ce558fcea38869662c8ba18743b96940549e502) --- .../client/reactive/ConfigurationTest.java | 3 --- .../reactive/GlobalConfigurationTest.java | 2 -- .../reactive/HelloClientWithBaseUri.java | 9 -------- .../configuration-test-application.properties | 4 ---- ...-configuration-test-application.properties | 1 - .../RestClientCDIDelegateBuilderTest.java | 22 ------------------- .../client/impl/ClientBuilderImpl.java | 6 ++--- 7 files changed, 3 insertions(+), 44 deletions(-) diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java index 46b11caaa075e..d2d1047f50ed7 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/ConfigurationTest.java @@ -78,9 +78,6 @@ private void verifyClientConfig(RestClientConfig clientConfig, boolean checkExtr assertThat(clientConfig.followRedirects.get()).isEqualTo(true); assertThat(clientConfig.queryParamStyle).isPresent(); assertThat(clientConfig.queryParamStyle.get()).isEqualTo(QueryParamStyle.COMMA_SEPARATED); - assertThat(clientConfig.hostnameVerifier).isPresent(); - assertThat(clientConfig.hostnameVerifier.get()) - .isEqualTo("io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyHostnameVerifier"); if (checkExtraProperties) { assertThat(clientConfig.connectionTTL).isPresent(); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java index c08bc21756686..545fe54383a21 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/GlobalConfigurationTest.java @@ -64,8 +64,6 @@ void checkGlobalConfigValues() { assertThat(configRoot.readTimeout).isEqualTo(2001); assertThat(configRoot.userAgent.get()).isEqualTo("agent"); assertThat(configRoot.headers).isEqualTo(Collections.singletonMap("foo", "bar")); - assertThat(configRoot.hostnameVerifier.get()) - .isEqualTo("io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyHostnameVerifier"); assertThat(configRoot.connectionTTL.get()).isEqualTo(20000); // value in ms, will be converted to seconds assertThat(configRoot.connectionPoolSize.get()).isEqualTo(2); assertThat(configRoot.keepAliveEnabled.get()).isTrue(); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/HelloClientWithBaseUri.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/HelloClientWithBaseUri.java index 1a947b9261921..53012935d2508 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/HelloClientWithBaseUri.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/HelloClientWithBaseUri.java @@ -1,7 +1,5 @@ package io.quarkus.rest.client.reactive; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSession; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -30,11 +28,4 @@ public void filter(ClientRequestContext requestContext, ClientResponseContext re } } - class MyHostnameVerifier implements HostnameVerifier { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - } - } diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/configuration-test-application.properties b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/configuration-test-application.properties index d240d98e8330d..5d886106d9c30 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/configuration-test-application.properties +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/configuration-test-application.properties @@ -2,7 +2,6 @@ io.quarkus.rest.client.reactive.HelloClientWithBaseUri/mp-rest/url=http://localhost:${quarkus.http.test-port:8081}/invalid-endpoint io.quarkus.rest.client.reactive.HelloClientWithBaseUri/mp-rest/scope=InvalidScope io.quarkus.rest.client.reactive.HelloClientWithBaseUri/mp-rest/providers=InvalidProvider -io.quarkus.rest.client.reactive.HelloClientWithBaseUri/mp-rest/hostnameVerifier=InvalidVerifier # client identified by class name quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".url=http://localhost:${quarkus.http.test-port:8081}/hello @@ -13,7 +12,6 @@ quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".rea quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".follow-redirects=true #quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".proxy-address=localhost:8080 quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".query-param-style=COMMA_SEPARATED -quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".hostname-verifier=io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyHostnameVerifier quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".connection-ttl=30000 quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".connection-pool-size=10 quarkus.rest-client."io.quarkus.rest.client.reactive.HelloClientWithBaseUri".keep-alive-enabled=false @@ -30,7 +28,6 @@ quarkus.rest-client.client-prefix.read-timeout=6000 quarkus.rest-client.client-prefix.follow-redirects=true quarkus.rest-client.client-prefix.proxy-address=localhost:8080 quarkus.rest-client.client-prefix.query-param-style=COMMA_SEPARATED -quarkus.rest-client.client-prefix.hostname-verifier=io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyHostnameVerifier quarkus.rest-client.client-prefix.connection-ttl=30000 quarkus.rest-client.client-prefix.connection-pool-size=10 quarkus.rest-client.client-prefix.keep-alive-enabled=false @@ -54,4 +51,3 @@ mp-client-prefix/mp-rest/readTimeout=6000 mp-client-prefix/mp-rest/followRedirects=true mp-client-prefix/mp-rest/proxyAddress=localhost:8080 mp-client-prefix/mp-rest/queryParamStyle=COMMA_SEPARATED -mp-client-prefix/mp-rest/hostnameVerifier=io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyHostnameVerifier diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/global-configuration-test-application.properties b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/global-configuration-test-application.properties index 473ed229a6259..1a1964e6f63d7 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/global-configuration-test-application.properties +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/resources/global-configuration-test-application.properties @@ -18,7 +18,6 @@ quarkus.rest-client.connect-timeout=2000 quarkus.rest-client.read-timeout=2001 quarkus.rest-client.user-agent=agent quarkus.rest-client.headers.foo=bar -quarkus.rest-client.hostname-verifier=io.quarkus.rest.client.reactive.HelloClientWithBaseUri$MyHostnameVerifier quarkus.rest-client.connection-ttl=20000 quarkus.rest-client.connection-pool-size=2 quarkus.rest-client.keep-alive-enabled=true diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java index c21f6a668020f..72c9d8e1a1bc7 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java @@ -12,8 +12,6 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSession; import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientResponseContext; import javax.ws.rs.client.ClientResponseFilter; @@ -101,7 +99,6 @@ public void testClientSpecificConfigs() { Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.USER_AGENT, "agent1"); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.STATIC_HEADERS, Collections.singletonMap("header1", "value")); - Mockito.verify(restClientBuilderMock).hostnameVerifier(Mockito.any(MyHostnameVerifier1.class)); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_TTL, 10); // value converted to seconds Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_POOL_SIZE, 103); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.KEEP_ALIVE_ENABLED, false); @@ -144,7 +141,6 @@ public void testGlobalConfigs() { Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.USER_AGENT, "agent2"); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.STATIC_HEADERS, Collections.singletonMap("header2", "value")); - Mockito.verify(restClientBuilderMock).hostnameVerifier(Mockito.any(MyHostnameVerifier2.class)); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_TTL, 20); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.CONNECTION_POOL_SIZE, 203); Mockito.verify(restClientBuilderMock).property(QuarkusRestClientProperties.KEEP_ALIVE_ENABLED, true); @@ -173,8 +169,6 @@ private static RestClientsConfig createSampleConfigRoot() { configRoot.readTimeout = 201L; configRoot.userAgent = Optional.of("agent2"); configRoot.headers = Collections.singletonMap("header2", "value"); - configRoot.hostnameVerifier = Optional - .of("io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyHostnameVerifier2"); configRoot.connectionTTL = Optional.of(20000); // value in ms, will be converted to seconds configRoot.connectionPoolSize = Optional.of(203); configRoot.keepAliveEnabled = Optional.of(true); @@ -212,8 +206,6 @@ private static RestClientConfig createSampleClientConfig() { clientConfig.readTimeout = Optional.of(101L); clientConfig.userAgent = Optional.of("agent1"); clientConfig.headers = Collections.singletonMap("header1", "value"); - clientConfig.hostnameVerifier = Optional - .of("io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyHostnameVerifier1"); clientConfig.connectionTTL = Optional.of(10000); // value in milliseconds, will be converted to seconds clientConfig.connectionPoolSize = Optional.of(103); clientConfig.keepAliveEnabled = Optional.of(false); @@ -251,18 +243,4 @@ public void filter(ClientRequestContext requestContext, ClientResponseContext re } } - public static class MyHostnameVerifier1 implements HostnameVerifier { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - } - - public static class MyHostnameVerifier2 implements HostnameVerifier { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - } - } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java index 062c9ecd26185..c23008fbaa82f 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java @@ -81,7 +81,7 @@ public ClientBuilder withConfig(Configuration config) { @Override public ClientBuilder sslContext(SSLContext sslContext) { // TODO - throw new RuntimeException("Specifying SSLContext is not supported at the moment"); + throw new UnsupportedOperationException("Specifying SSLContext is not supported at the moment"); } @Override @@ -104,8 +104,8 @@ public ClientBuilder trustStore(KeyStore trustStore, char[] password) { @Override public ClientBuilder hostnameVerifier(HostnameVerifier verifier) { - this.hostnameVerifier = verifier; - return this; + // TODO + throw new UnsupportedOperationException("Specifying HostnameVerifier is not supported at the moment"); } @Override From 07731f8556757e2ba7a2c19ce82f78f2a7c8cf57 Mon Sep 17 00:00:00 2001 From: polarctos Date: Thu, 27 Apr 2023 00:23:18 +0200 Subject: [PATCH 03/12] Enabling hostname verification by default Since introduction of the setting 'verifyHost' the hostname verification was disabled by default for the resteasy-reactive-client, as the default value for boolean (primitive) is false. This disabled default makes the reactive client vulnerable to MITM attacks. In the meantime setting the config explicitly is a workaround e.g. with 'quarkus.rest-client.verify-host=true'. This change now adds a proper default both in the configuration and for the field in the reactive client builder implementation. Add test case for enabled host verification default (cherry picked from commit 18f6f4cad6a96d7b10ec63594b167467d48b36d8) --- .../restclient/config/RestClientConfig.java | 3 ++- .../restclient/config/RestClientsConfig.java | 3 ++- .../reactive/client/impl/ClientBuilderImpl.java | 2 +- .../rest/client/main/ClientCallingResource.java | 13 +++++++++++++ .../main/wronghost/WrongHostRejectedClient.java | 16 ++++++++++++++++ .../src/main/resources/application.properties | 5 ++++- .../wronghost/ExternalWrongHostTestCase.java | 10 ++++++++++ 7 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/wronghost/WrongHostRejectedClient.java diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java index 1b290a754a214..06f840242b1da 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java @@ -137,7 +137,8 @@ public class RestClientConfig { public Optional queryParamStyle; /** - * Set whether hostname verification is enabled. + * Set whether hostname verification is enabled. Default is enabled. + * This setting should not be disabled in production as it makes the client vulnerable to MITM attacks. */ @ConfigItem public Optional verifyHost; diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java index 5263804a5c57d..89bfa6686ad3e 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientsConfig.java @@ -226,7 +226,8 @@ public class RestClientsConfig { public Optional queryParamStyle; /** - * Set whether hostname verification is enabled. + * Set whether hostname verification is enabled. Default is enabled. + * This setting should not be disabled in production as it makes the client vulnerable to MITM attacks. * * Can be overwritten by client-specific settings. */ diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java index c23008fbaa82f..63f332a2702b0 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java @@ -59,7 +59,7 @@ public class ClientBuilderImpl extends ClientBuilder { private boolean followRedirects; private boolean trustAll; - private boolean verifyHost; + private boolean verifyHost = true; private LoggingScope loggingScope; private Integer loggingBodySize = 100; diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java index 9e94a122f1f6b..dafed4f2907ed 100644 --- a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java +++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java @@ -23,6 +23,7 @@ import io.quarkus.it.rest.client.main.MyResponseExceptionMapper.MyException; import io.quarkus.it.rest.client.main.selfsigned.ExternalSelfSignedClient; import io.quarkus.it.rest.client.main.wronghost.WrongHostClient; +import io.quarkus.it.rest.client.main.wronghost.WrongHostRejectedClient; import io.smallrye.mutiny.Uni; import io.vertx.core.Future; import io.vertx.core.json.Json; @@ -52,6 +53,9 @@ public class ClientCallingResource { @RestClient WrongHostClient wrongHostClient; + @RestClient + WrongHostRejectedClient wrongHostRejectedClient; + @Inject InMemorySpanExporter inMemorySpanExporter; @@ -190,6 +194,15 @@ void init(@Observes Router router) { router.get("/wrong-host").blockingHandler( rc -> rc.response().setStatusCode(200).end(String.valueOf(wrongHostClient.invoke().getStatus()))); + + router.get("/wrong-host-rejected").blockingHandler(rc -> { + try { + int result = wrongHostRejectedClient.invoke().getStatus(); + rc.response().setStatusCode(200).end(String.valueOf(result)); + } catch (Exception e) { + rc.response().setStatusCode(500).end(e.getCause().getClass().getSimpleName()); + } + }); } private Future success(RoutingContext rc, String body) { diff --git a/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/wronghost/WrongHostRejectedClient.java b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/wronghost/WrongHostRejectedClient.java new file mode 100644 index 0000000000000..94e3bb0e902d2 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/main/java/io/quarkus/it/rest/client/main/wronghost/WrongHostRejectedClient.java @@ -0,0 +1,16 @@ +package io.quarkus.it.rest.client.main.wronghost; + +import javax.ws.rs.GET; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@RegisterRestClient(baseUri = "https://wrong.host.badssl.com/", configKey = "wrong-host-rejected") +public interface WrongHostRejectedClient { + + @GET + @Produces(MediaType.TEXT_PLAIN) + Response invoke(); +} diff --git a/integration-tests/rest-client-reactive/src/main/resources/application.properties b/integration-tests/rest-client-reactive/src/main/resources/application.properties index 4bf8d7c8f6404..7258d0ec3f4f9 100644 --- a/integration-tests/rest-client-reactive/src/main/resources/application.properties +++ b/integration-tests/rest-client-reactive/src/main/resources/application.properties @@ -5,7 +5,10 @@ io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url} # Self-Signed client quarkus.rest-client.self-signed.trust-store=${self-signed.trust-store} quarkus.rest-client.self-signed.trust-store-password=${self-signed.trust-store-password} -# Wrong Host client +# Wrong Host client (connection accepted, as host verification is turned off) quarkus.rest-client.wrong-host.trust-store=${wrong-host.trust-store} quarkus.rest-client.wrong-host.trust-store-password=${wrong-host.trust-store-password} quarkus.rest-client.wrong-host.verify-host=false +# Wrong Host client verified (connection rejected, as host verification is turned on by default) +quarkus.rest-client.wrong-host-rejected.trust-store=${wrong-host.trust-store} +quarkus.rest-client.wrong-host-rejected.trust-store-password=${wrong-host.trust-store-password} diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/wronghost/ExternalWrongHostTestCase.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/wronghost/ExternalWrongHostTestCase.java index d963c850c0dce..838cea76d07c3 100644 --- a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/wronghost/ExternalWrongHostTestCase.java +++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/wronghost/ExternalWrongHostTestCase.java @@ -1,6 +1,7 @@ package io.quarkus.it.rest.client.wronghost; import static io.restassured.RestAssured.when; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import org.junit.jupiter.api.Test; @@ -17,4 +18,13 @@ public void restClient() { .statusCode(200) .body(is("200")); } + + @Test + public void restClientRejected() { + when() + .get("/wrong-host-rejected") + .then() + .statusCode(500) + .body(containsString("SSLHandshakeException")); + } } From ac5710af9e141a6330b2ddd68bb2e874b0368a8e Mon Sep 17 00:00:00 2001 From: Rolfe Dlugy-Hegwer Date: Thu, 4 May 2023 14:26:48 -0400 Subject: [PATCH 04/12] Fix broken link so it points to the config-yaml guide (cherry picked from commit d4dc94018df30d985eaac3fbea501edf990c2ce6) --- .../runtime/src/main/resources/META-INF/quarkus-extension.yaml | 2 +- .../main/resources/codestarts/quarkus/config/yaml/codestart.yml | 2 +- .../tools/devtools-testing/src/main/resources/fake-catalog.json | 2 +- ...m-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json | 2 +- ...bom-quarkus-platform-descriptor-2.0.3.Final-2.0.3.Final.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 4fb7816a51a5d..5c3ed717f678a 100644 --- a/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/config-yaml/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -8,7 +8,7 @@ metadata: categories: - "core" status: "stable" - guide: "https://quarkus.io/guides/config#yaml" + guide: "https://quarkus.io/guides/config-yaml" codestart: name: "config-yaml" languages: diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/config/yaml/codestart.yml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/config/yaml/codestart.yml index cbdd9c9db0eb2..3eb4a9dc82468 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/config/yaml/codestart.yml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/config/yaml/codestart.yml @@ -4,7 +4,7 @@ language: base: shared-data: config: - guide: https://quarkus.io/guides/config#yaml + guide: https://quarkus.io/guides/config-yaml file-name: application.yml dependencies: - io.quarkus:quarkus-config-yaml \ No newline at end of file diff --git a/independent-projects/tools/devtools-testing/src/main/resources/fake-catalog.json b/independent-projects/tools/devtools-testing/src/main/resources/fake-catalog.json index a4ebbf2f6bab3..800b26070f1d1 100644 --- a/independent-projects/tools/devtools-testing/src/main/resources/fake-catalog.json +++ b/independent-projects/tools/devtools-testing/src/main/resources/fake-catalog.json @@ -88,7 +88,7 @@ "core" ], "status": "stable", - "guide": "https://quarkus.io/guides/config#yaml", + "guide": "https://quarkus.io/guides/config-yaml", "codestart": { "name": "config-yaml", "kind": "core", diff --git a/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json b/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json index 2be9b230c26c2..9da28392a450c 100644 --- a/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json +++ b/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json @@ -192,7 +192,7 @@ "keywords" : [ "config", "configuration", "yaml" ], "categories" : [ "core" ], "status" : "stable", - "guide" : "https://quarkus.io/guides/config#yaml", + "guide" : "https://quarkus.io/guides/config-yaml", "codestart" : { "name" : "config-yaml", "languages" : [ "java", "kotlin" ], diff --git a/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-universe-bom-quarkus-platform-descriptor-2.0.3.Final-2.0.3.Final.json b/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-universe-bom-quarkus-platform-descriptor-2.0.3.Final-2.0.3.Final.json index dfce692711852..0b8bb1c8d87b4 100644 --- a/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-universe-bom-quarkus-platform-descriptor-2.0.3.Final-2.0.3.Final.json +++ b/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-universe-bom-quarkus-platform-descriptor-2.0.3.Final-2.0.3.Final.json @@ -504,7 +504,7 @@ "keywords" : [ "config", "configuration", "yaml" ], "categories" : [ "core" ], "status" : "stable", - "guide" : "https://quarkus.io/guides/config#yaml", + "guide" : "https://quarkus.io/guides/config-yaml", "codestart" : { "name" : "config-yaml", "languages" : [ "java", "kotlin" ], From 278449e95bd35bda04e2ba7db2a8c0329981e2db Mon Sep 17 00:00:00 2001 From: Mathias Holzer Date: Mon, 15 May 2023 16:48:44 +0200 Subject: [PATCH 05/12] Fix security-csrf-prevention.adoc Fixed typo in mention of default value for token name; fixed missing parameter type and import in code example (cherry picked from commit 37f7f5b9a2700f18ff29b34ce75fe48f6b74a051) --- docs/src/main/asciidoc/security-csrf-prevention.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/security-csrf-prevention.adoc b/docs/src/main/asciidoc/security-csrf-prevention.adoc index d7a0dcae5af0e..47c639786baef 100644 --- a/docs/src/main/asciidoc/security-csrf-prevention.adoc +++ b/docs/src/main/asciidoc/security-csrf-prevention.adoc @@ -120,7 +120,7 @@ public class UserNameResource { The form POST request will fail with HTTP status `400` if the filter finds the hidden CSRF form field is missing, the CSRF cookie is missing, or if the CSRF form field and CSRF cookie values do not match. -At this stage no additional configuration is needed - by default the CSRF form field and cookie name will be set to `csrf_token`, and the filter will verify the token. But you can change these names if you would like: +At this stage no additional configuration is needed - by default the CSRF form field and cookie name will be set to `csrf-token`, and the filter will verify the token. But you can change these names if you would like: [source,properties] ---- @@ -241,6 +241,7 @@ import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Cookie; import javax.ws.rs.core.MediaType; import io.quarkus.qute.Template; @@ -263,7 +264,7 @@ public class UserNameResource { @Path("/csrfTokenForm") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.TEXT_PLAIN) - public String postCsrfTokenForm(@CookieParam("csrf-token") csrfCookie, @FormParam("csrf-token") String formCsrfToken, @FormParam("name") String userName) { + public String postCsrfTokenForm(@CookieParam("csrf-token") Cookie csrfCookie, @FormParam("csrf-token") String formCsrfToken, @FormParam("name") String userName) { if (!csrfCookie.getValue().equals(formCsrfToken)) { <1> throw new BadRequestException(); } From 8a127911b35eaef4c77c7df8cde697cb992cae5c Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 17 May 2023 15:11:25 +0200 Subject: [PATCH 06/12] Fix location and content location headers in Resteasy Reactive When providing a location, the URI was being decoded, so the value was being altered from what users set. Note that these changes are based on what Resteasy already does: https://github.com/resteasy/resteasy/blob/dadddfb699a875c44ba05c0abe176873acbd9aa2/resteasy-core/src/main/java/org/jboss/resteasy/specimpl/ResponseBuilderImpl.java#L187 Fix https://github.com/quarkusio/quarkus/issues/33419 (cherry picked from commit ced8b0a739651f312744e721ca9f11c49f1834f0) --- .../server/jaxrs/ResponseBuilderImpl.java | 19 ++++++++++++------- .../server/jaxrs/RestResponseBuilderImpl.java | 19 ++++++++++++------- .../vertx/test/response/ResponseTest.java | 17 +++++++++++++++++ .../test/response/RestResponseResource.java | 19 +++++++++++++++++++ .../vertx/test/response/RestResponseTest.java | 8 ++++++++ 5 files changed, 68 insertions(+), 14 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 1c63e1cd14415..c28577bc8559a 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 @@ -40,10 +40,12 @@ public Response.ResponseBuilder location(URI location) { prefix = deployment.getPrefix(); } // Spec says relative to request, but TCK tests relative to Base URI, so we do that - location = new URI(req.getRequestScheme(), null, host, port, - prefix + - (location.getPath().startsWith("/") ? location.getPath() : "/" + location.getPath()), - location.getQuery(), null); + String path = location.toString(); + if (!path.startsWith("/")) { + path = "/" + path; + } + URI baseUri = new URI(req.getRequestScheme(), null, host, port, null, null, null); + location = baseUri.resolve(prefix + path); } catch (URISyntaxException e) { throw new RuntimeException(e); } @@ -72,9 +74,12 @@ public Response.ResponseBuilder contentLocation(URI location) { port = Integer.parseInt(host.substring(index + 1)); host = host.substring(0, index); } - location = new URI(req.getRequestScheme(), null, host, port, - location.getPath().startsWith("/") ? location.getPath() : "/" + location.getPath(), - location.getQuery(), null); + String path = location.toString(); + if (!path.startsWith("/")) { + path = "/" + path; + } + location = new URI(req.getRequestScheme(), null, host, port, null, null, null) + .resolve(path); } catch (URISyntaxException e) { throw new RuntimeException(e); } 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 54aff4e66ecaf..142ac9393de7c 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 @@ -40,10 +40,12 @@ public RestResponse.ResponseBuilder location(URI location) { prefix = deployment.getPrefix(); } // Spec says relative to request, but TCK tests relative to Base URI, so we do that - location = new URI(req.getRequestScheme(), null, host, port, - prefix + - (location.getPath().startsWith("/") ? location.getPath() : "/" + location.getPath()), - location.getQuery(), null); + String path = location.toString(); + if (!path.startsWith("/")) { + path = "/" + path; + } + URI baseUri = new URI(req.getRequestScheme(), null, host, port, null, null, null); + location = baseUri.resolve(prefix + path); } catch (URISyntaxException e) { throw new RuntimeException(e); } @@ -72,9 +74,12 @@ public RestResponse.ResponseBuilder contentLocation(URI location) { port = Integer.parseInt(host.substring(index + 1)); host = host.substring(0, index); } - location = new URI(req.getRequestScheme(), null, host, port, - location.getPath().startsWith("/") ? location.getPath() : "/" + location.getPath(), - location.getQuery(), null); + String path = location.toString(); + if (!path.startsWith("/")) { + path = "/" + path; + } + location = new URI(req.getRequestScheme(), null, host, port, null, null, null) + .resolve(path); } catch (URISyntaxException e) { throw new RuntimeException(e); } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ResponseTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ResponseTest.java index 93a4dcc19168d..fdd63ede1d93b 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ResponseTest.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ResponseTest.java @@ -2,6 +2,7 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -16,4 +17,20 @@ public void testCaseInsensitivity() { Assertions.assertEquals("HEAD", response.getHeaders().getFirst("allow")); Assertions.assertEquals("HEAD", response.getHeaders().getFirst(HttpHeaders.ALLOW)); } + + @Test + public void testLocation() { + final var location = UriBuilder.fromUri("http://localhost:8080").path("{language}") + .build("en/us"); + Response response = Response.ok("Hello").location(location).build(); + Assertions.assertEquals("http://localhost:8080/en%2Fus", response.getLocation().toString()); + } + + @Test + public void testContentLocation() { + final var location = UriBuilder.fromUri("http://localhost:8080").path("{language}") + .build("en/us"); + Response response = Response.ok("Hello").contentLocation(location).build(); + Assertions.assertEquals("http://localhost:8080/en%2Fus", response.getHeaderString("Content-Location")); + } } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseResource.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseResource.java index 7a8c7079fe229..0006d5dd1753e 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseResource.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseResource.java @@ -13,6 +13,7 @@ import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.Variant; import org.jboss.resteasy.reactive.RestResponse; @@ -35,6 +36,24 @@ public RestResponse wildcard() { return RestResponse.ResponseBuilder.ok("Hello").header("content-type", "text/plain").build(); } + @GET + @Path("rest-response-location") + public RestResponse location() { + final var location = UriBuilder.fromResource(RestResponseResource.class).path("{language}") + .queryParam("user", "John") + .build("en/us"); + return RestResponse.ResponseBuilder.ok("Hello").location(location).build(); + } + + @GET + @Path("rest-response-content-location") + public RestResponse contentLocation() { + final var location = UriBuilder.fromResource(RestResponseResource.class).path("{language}") + .queryParam("user", "John") + .build("en/us"); + return RestResponse.ResponseBuilder.ok("Hello").contentLocation(location).build(); + } + @GET @Path("rest-response-full") public RestResponse getResponse() throws URISyntaxException { diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseTest.java index faea3d3351c65..17f12462c76f0 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseTest.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/RestResponseTest.java @@ -1,5 +1,7 @@ package org.jboss.resteasy.reactive.server.vertx.test.response; +import static org.hamcrest.CoreMatchers.endsWith; + import java.util.function.Supplier; import org.hamcrest.Matchers; @@ -99,5 +101,11 @@ public void test() { .then().statusCode(200) .and().body(Matchers.equalTo("Uni request filter")) .and().contentType("text/plain"); + RestAssured.get("/rest-response-location") + .then().statusCode(200) + .header("Location", endsWith("/en%2Fus?user=John")); + RestAssured.get("/rest-response-content-location") + .then().statusCode(200) + .header("Content-Location", endsWith("/en%2Fus?user=John")); } } From 4406cde829df67c4c4cd23f1d6ee4d86fda3749d Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 19 May 2023 14:17:48 +0300 Subject: [PATCH 07/12] Ensure that cookies added in filters are visible to resource methods Fixes: #33490 (cherry picked from commit 196b7425ee37d0b74fac3d8209c59028c120c5da) --- .../server/jaxrs/HttpHeadersImpl.java | 6 +- .../vertx/test/CookiesSetInFilterTest.java | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/CookiesSetInFilterTest.java diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/HttpHeadersImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/HttpHeadersImpl.java index 8177b3e08c51c..427c597f36a45 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/HttpHeadersImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/HttpHeadersImpl.java @@ -23,7 +23,6 @@ public class HttpHeadersImpl implements HttpHeaders { private final MultivaluedMap requestHeaders; private final MultivaluedMap unmodifiableRequestHeaders; - private Map cookies; public HttpHeadersImpl(Iterable> vertxHeaders) { requestHeaders = new CaseInsensitiveMap<>(); @@ -50,10 +49,7 @@ public List getRequestHeader(String name) { @Override public Map getCookies() { - if (cookies == null) { - cookies = Collections.unmodifiableMap(HeaderUtil.getCookies(requestHeaders)); - } - return cookies; + return Collections.unmodifiableMap(HeaderUtil.getCookies(requestHeaders)); } @Override diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/CookiesSetInFilterTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/CookiesSetInFilterTest.java new file mode 100644 index 0000000000000..986833c2e4d56 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/CookiesSetInFilterTest.java @@ -0,0 +1,66 @@ +package org.jboss.resteasy.reactive.server.vertx.test; + +import static io.restassured.RestAssured.given; +import static io.restassured.RestAssured.when; +import static org.hamcrest.Matchers.is; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.NewCookie; + +import org.jboss.resteasy.reactive.RestCookie; +import org.jboss.resteasy.reactive.common.headers.NewCookieHeaderDelegate; +import org.jboss.resteasy.reactive.server.ServerRequestFilter; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class CookiesSetInFilterTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(TestResource.class, Filters.class)); + + @Test + void requestDoesNotContainCookie() { + when().get("/test") + .then() + .statusCode(200) + .body(is("foo")); + } + + @Test + void requestContainsCookie() { + given() + .cookie("dummy", "bar") + .when().get("/test") + .then() + .statusCode(200) + .body(is("bar")); + } + + @Path("test") + public static class TestResource { + + @GET + public String get(@RestCookie String dummy) { + return dummy; + } + } + + public static class Filters { + + @ServerRequestFilter + public void setCookieIfMissing(ContainerRequestContext context) { + if (!context.getCookies().containsKey("dummy")) { + context.getHeaders().add(HttpHeaders.COOKIE, + NewCookieHeaderDelegate.INSTANCE.toString(new NewCookie("dummy", "foo"))); + } + } + } +} From 75b7cf379435f87b338c2c11df4242ea62d6d902 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Thu, 25 May 2023 10:11:31 +0200 Subject: [PATCH 08/12] Skip manifest entry properties that are missing values (cherry picked from commit 0e9a248b9882980b92e0161dda9dec16d7f7d726) --- .../java/io/quarkus/maven/QuarkusBootstrapProvider.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java index f645cbc37f0aa..66014e55fd951 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java @@ -190,8 +190,11 @@ private CuratedApplication doBootstrap(QuarkusBootstrapMojo mojo, LaunchMode mod effectiveProperties.putIfAbsent("quarkus.application.version", mojo.mavenProject().getVersion()); for (Map.Entry attribute : mojo.manifestEntries().entrySet()) { - effectiveProperties.put(toManifestAttributeKey(attribute.getKey()), - attribute.getValue()); + if (attribute.getValue() == null) { + mojo.getLog().warn("Skipping manifest entry property " + attribute.getKey() + " with a missing value"); + } else { + effectiveProperties.put(toManifestAttributeKey(attribute.getKey()), attribute.getValue()); + } } for (ManifestSection section : mojo.manifestSections()) { for (Map.Entry attribute : section.getManifestEntries().entrySet()) { From 2840a9e37a85e5852a9586604f763f8c6981fd0f Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 19 Jun 2023 18:07:33 +0100 Subject: [PATCH 09/12] Fix NPE in RunningDevService which represens a shared service (cherry picked from commit 28034bbf81db8f5f7199dc98b133a662dcab7b51) --- .../deployment/builditem/DevServicesResultBuildItem.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java index 5ed1385142e11..560a9d1f49e38 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/DevServicesResultBuildItem.java @@ -87,7 +87,9 @@ public boolean isOwner() { @Override public void close() throws IOException { - this.closeable.close(); + if (this.closeable != null) { + this.closeable.close(); + } } public DevServicesResultBuildItem toBuildItem() { From 977a0f0c3668b0f66135a8dae1f85f7d16a47eb8 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 16 Jun 2023 18:03:21 +0100 Subject: [PATCH 10/12] Avoid calling OIDC UserInfo endpoint if UserInfo is cached (cherry picked from commit e2cb17cf9ca1d5c05ec2786f41b796e18e823544) --- .../oidc/runtime/OidcIdentityProvider.java | 22 +++++++++++++------ .../it/keycloak/CodeFlowUserInfoResource.java | 4 +--- .../keycloak/CodeFlowAuthorizationTest.java | 13 ++++++++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java index b6afb3fee8dc1..787fb88f8722b 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java @@ -435,7 +435,7 @@ private Uni refreshJwksAndVerifyTokenUni(TenantConfigCo .recoverWithUni(f -> introspectTokenUni(resolvedContext, token)); } - private Uni introspectTokenUni(TenantConfigContext resolvedContext, String token) { + private Uni introspectTokenUni(TenantConfigContext resolvedContext, final String token) { TokenIntrospectionCache tokenIntrospectionCache = tenantResolver.getTokenIntrospectionCache(); Uni tokenIntrospectionUni = tokenIntrospectionCache == null ? null : tokenIntrospectionCache @@ -444,7 +444,12 @@ private Uni introspectTokenUni(TenantConfigContext reso tokenIntrospectionUni = newTokenIntrospectionUni(resolvedContext, token); } else { tokenIntrospectionUni = tokenIntrospectionUni.onItem().ifNull() - .switchTo(newTokenIntrospectionUni(resolvedContext, token)); + .switchTo(new Supplier>() { + @Override + public Uni get() { + return newTokenIntrospectionUni(resolvedContext, token); + } + }); } return tokenIntrospectionUni.onItem().transform(t -> new TokenVerificationResult(null, t)); } @@ -489,10 +494,8 @@ private Uni getUserInfoUni(RoutingContext vertxContext, TokenAuthentic } LOG.debug("Requesting UserInfo"); - String accessToken = vertxContext.get(OidcConstants.ACCESS_TOKEN_VALUE); - if (accessToken == null) { - accessToken = request.getToken().getToken(); - } + String contextAccessToken = vertxContext.get(OidcConstants.ACCESS_TOKEN_VALUE); + final String accessToken = contextAccessToken != null ? contextAccessToken : request.getToken().getToken(); UserInfoCache userInfoCache = tenantResolver.getUserInfoCache(); Uni userInfoUni = userInfoCache == null ? null @@ -501,7 +504,12 @@ private Uni getUserInfoUni(RoutingContext vertxContext, TokenAuthentic userInfoUni = newUserInfoUni(resolvedContext, accessToken); } else { userInfoUni = userInfoUni.onItem().ifNull() - .switchTo(newUserInfoUni(resolvedContext, accessToken)); + .switchTo(new Supplier>() { + @Override + public Uni get() { + return newUserInfoUni(resolvedContext, accessToken); + } + }); } return userInfoUni; } diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CodeFlowUserInfoResource.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CodeFlowUserInfoResource.java index 11dfe8cec7dd6..6ccfa133078fc 100644 --- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CodeFlowUserInfoResource.java +++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/CodeFlowUserInfoResource.java @@ -31,11 +31,9 @@ public class CodeFlowUserInfoResource { @GET @Path("/code-flow-user-info-only") public String access() { - int cacheSize = tokenCache.getCacheSize(); - tokenCache.clearCache(); return identity.getPrincipal().getName() + ":" + userInfo.getString("preferred_username") + ":" + accessToken.getName() + ", cache size: " - + cacheSize; + + tokenCache.getCacheSize(); } @GET diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java index d3273facb0eb8..316d91885b88f 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java @@ -3,8 +3,10 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.containing; import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -208,9 +210,11 @@ public void testCodeFlowUserInfo() throws Exception { defineCodeFlowAuthorizationOauth2TokenStub(); doTestCodeFlowUserInfo("code-flow-user-info-only", 300); + clearCache(); doTestCodeFlowUserInfo("code-flow-user-info-github", 360); + clearCache(); doTestCodeFlowUserInfo("code-flow-user-info-dynamic-github", 301); - + clearCache(); doTestCodeFlowUserInfoCashedInIdToken(); } @@ -250,6 +254,13 @@ private void doTestCodeFlowUserInfo(String tenantId, long internalIdTokenLifetim page = form.getInputByValue("login").click(); assertEquals("alice:alice:alice, cache size: 1", page.getBody().asNormalizedText()); + page = webClient.getPage("http://localhost:8081/" + tenantId); + assertEquals("alice:alice:alice, cache size: 1", page.getBody().asNormalizedText()); + page = webClient.getPage("http://localhost:8081/" + tenantId); + assertEquals("alice:alice:alice, cache size: 1", page.getBody().asNormalizedText()); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching("/auth/realms/quarkus/protocol/openid-connect/userinfo"))); + wireMockServer.resetRequests(); Cookie sessionCookie = getSessionCookie(webClient, tenantId); assertNotNull(sessionCookie); From 5595955a70e84a433829f4dacde61e7610ecb0bc Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 14 Jun 2023 11:59:27 +0200 Subject: [PATCH 11/12] Also include stream information in CLI project creation This has been done a long while ago for standard apps but I missed to update the CLI somehow. (cherry picked from commit f2468393ba5fe9e6f9f37db3da11c18029dfb8f3) --- docs/src/main/asciidoc/_attributes.adoc | 3 ++- .../_includes/devtools/create-cli.adoc | 19 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/src/main/asciidoc/_attributes.adoc b/docs/src/main/asciidoc/_attributes.adoc index af5961b6be373..14d43005533e5 100644 --- a/docs/src/main/asciidoc/_attributes.adoc +++ b/docs/src/main/asciidoc/_attributes.adoc @@ -1,4 +1,4 @@ -// Common attributes. +// Common attributes. // --> No blank lines (it ends the document header) :project-name: Quarkus :quarkus-version: ${project.version} @@ -52,5 +52,6 @@ :micrometer-registry-guide: https://quarkiverse.github.io/quarkiverse-docs/quarkus-micrometer-registry/dev/index.html // . :create-app-group-id: org.acme +:create-cli-group-id: {create-app-group-id} // . include::_attributes-local.adoc[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/_includes/devtools/create-cli.adoc b/docs/src/main/asciidoc/_includes/devtools/create-cli.adoc index 932184d383594..2ea380f6d388a 100644 --- a/docs/src/main/asciidoc/_includes/devtools/create-cli.adoc +++ b/docs/src/main/asciidoc/_includes/devtools/create-cli.adoc @@ -3,11 +3,10 @@ **** [source,bash,subs=attributes+] ---- -ifdef::create-cli-group-id[] -ifdef::create-cli-extensions[] +ifdef::create-cli-extensions,create-cli-stream[] quarkus create cli {create-cli-group-id}:{create-cli-artifact-id} \ endif::[] -ifndef::create-cli-extensions[] +ifndef::create-cli-extensions,create-cli-stream[] ifndef::create-cli-code[] quarkus create cli {create-cli-group-id}:{create-cli-artifact-id} \ endif::[] @@ -15,17 +14,16 @@ ifdef::create-cli-code[] quarkus create cli {create-cli-group-id}:{create-cli-artifact-id} endif::[] endif::[] -endif::[] -ifndef::create-cli-group-id[] +ifdef::create-cli-stream[] ifdef::create-cli-extensions[] -quarkus create cli org.acme:{create-cli-artifact-id} \ + --stream={create-cli-stream} \ endif::[] ifndef::create-cli-extensions[] ifndef::create-cli-code[] -quarkus create cli org.acme:{create-cli-artifact-id} \ + --stream={create-cli-stream} \ endif::[] ifdef::create-cli-code[] -quarkus create cli org.acme:{create-cli-artifact-id} + --stream={create-cli-stream} endif::[] endif::[] endif::[] @@ -61,9 +59,10 @@ _For more information about how to install the Quarkus CLI and use it, please re [source,bash,subs=attributes+] ---- mvn io.quarkus.platform:quarkus-maven-plugin:{quarkus-version}:create \ -ifdef::create-cli-group-id[] - -DprojectGroupId={create-cli-group-id} \ +ifdef::create-cli-stream[] + -DplatformVersion={quarkus-version} \ endif::[] + -DprojectGroupId={create-cli-group-id} \ ifndef::create-cli-group-id[] -DprojectGroupId=org.acme \ endif::[] From a4aabec63f66d9591729f51014ff0bcde6ac3aef Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Tue, 27 Jun 2023 10:20:21 +0200 Subject: [PATCH 12/12] Update Netty to 4.1.94 and Brotli to 1.12.0 (cherry picked from commit 15b2ece5d441f142208a4399bc9acb3bc91e10f7) --- 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 ef8978c1218e6..a83e89c150cc1 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -138,8 +138,8 @@ 14.0.6.Final 4.5.1.Final 3.1.1 - 4.1.86.Final - 1.8.0 + 4.1.94.Final + 1.12.0 1.0.3 3.5.0.Final 1.9.0