From 8a2d645dd4aaee061aae2598445d7eea1beffef8 Mon Sep 17 00:00:00 2001 From: Joerg Matysiak Date: Fri, 17 Feb 2023 11:41:06 +0100 Subject: [PATCH] Avoid internal server error when root path and non-appliation root path are both set and the wrong metrics/health endpoint is called. Fixes #17166 Avoid internal server error when root path and non-appliation root path are both set and the wrong metrics/health endpoint is called. Fixes #17166 --- .../quarkus/deployment/KeycloakProcessor.java | 18 ++- .../health/KeycloakPathConfigurationTest.java | 113 ++++++++++++++++++ 2 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 quarkus/deployment/src/test/java/test/org/keycloak/quarkus/services/health/KeycloakPathConfigurationTest.java diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 7ea4122516..108d8dd869 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -78,6 +78,7 @@ import io.quarkus.hibernate.orm.deployment.PersistenceXmlDescriptorBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentCustomizerBuildItem; import io.quarkus.runtime.configuration.ProfileManager; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.smallrye.config.ConfigValue; @@ -179,6 +180,10 @@ class KeycloakProcessor { ClasspathThemeResourceProviderFactory.class, JarThemeProviderFactory.class, JpaMapStorageProviderFactory.class); + public static final String QUARKUS_HEALTH_ROOT_PROPERTY = "quarkus.smallrye-health.root-path"; + public static final String QUARKUS_METRICS_PATH_PROPERTY = "quarkus.micrometer.export.prometheus.path"; + public static final String QUARKUS_DEFAULT_HEALTH_PATH = "health"; + public static final String QUARKUS_DEFAULT_METRICS_PATH = "metrics"; static { DEPLOYEABLE_SCRIPT_PROVIDERS.put(AUTHENTICATORS, KeycloakProcessor::registerScriptAuthenticator); @@ -562,17 +567,20 @@ class KeycloakProcessor { @Record(ExecutionTime.RUNTIME_INIT) @BuildStep - void initializeFilter(BuildProducer filters, KeycloakRecorder recorder, HttpBuildTimeConfig httpBuildConfig, - ExecutorBuildItem executor) { - String rootPath = httpBuildConfig.rootPath; + void initializeFilter(BuildProducer filters, KeycloakRecorder recorder, ExecutorBuildItem executor, NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem) { + List ignoredPaths = new ArrayList<>(); if (isHealthEnabled()) { - ignoredPaths.add(rootPath + "health"); + ignoredPaths.add(nonApplicationRootPathBuildItem. + resolvePath(Configuration.getOptionalValue(QUARKUS_HEALTH_ROOT_PROPERTY) + .orElse(QUARKUS_DEFAULT_HEALTH_PATH))); } if (isMetricsEnabled()) { - ignoredPaths.add(rootPath + "metrics"); + ignoredPaths.add(nonApplicationRootPathBuildItem. + resolvePath(Configuration.getOptionalValue(QUARKUS_METRICS_PATH_PROPERTY) + .orElse(QUARKUS_DEFAULT_METRICS_PATH))); } filters.produce(new FilterBuildItem(recorder.createRequestFilter(ignoredPaths, executor.getExecutorProxy()),FilterBuildItem.AUTHORIZATION - 10)); diff --git a/quarkus/deployment/src/test/java/test/org/keycloak/quarkus/services/health/KeycloakPathConfigurationTest.java b/quarkus/deployment/src/test/java/test/org/keycloak/quarkus/services/health/KeycloakPathConfigurationTest.java new file mode 100644 index 0000000000..5eb4db07ad --- /dev/null +++ b/quarkus/deployment/src/test/java/test/org/keycloak/quarkus/services/health/KeycloakPathConfigurationTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package test.org.keycloak.quarkus.services.health; + +import io.quarkus.test.QuarkusUnitTest; +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; + +import static io.restassured.RestAssured.given; + +class KeycloakPathConfigurationTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("keycloak.conf", "META-INF/keycloak.conf")) + .overrideConfigKey("kc.http-relative-path","/auth") + .overrideConfigKey("quarkus.http.non-application-root-path", "/q") + .overrideConfigKey("quarkus.micrometer.export.prometheus.path", "/prom/metrics"); + + + @Test + void testHealth() { + given().basePath("/") + .when().get("q/health") + .then() + .statusCode(200); + } + + @Test + void testWrongHealthEndpoints() { + given().basePath("/") + .when().get("health") + .then() + // Health is available under `/q/health` (see non-application-root-path), + // so /health should return 404. + .statusCode(404); + + given().basePath("/") + .when().get("auth/health") + .then() + // Health is available under `/q/health` (see non-application-root-path), + // so /auth/health one should return 404. + .statusCode(404); + } + + @Test + void testMetrics() { + given().basePath("/") + .when().get("prom/metrics") + .then() + .statusCode(200); + } + + @Test + void testWrongMetricsEndpoints() { + given().basePath("/") + .when().get("metrics") + .then() + // Metrics is available under `/prom/metrics` (see non-application-root-path), + // so /metrics should return 404. + .statusCode(404); + + given().basePath("/") + .when().get("auth/metrics") + .then() + // Metrics is available under `/prom/metrics` (see non-application-root-path), + // so /auth/metrics should return 404. + .statusCode(404); + + given().basePath("/") + .when().get("q/metrics") + .then() + // Metrics is available under `/prom/metrics` (see non-application-root-path), + // so /q/metrics should return 404. + .statusCode(404); + + } + + @Test + void testAuthEndpointAvailable() { + + given().basePath("/") + .when().get("auth") + .then() + .statusCode(200); + } + + @Test + void testRootUnavailable() { + given().basePath("/") + .when().get("") + .then() + // application root is configured to /auth, so we expect 404 on / + .statusCode(404); + } +}