diff --git a/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java b/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java index 27498c6d5fc3b..6fd9f8519fc71 100644 --- a/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java +++ b/extensions/reactive-routes/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java @@ -146,12 +146,7 @@ public void handle(RoutingContext event) { @Override protected void proceed(Throwable throwable) { - if (event.failed()) { - //auth failure handler should never get called from route failure handlers - //but if we get to this point bad things have happened, - //so it is better to send a response than to hang - event.end(); - } else { + if (!event.failed()) { event.fail(throwable); } } diff --git a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java index 291774d72a214..7a8a444f88f68 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java @@ -4,7 +4,6 @@ import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import java.util.Optional; -import java.util.function.Consumer; import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.deployment.Capabilities; @@ -30,7 +29,6 @@ import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.VertxHttpRecorder; import io.vertx.core.Handler; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; public class ResteasyStandaloneBuildStep { @@ -75,6 +73,7 @@ public void boot(ShutdownContextBuildItem shutdown, BuildProducer feature, BuildProducer defaultRoutes, BuildProducer routes, + BuildProducer filterBuildItemBuildProducer, CoreVertxBuildItem vertx, ResteasyStandaloneBuildItem standalone, Optional requireVirtual, @@ -92,15 +91,16 @@ public void boot(ShutdownContextBuildItem shutdown, executorBuildItem.getExecutorProxy(), resteasyVertxConfig); // failure handler for auth failures that occurred before the handler defined right above started processing the request - final Consumer addFailureHandler = recorder.addVertxFailureHandler(vertx.getVertx(), + final Handler failureHandler = recorder.vertxFailureHandler(vertx.getVertx(), executorBuildItem.getExecutorProxy(), resteasyVertxConfig); + filterBuildItemBuildProducer.produce(new FilterBuildItem(failureHandler, + VertxHttpRecorder.AFTER_DEFAULT_ROUTE_ORDER_MARK + REST_ROUTE_ORDER_OFFSET, true)); // Exact match for resources matched to the root path routes.produce( RouteBuildItem.builder() .orderedRoute(standalone.deploymentRootPath, - VertxHttpRecorder.AFTER_DEFAULT_ROUTE_ORDER_MARK + REST_ROUTE_ORDER_OFFSET, - addFailureHandler) + VertxHttpRecorder.AFTER_DEFAULT_ROUTE_ORDER_MARK + REST_ROUTE_ORDER_OFFSET) .handler(handler).build()); String matchPath = standalone.deploymentRootPath; if (matchPath.endsWith("/")) { diff --git a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java index 3c94837db867f..20c347b210357 100644 --- a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java +++ b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/ResteasyStandaloneRecorder.java @@ -1,8 +1,9 @@ package io.quarkus.resteasy.runtime.standalone; +import static io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder.DefaultAuthFailureHandler.extractRootCause; + import java.util.concurrent.Executor; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.function.Supplier; import org.jboss.resteasy.specimpl.ResteasyUriInfo; @@ -23,7 +24,6 @@ import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.vertx.core.Handler; import io.vertx.core.Vertx; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; /** @@ -77,35 +77,30 @@ public Handler vertxRequestHandler(Supplier vertx, Execut return null; } - public Consumer addVertxFailureHandler(Supplier vertx, Executor executor, ResteasyVertxConfig config) { + public Handler vertxFailureHandler(Supplier vertx, Executor executor, ResteasyVertxConfig config) { if (deployment == null) { return null; } else { - return new Consumer() { - @Override - public void accept(Route route) { - // allow customization of auth failures with exception mappers; this failure handler is only - // used when auth failed before RESTEasy Classic began processing the request - route.failureHandler(new VertxRequestHandler(vertx.get(), deployment, contextPath, - new ResteasyVertxAllocator(config.responseBufferSize), executor, - readTimeout.getValue().readTimeout.toMillis()) { + // allow customization of auth failures with exception mappers; this failure handler is only + // used when auth failed before RESTEasy Classic began processing the request + return new VertxRequestHandler(vertx.get(), deployment, contextPath, + new ResteasyVertxAllocator(config.responseBufferSize), executor, + readTimeout.getValue().readTimeout.toMillis()) { - @Override - public void handle(RoutingContext request) { - if (request.failure() instanceof AuthenticationFailedException - || request.failure() instanceof AuthenticationCompletionException - || request.failure() instanceof AuthenticationRedirectException) { - super.handle(request); - } else { - request.next(); - } - } + @Override + public void handle(RoutingContext request) { + if (request.failure() instanceof AuthenticationFailedException + || request.failure() instanceof AuthenticationCompletionException + || request.failure() instanceof AuthenticationRedirectException) { + super.handle(request); + } else { + request.next(); + } + } - @Override - protected void setCurrentIdentityAssociation(RoutingContext routingContext) { - // security identity is not available as authentication failed - } - }); + @Override + protected void setCurrentIdentityAssociation(RoutingContext routingContext) { + // security identity is not available as authentication failed } }; } @@ -115,7 +110,8 @@ public Handler defaultAuthFailureHandler() { return new Handler() { @Override public void handle(RoutingContext event) { - if (event.get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof DefaultAuthFailureHandler) { + if (deployment != null + && event.get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof DefaultAuthFailureHandler) { // only replace default auth failure handler if we can extract URI info // as org.jboss.resteasy.plugins.server.BaseHttpRequest requires it; @@ -135,13 +131,8 @@ public void handle(RoutingContext event) { @Override public void accept(RoutingContext event, Throwable throwable) { - if (event.failed()) { - //auth failure handler should never get called from route failure handlers - //but if we get to this point bad things have happened, - //so it is better to send a response than to hang - event.end(); - } else { - event.fail(throwable); + if (!event.failed()) { + event.fail(extractRootCause(throwable)); } } }); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 26f6c576fe68c..023c829bf01e1 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -194,7 +194,6 @@ import io.vertx.core.Handler; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; public class ResteasyReactiveProcessor { @@ -1055,6 +1054,7 @@ public void setupDeployment(BeanContainerBuildItem beanContainerBuildItem, BuildProducer quarkusRestDeploymentBuildItemBuildProducer, BuildProducer reflectiveClass, BuildProducer routes, + BuildProducer filterBuildItemBuildProducer, ApplicationResultBuildItem applicationResultBuildItem, ResourceInterceptorsBuildItem resourceInterceptorsBuildItem, ExceptionMappersBuildItem exceptionMappersBuildItem, @@ -1187,11 +1187,12 @@ public void setupDeployment(BeanContainerBuildItem beanContainerBuildItem, if (!requestContextFactoryBuildItem.isPresent()) { RuntimeValue restInitialHandler = recorder.restInitialHandler(deployment); Handler handler = recorder.handler(restInitialHandler); - Consumer addFailureHandler = recorder.addFailureHandler(restInitialHandler); + Handler failureHandler = recorder.failureHandler(restInitialHandler); + filterBuildItemBuildProducer.produce(new FilterBuildItem(failureHandler, order, true)); // Exact match for resources matched to the root path routes.produce(RouteBuildItem.builder() - .orderedRoute(deploymentPath, order, addFailureHandler).handler(handler).build()); + .orderedRoute(deploymentPath, order).handler(handler).build()); String matchPath = deploymentPath; if (matchPath.endsWith("/")) { matchPath += "*"; @@ -1200,7 +1201,7 @@ public void setupDeployment(BeanContainerBuildItem beanContainerBuildItem, } // Match paths that begin with the deployment path routes.produce( - RouteBuildItem.builder().orderedRoute(matchPath, order, addFailureHandler) + RouteBuildItem.builder().orderedRoute(matchPath, order) .handler(handler).build()); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/AuthenticationRedirectExceptionMapperTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/AuthenticationRedirectExceptionMapperTest.java new file mode 100644 index 0000000000000..e5a6d2905b6d8 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/AuthenticationRedirectExceptionMapperTest.java @@ -0,0 +1,97 @@ +package io.quarkus.resteasy.reactive.server.test.security; + +import static org.jboss.resteasy.reactive.RestResponse.StatusCode.FOUND; + +import java.util.Set; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.reactive.server.ServerExceptionMapper; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.security.AuthenticationRedirectException; +import io.quarkus.security.identity.AuthenticationRequestContext; +import io.quarkus.security.identity.IdentityProvider; +import io.quarkus.security.identity.IdentityProviderManager; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.identity.request.AuthenticationRequest; +import io.quarkus.security.identity.request.BaseAuthenticationRequest; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.vertx.http.runtime.security.ChallengeData; +import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; +import io.restassured.RestAssured; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; + +public class AuthenticationRedirectExceptionMapperTest { + + private static final int EXPECTED_STATUS = 409; + private static final String APP_PROPS = "" + + "quarkus.http.auth.proactive=false\n" + + "quarkus.http.auth.permission.default.paths=/*\n" + + "quarkus.http.auth.permission.default.policy=authenticated"; + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource(new StringAsset(APP_PROPS), "application.properties")); + + @Test + public void testAuthenticationRedirectExceptionMapper() { + RestAssured + .given() + .redirects() + .follow(false) + .when() + .get("/secured-route") + .then() + .statusCode(EXPECTED_STATUS); + } + + public static final class AuthenticationRedirectExceptionMapper { + + @ServerExceptionMapper(AuthenticationRedirectException.class) + public Response authenticationRedirectException() { + return Response.status(EXPECTED_STATUS).build(); + } + } + + @ApplicationScoped + public static class RedirectingAuthenticator implements HttpAuthenticationMechanism { + + @Override + public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) { + throw new AuthenticationRedirectException(FOUND, "https://quarkus.io/"); + } + + @Override + public Set> getCredentialTypes() { + return Set.of(BaseAuthenticationRequest.class); + } + + @Override + public Uni getChallenge(RoutingContext context) { + return Uni.createFrom().item(new ChallengeData(FOUND, "header-name", "header-value")); + } + + } + + @ApplicationScoped + public static class BasicIdentityProvider implements IdentityProvider { + + @Override + public Class getRequestType() { + return BaseAuthenticationRequest.class; + } + + @Override + public Uni authenticate( + BaseAuthenticationRequest simpleAuthenticationRequest, + AuthenticationRequestContext authenticationRequestContext) { + return Uni.createFrom().nothing(); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java index 5158df4b37136..8cc7fde9154a7 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java @@ -1,6 +1,7 @@ package io.quarkus.resteasy.reactive.server.runtime; import static io.quarkus.resteasy.reactive.server.runtime.NotFoundExceptionMapper.classMappers; +import static io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder.DefaultAuthFailureHandler.extractRootCause; import java.io.Closeable; import java.lang.reflect.Constructor; @@ -59,7 +60,6 @@ import io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder.DefaultAuthFailureHandler; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.vertx.core.Handler; -import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; @Recorder @@ -211,32 +211,27 @@ public void accept(RoutingContext routingContext) { return new ResteasyReactiveVertxHandler(eventCustomizer, initialHandler); } - public Consumer addFailureHandler(RuntimeValue restInitialHandlerRuntimeValue) { + public Handler failureHandler(RuntimeValue restInitialHandlerRuntimeValue) { final RestInitialHandler restInitialHandler = restInitialHandlerRuntimeValue.getValue(); - return new Consumer() { + // process auth failures with abort handlers + return new Handler() { @Override - public void accept(Route route) { - // process auth failures with abort handlers - route.failureHandler(new Handler() { - @Override - public void handle(RoutingContext event) { - - // this condition prevent exception mappers from handling auth failure exceptions when proactive - // security is enabled as for now, community decided that's expected behavior and only way for - // users to handle the exceptions is to define their own failure handler as in Reactive Routes - // more info here: https://github.com/quarkusio/quarkus/pull/28648#issuecomment-1287203946 - final boolean eventFailedByRESTEasyReactive = event - .get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof FailingDefaultAuthFailureHandler; - - if (eventFailedByRESTEasyReactive && (event.failure() instanceof AuthenticationFailedException - || event.failure() instanceof AuthenticationCompletionException - || event.failure() instanceof AuthenticationRedirectException)) { - restInitialHandler.beginProcessing(event, event.failure()); - } else { - event.next(); - } - } - }); + public void handle(RoutingContext event) { + + // this condition prevent exception mappers from handling auth failure exceptions when proactive + // security is enabled as for now, community decided that's expected behavior and only way for + // users to handle the exceptions is to define their own failure handler as in Reactive Routes + // more info here: https://github.com/quarkusio/quarkus/pull/28648#issuecomment-1287203946 + final boolean eventFailedByRESTEasyReactive = event + .get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof FailingDefaultAuthFailureHandler; + + if (eventFailedByRESTEasyReactive && (event.failure() instanceof AuthenticationFailedException + || event.failure() instanceof AuthenticationCompletionException + || event.failure() instanceof AuthenticationRedirectException)) { + restInitialHandler.beginProcessing(event, event.failure()); + } else { + event.next(); + } } }; } @@ -342,13 +337,8 @@ private static final class FailingDefaultAuthFailureHandler implements BiConsume @Override public void accept(RoutingContext event, Throwable throwable) { - if (event.failed()) { - //auth failure handler should never get called from route failure handlers - //but if we get to this point bad things have happened, - //so it is better to send a response than to hang - event.end(); - } else { - event.fail(throwable); + if (!event.failed()) { + event.fail(extractRootCause(throwable)); } } } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/FilterBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/FilterBuildItem.java index 0bec0dbac68b5..5e3253279e06c 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/FilterBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/FilterBuildItem.java @@ -18,6 +18,7 @@ public final class FilterBuildItem extends MultiBuildItem { private final Handler handler; private final int priority; + private final boolean isFailureHandler; /** * Creates a new instance of {@link FilterBuildItem}. @@ -28,10 +29,30 @@ public final class FilterBuildItem extends MultiBuildItem { */ public FilterBuildItem(Handler handler, int priority) { this.handler = handler; + checkPriority(priority); + this.priority = priority; + this.isFailureHandler = false; + } + + /** + * Creates a new instance of {@link FilterBuildItem}. + * + * @param handler the handler, if {@code null} the filter won't be used. + * @param priority the priority, higher priority gets invoked first. Priority is only used to sort filters, user + * routes are called afterwards. Must be positive. + * @param isFailureHandler whether an HTTP request or failure should be routed to a handler. + */ + public FilterBuildItem(Handler handler, int priority, boolean isFailureHandler) { + this.handler = handler; + checkPriority(priority); + this.priority = priority; + this.isFailureHandler = isFailureHandler; + } + + private void checkPriority(int priority) { if (priority < 0) { throw new IllegalArgumentException("`priority` must be positive"); } - this.priority = priority; } public Handler getHandler() { @@ -42,11 +63,15 @@ public int getPriority() { return priority; } + public boolean isFailureHandler() { + return isFailureHandler; + } + /** * @return a filter object wrapping the handler and priority. */ public Filter toFilter() { - return new Filters.SimpleFilter(handler, priority); + return new Filters.SimpleFilter(handler, priority, isFailureHandler); } } 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 b596cee278fb3..18f5922d0140d 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 @@ -366,8 +366,13 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute for (Filter filter : filterList) { if (filter.getHandler() != null) { - // Filters with high priority gets called first. - httpRouteRouter.route().order(-1 * filter.getPriority()).handler(filter.getHandler()); + if (filter.isFailureHandler()) { + // Filters handling failures with high priority gets called first. + httpRouteRouter.route().order(-1 * filter.getPriority()).failureHandler(filter.getHandler()); + } else { + // Filters handling HTTP requests with high priority gets called first. + httpRouteRouter.route().order(-1 * filter.getPriority()).handler(filter.getHandler()); + } } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filter.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filter.java index 0376488537ecd..0a26bff30cfb0 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filter.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filter.java @@ -1,16 +1,17 @@ package io.quarkus.vertx.http.runtime.filters; import io.vertx.core.Handler; +import io.vertx.ext.web.Route; import io.vertx.ext.web.RoutingContext; /** - * Represents a Filter, i.e. a route called on every HTTP request. + * Represents a Filter, i.e. a route called on every HTTP request or failure (depending on {@link #isFailureHandler()}). * The priority attribute allows sorting the filters. Highest priority are called first. */ public interface Filter { /** - * The handler called on HTTP request. + * The handler called on HTTP request or failure. * It's important that the handler call {@link RoutingContext#next()} to invoke the next filter or the user routes. * * @return the handler @@ -22,4 +23,14 @@ public interface Filter { */ int getPriority(); + /** + * Whether to add {@link #getHandler()} as HTTP request handler (via {@link Route#handler(Handler)}) or + * as failure handler (via {@link Route#failureHandler(Handler)}). + * + * @return true if filter should be applied on failures rather than HTTP requests + */ + default boolean isFailureHandler() { + return false; + } + } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filters.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filters.java index 335a6f59aae86..86513b9145f0b 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filters.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/Filters.java @@ -56,6 +56,7 @@ public static class SimpleFilter implements Filter { private Handler handler; private int priority; + private boolean isFailureHandler = false; @SuppressWarnings("unused") public SimpleFilter() { @@ -63,11 +64,22 @@ public SimpleFilter() { } public SimpleFilter(Handler handler, int priority) { + checkPriority(priority); + this.handler = handler; + this.priority = priority; + } + + public SimpleFilter(Handler handler, int priority, boolean isFailureHandler) { + checkPriority(priority); + this.handler = handler; + this.priority = priority; + this.isFailureHandler = isFailureHandler; + } + + private void checkPriority(int priority) { if (priority < 0) { throw new IllegalArgumentException("`priority` cannot be negative"); } - this.handler = handler; - this.priority = priority; } public void setHandler(Handler handler) { @@ -88,5 +100,13 @@ public int getPriority() { return priority; } + @Override + public boolean isFailureHandler() { + return isFailureHandler; + } + + public void setFailureHandler(boolean failureHandler) { + isFailureHandler = failureHandler; + } } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java index 05fbc575de746..6267661eb99e1 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityRecorder.java @@ -76,12 +76,7 @@ public void handle(RoutingContext event) { @Override protected void proceed(Throwable throwable) { - if (event.failed()) { - //default auth failure handler should never get called from route failure handlers - //but if we get to this point bad things have happened, - //so it is better to send a response than to hang - event.end(); - } else { + if (!event.failed()) { //failing event makes it possible to customize response via failure handlers //QuarkusErrorHandler will send response if no other failure handler did event.fail(throwable); @@ -356,7 +351,7 @@ private static HttpAuthenticator getAuthenticator(RoutingContext event) { return event.get(HttpAuthenticator.class.getName()); } - private static Throwable extractRootCause(Throwable throwable) { + public static Throwable extractRootCause(Throwable throwable) { while ((throwable instanceof CompletionException && throwable.getCause() != null) || (throwable instanceof CompositeException)) { if (throwable instanceof CompositeException) {