diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java index fbcd7f668990ac..dff63f69f971b3 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java @@ -11,7 +11,11 @@ import org.jboss.logging.Logger; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpResponseStatus; import io.quarkus.runtime.TemplateHtmlBuilder; +import io.quarkus.security.ForbiddenException; +import io.quarkus.security.UnauthorizedException; +import io.quarkus.vertx.http.runtime.security.HttpAuthenticator; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; @@ -40,6 +44,25 @@ public void handle(RoutingContext event) { event.response().end(); return; } + //this can happen if there is no auth mechanisms + if (event.failure() instanceof UnauthorizedException) { + HttpAuthenticator authenticator = event.get(HttpAuthenticator.class.getName()); + if (authenticator != null) { + authenticator.sendChallenge(event, new Runnable() { + @Override + public void run() { + event.response().end(); + } + }); + } else { + event.response().setStatusCode(HttpResponseStatus.UNAUTHORIZED.code()).end(); + } + return; + } + if (event.failure() instanceof ForbiddenException) { + event.response().setStatusCode(HttpResponseStatus.FORBIDDEN.code()).end(); + return; + } event.response().setStatusCode(500); String uuid = BASE_ID + ERROR_COUNT.incrementAndGet(); String details = ""; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index cf2e41b37fd33e..89eab0692c37c6 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -47,6 +47,7 @@ import io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter; import io.vertx.core.AbstractVerticle; import io.vertx.core.AsyncResult; +import io.vertx.core.Context; import io.vertx.core.DeploymentOptions; import io.vertx.core.Future; import io.vertx.core.Handler; @@ -66,6 +67,7 @@ import io.vertx.core.net.PemKeyCertOptions; import io.vertx.core.net.PfxOptions; import io.vertx.core.net.SocketAddress; +import io.vertx.core.net.impl.ConnectionBase; import io.vertx.core.net.impl.VertxHandler; import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; @@ -874,8 +876,27 @@ public Handler createBodyHandler(HttpConfiguration httpConfigura return new Handler() { @Override public void handle(RoutingContext event) { - event.request().resume(); - bodyHandler.handle(event); + if (!Context.isOnEventLoopThread()) { + ((ConnectionBase) event.request().connection()).channel().eventLoop().execute(new Runnable() { + @Override + public void run() { + try { + //this can happen if blocking authentication is involved for get requests + if (!event.request().isEnded()) { + event.request().resume(); + bodyHandler.handle(event); + } else { + event.next(); + } + } catch (Throwable t) { + event.fail(t); + } + } + }); + } else { + event.request().resume(); + bodyHandler.handle(event); + } } }; } diff --git a/extensions/vertx-web/deployment/pom.xml b/extensions/vertx-web/deployment/pom.xml index 09dbd03bdcabef..af68e87fef09f5 100644 --- a/extensions/vertx-web/deployment/pom.xml +++ b/extensions/vertx-web/deployment/pom.xml @@ -27,6 +27,16 @@ quarkus-vertx-web + + io.quarkus + quarkus-security + test + + + io.quarkus + quarkus-elytron-security-properties-file-deployment + test + io.quarkus quarkus-junit5-internal diff --git a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java index d36a2233de49a3..c2d0ef263f2910 100644 --- a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java +++ b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import javax.annotation.security.RolesAllowed; import javax.enterprise.context.RequestScoped; import javax.enterprise.event.Observes; import javax.inject.Inject; @@ -30,8 +31,12 @@ public class SimpleRouteTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(SimpleBean.class, - SimpleEventBusBean.class, SimpleRoutesBean.class, Transformer.class)); + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("application.properties") + .addAsResource("test-users.properties") + .addAsResource("test-roles.properties") + .addClasses(SimpleBean.class, + SimpleEventBusBean.class, SimpleRoutesBean.class, Transformer.class)); @Test public void testSimpleRoute() { @@ -51,6 +56,13 @@ public void testSimpleRoute() { when().get("/request").then().statusCode(200).body(is("HellO!")); } + @Test + public void testSecuredRoute() { + when().get("/secured").then().statusCode(401); + given().auth().basic("bob", "bob").get("/secured").then().statusCode(403); + given().auth().basic("alice", "alice").get("/secured").then().statusCode(200); + } + static class SimpleBean { @Inject @@ -64,6 +76,12 @@ void hello(RoutingContext context) { context.response().setStatusCode(200).end("Hello " + (name != null ? name : "world") + "!"); } + @Route(path = "/secured") + @RolesAllowed("admin") //we are just testing that this is actually denied + void secure(RoutingContext context) { + context.response().setStatusCode(200).end(); + } + @Route(path = "/rx-hello") void rxHello(io.vertx.reactivex.ext.web.RoutingContext context) { String name = context.request().getParam("name"); diff --git a/extensions/vertx-web/deployment/src/test/resources/application.properties b/extensions/vertx-web/deployment/src/test/resources/application.properties new file mode 100644 index 00000000000000..3f44daa1d2e17d --- /dev/null +++ b/extensions/vertx-web/deployment/src/test/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.security.users.file.enabled=true +quarkus.security.users.file.users=test-users.properties +quarkus.security.users.file.roles=test-roles.properties +quarkus.security.users.file.plain-text=true diff --git a/extensions/vertx-web/deployment/src/test/resources/test-roles.properties b/extensions/vertx-web/deployment/src/test/resources/test-roles.properties new file mode 100644 index 00000000000000..452e1223fb380f --- /dev/null +++ b/extensions/vertx-web/deployment/src/test/resources/test-roles.properties @@ -0,0 +1,2 @@ +bob=user +alice=admin \ No newline at end of file diff --git a/extensions/vertx-web/deployment/src/test/resources/test-users.properties b/extensions/vertx-web/deployment/src/test/resources/test-users.properties new file mode 100644 index 00000000000000..fefc2ae47afe01 --- /dev/null +++ b/extensions/vertx-web/deployment/src/test/resources/test-users.properties @@ -0,0 +1,2 @@ +bob=bob +alice=alice \ No newline at end of file diff --git a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/RouteHandler.java b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/RouteHandler.java index f87177660062b3..f51f2651f221e9 100644 --- a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/RouteHandler.java +++ b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/RouteHandler.java @@ -1,6 +1,9 @@ package io.quarkus.vertx.web.runtime; +import io.quarkus.arc.Arc; +import io.quarkus.arc.ManagedContext; import io.quarkus.arc.runtime.BeanInvoker; +import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.quarkus.vertx.web.Route; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; @@ -14,7 +17,24 @@ public interface RouteHandler extends Handler, BeanInvoker