From 8694c5ca9ca542638f511b7b06844bbdd188bd09 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 26 Apr 2024 16:08:59 +0300 Subject: [PATCH] Always populate metrics uri in presence of auth failures Closes: #40305 --- .../ObservabilityIntegrationBuildItem.java | 9 ++ .../deployment/ObservabilityProcessor.java | 14 +- .../deployment/ResteasyReactiveProcessor.java | 8 +- .../runtime/ResteasyReactiveRecorder.java | 8 +- .../ObservabilityIntegrationRecorder.java | 139 +++++++++--------- 5 files changed, 102 insertions(+), 76 deletions(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java new file mode 100644 index 0000000000000..6db8506c6f418 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java @@ -0,0 +1,9 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * A marker build item signifying that observability features have been integrated + */ +public final class ObservabilityIntegrationBuildItem extends SimpleBuildItem { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java index 54a8626b5fbea..c6625226dcd1b 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java @@ -12,6 +12,7 @@ import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; @@ -42,17 +43,20 @@ public List scan(MethodInfo method, ClassInfo actualEndp @BuildStep @Record(value = ExecutionTime.STATIC_INIT) - FilterBuildItem preAuthFailureFilter(Capabilities capabilities, + void preAuthFailureFilter(Capabilities capabilities, Optional metricsCapability, ObservabilityIntegrationRecorder recorder, - ResteasyReactiveDeploymentBuildItem deployment) { + ResteasyReactiveDeploymentBuildItem deployment, + BuildProducer filterProducer, + BuildProducer observabilityIntegrationProducer) { boolean integrationNeeded = integrationNeeded(capabilities, metricsCapability); if (!integrationNeeded) { - return null; + return; } - return FilterBuildItem.ofPreAuthenticationFailureHandler( - recorder.preAuthFailureHandler(deployment.getDeployment())); + filterProducer.produce(FilterBuildItem.ofPreAuthenticationFailureHandler( + recorder.preAuthFailureHandler(deployment.getDeployment()))); + observabilityIntegrationProducer.produce(new ObservabilityIntegrationBuildItem()); } private boolean integrationNeeded(Capabilities capabilities, diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 8fbaa8e3f20db..a6e846be18697 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -1365,9 +1365,13 @@ private static boolean notFoundCustomExMapper(String builtInExSignature, String @BuildStep @Record(value = ExecutionTime.STATIC_INIT) - public FilterBuildItem addDefaultAuthFailureHandler(ResteasyReactiveRecorder recorder) { + public FilterBuildItem addDefaultAuthFailureHandler(ResteasyReactiveRecorder recorder, + ResteasyReactiveDeploymentBuildItem deployment, + Optional observabilityIntegrationBuildItem) { // replace default auth failure handler added by vertx-http so that our exception mappers can customize response - return new FilterBuildItem(recorder.defaultAuthFailureHandler(), FilterBuildItem.AUTHENTICATION - 1); + return new FilterBuildItem( + recorder.defaultAuthFailureHandler(deployment.getDeployment(), observabilityIntegrationBuildItem.isPresent()), + FilterBuildItem.AUTHENTICATION - 1); } private void checkForDuplicateEndpoint(ResteasyReactiveConfig config, Map> allMethods) { diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java index e54d35e3229ec..7d0a6f1231ae1 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java @@ -53,6 +53,7 @@ import io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory; import io.quarkus.resteasy.reactive.common.runtime.ArcThreadSetupAction; import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveCommonRecorder; +import io.quarkus.resteasy.reactive.server.runtime.observability.ObservabilityIntegrationRecorder; import io.quarkus.runtime.BlockingOperationControl; import io.quarkus.runtime.ExecutorRecorder; import io.quarkus.runtime.LaunchMode; @@ -341,11 +342,16 @@ public ServerSerialisers createServerSerialisers() { return new ServerSerialisers(); } - public Handler defaultAuthFailureHandler() { + public Handler defaultAuthFailureHandler( + RuntimeValue deployment, boolean setTemplatePath) { return new Handler() { @Override public void handle(RoutingContext event) { if (event.get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof DefaultAuthFailureHandler) { + if (setTemplatePath) { + ObservabilityIntegrationRecorder.setTemplatePath(event, deployment.getValue()); + } + // fail event rather than end it, so it's handled by abort handlers (see #addFailureHandler method) event.put(QuarkusHttpUser.AUTH_FAILURE_HANDLER, new FailingDefaultAuthFailureHandler()); } diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java index 72acb0831b1e6..3e550fa61c9e4 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java @@ -47,82 +47,85 @@ private boolean shouldHandle(RoutingContext event) { || event.failure() instanceof ForbiddenException || event.failure() instanceof UnauthorizedException; } + }; + } - private void setTemplatePath(RoutingContext rc, Deployment deployment) { - // do what RestInitialHandler does - var initMappers = new RequestMapper<>(deployment.getClassMappers()); - var requestMatch = initMappers.map(getPathWithoutPrefix(rc, deployment)); - var remaining = requestMatch.remaining.isEmpty() ? "/" : requestMatch.remaining; - - var serverRestHandlers = requestMatch.value.handlers; - if (serverRestHandlers == null || serverRestHandlers.length < 1) { - // nothing we can do - return; - } - var firstHandler = serverRestHandlers[0]; - if (!(firstHandler instanceof ClassRoutingHandler)) { - // nothing we can do - return; - } - - var classRoutingHandler = (ClassRoutingHandler) firstHandler; - var mappers = classRoutingHandler.getMappers(); - - var requestMethod = rc.request().method().name(); - - // do what ClassRoutingHandler does - var mapper = mappers.get(requestMethod); - if (mapper == null) { - if (requestMethod.equals(HttpMethod.HEAD) || requestMethod.equals(HttpMethod.OPTIONS)) { - mapper = mappers.get(HttpMethod.GET); - } - if (mapper == null) { - mapper = mappers.get(null); - } - if (mapper == null) { - // can't match the path - return; - } + public static void setTemplatePath(RoutingContext rc, Deployment deployment) { + // do what RestInitialHandler does + var initMappers = new RequestMapper<>(deployment.getClassMappers()); + var requestMatch = initMappers.map(getPathWithoutPrefix(rc, deployment)); + if (requestMatch == null) { + return; + } + var remaining = requestMatch.remaining.isEmpty() ? "/" : requestMatch.remaining; + + var serverRestHandlers = requestMatch.value.handlers; + if (serverRestHandlers == null || serverRestHandlers.length < 1) { + // nothing we can do + return; + } + var firstHandler = serverRestHandlers[0]; + if (!(firstHandler instanceof ClassRoutingHandler)) { + // nothing we can do + return; + } + + var classRoutingHandler = (ClassRoutingHandler) firstHandler; + var mappers = classRoutingHandler.getMappers(); + + var requestMethod = rc.request().method().name(); + + // do what ClassRoutingHandler does + var mapper = mappers.get(requestMethod); + if (mapper == null) { + if (requestMethod.equals(HttpMethod.HEAD) || requestMethod.equals(HttpMethod.OPTIONS)) { + mapper = mappers.get(HttpMethod.GET); + } + if (mapper == null) { + mapper = mappers.get(null); + } + if (mapper == null) { + // can't match the path + return; + } + } + var target = mapper.map(remaining); + if (target == null) { + if (requestMethod.equals(HttpMethod.HEAD)) { + mapper = mappers.get(HttpMethod.GET); + if (mapper != null) { + target = mapper.map(remaining); } - var target = mapper.map(remaining); - if (target == null) { - if (requestMethod.equals(HttpMethod.HEAD)) { - mapper = mappers.get(HttpMethod.GET); - if (mapper != null) { - target = mapper.map(remaining); - } - } + } - if (target == null) { - // can't match the path - return; - } - } + if (target == null) { + // can't match the path + return; + } + } - var templatePath = requestMatch.template.template + target.template.template; - if (templatePath.endsWith("/")) { - templatePath = templatePath.substring(0, templatePath.length() - 1); - } + var templatePath = requestMatch.template.template + target.template.template; + if (templatePath.endsWith("/")) { + templatePath = templatePath.substring(0, templatePath.length() - 1); + } - setUrlPathTemplate(rc, templatePath); - } + setUrlPathTemplate(rc, templatePath); + } - public String getPath(RoutingContext rc) { - return rc.normalizedPath(); - } + private static String getPath(RoutingContext rc) { + return rc.normalizedPath(); + } - public String getPathWithoutPrefix(RoutingContext rc, Deployment deployment) { - String path = getPath(rc); - if (path != null) { - String prefix = deployment.getPrefix(); - if (!prefix.isEmpty()) { - if (path.startsWith(prefix)) { - return path.substring(prefix.length()); - } - } + private static String getPathWithoutPrefix(RoutingContext rc, Deployment deployment) { + String path = getPath(rc); + if (path != null) { + String prefix = deployment.getPrefix(); + if (!prefix.isEmpty()) { + if (path.startsWith(prefix)) { + return path.substring(prefix.length()); } - return path; } - }; + } + return path; } }