From 1ffff3ffb73a4b79e5db532479f13c865e1b8523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Mon, 13 Feb 2023 21:51:06 +0100 Subject: [PATCH] Fix stuck HTTP2 request when sent challenge has resumed request fixes: #23360 (cherry picked from commit b696f3199a6e5e26ee589c32457e4a2b34a22d7d) --- .../Http2FormAuthRedirectTestCase.java | 86 ++++++++++++++++++ .../src/test/resources/server-keystore.jks | Bin 0 -> 2423 bytes .../runtime/security/HttpAuthenticator.java | 8 +- 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/Http2FormAuthRedirectTestCase.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/resources/server-keystore.jks diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/Http2FormAuthRedirectTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/Http2FormAuthRedirectTestCase.java new file mode 100644 index 0000000000000..a3d9df4cda142 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/Http2FormAuthRedirectTestCase.java @@ -0,0 +1,86 @@ +package io.quarkus.resteasy.reactive.server.test.security; + +import java.net.URL; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.security.test.utils.TestIdentityController; +import io.quarkus.security.test.utils.TestIdentityProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.vertx.http.runtime.QuarkusHttpHeaders; +import io.vertx.core.MultiMap; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpVersion; +import io.vertx.ext.web.client.HttpResponse; +import io.vertx.ext.web.client.WebClient; +import io.vertx.ext.web.client.WebClientOptions; + +public class Http2FormAuthRedirectTestCase { + + @TestHTTPResource(value = "/j_security_check", ssl = true) + URL sslUrl; + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(TestIdentityProvider.class, TestIdentityController.class) + .addAsResource(new StringAsset("quarkus.http.auth.form.enabled=true\n" + + "quarkus.http.insecure-requests=disabled\n" + + "quarkus.http.ssl.certificate.key-store-file=server-keystore.jks\n" + + "quarkus.http.ssl.certificate.key-store-password=secret"), "application.properties") + .addAsResource("server-keystore.jks"); + } + }); + + @BeforeAll + public static void setup() { + TestIdentityController.resetRoles().add("a d m i n", "a d m i n", "a d m i n"); + } + + @Test + public void testFormAuthFailure() { + Vertx vertx = Vertx.vertx(); + try { + WebClientOptions options = new WebClientOptions() + .setSsl(true) + .setVerifyHost(false) + .setTrustAll(true) + .setProtocolVersion(HttpVersion.HTTP_2) + .setFollowRedirects(false) + .setUseAlpn(true); + CompletableFuture result = new CompletableFuture<>(); + MultiMap formParams = new QuarkusHttpHeaders() + .add("j_username", "a d m i n") + .add("j_password", "wrongpassword"); + WebClient.create(vertx, options) + .post(sslUrl.getPort(), sslUrl.getHost(), sslUrl.getPath()) + .sendForm(formParams, ar -> { + if (ar.succeeded()) { + HttpResponse response = ar.result(); + result.complete(response.statusCode()); + } else { + result.completeExceptionally(ar.cause()); + } + }); + Assertions.assertEquals(302, result.get()); + } catch (ExecutionException | InterruptedException e) { + Assertions.fail(e.getMessage()); + } finally { + vertx.close(); + } + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/resources/server-keystore.jks b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/resources/server-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..da33e8e7a16683d421c7a541bf0013521efb605e GIT binary patch literal 2423 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 aH~3Ca%|*I@e88To>+D>~J`lt~rv3-)J!2gJ literal 0 HcmV?d00001 diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java index f8738d9c63902..3341ac30c12b6 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java @@ -136,9 +136,11 @@ public Uni apply(SecurityIdentity data) { */ public Uni sendChallenge(RoutingContext routingContext) { //we want to consume any body content if present - //challenges won't read the body, and if we don't consume - //things can get stuck - routingContext.request().resume(); + //challenges won't read the body and didn't resume context themselves + //as if we don't consume things can get stuck + if (!routingContext.request().isEnded()) { + routingContext.request().resume(); + } Uni result = null; // we only require auth mechanism to put itself into routing context when there is more than one mechanism registered