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
This commit is contained in:
Joerg Matysiak 2023-02-17 11:41:06 +01:00 committed by Pedro Igor
parent 48519be52b
commit 8a2d645dd4
2 changed files with 126 additions and 5 deletions

View file

@ -78,6 +78,7 @@ import io.quarkus.hibernate.orm.deployment.PersistenceXmlDescriptorBuildItem;
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem; import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentCustomizerBuildItem; import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentCustomizerBuildItem;
import io.quarkus.runtime.configuration.ProfileManager; 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.deployment.RouteBuildItem;
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
import io.smallrye.config.ConfigValue; import io.smallrye.config.ConfigValue;
@ -179,6 +180,10 @@ class KeycloakProcessor {
ClasspathThemeResourceProviderFactory.class, ClasspathThemeResourceProviderFactory.class,
JarThemeProviderFactory.class, JarThemeProviderFactory.class,
JpaMapStorageProviderFactory.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 { static {
DEPLOYEABLE_SCRIPT_PROVIDERS.put(AUTHENTICATORS, KeycloakProcessor::registerScriptAuthenticator); DEPLOYEABLE_SCRIPT_PROVIDERS.put(AUTHENTICATORS, KeycloakProcessor::registerScriptAuthenticator);
@ -562,17 +567,20 @@ class KeycloakProcessor {
@Record(ExecutionTime.RUNTIME_INIT) @Record(ExecutionTime.RUNTIME_INIT)
@BuildStep @BuildStep
void initializeFilter(BuildProducer<FilterBuildItem> filters, KeycloakRecorder recorder, HttpBuildTimeConfig httpBuildConfig, void initializeFilter(BuildProducer<FilterBuildItem> filters, KeycloakRecorder recorder, ExecutorBuildItem executor, NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem) {
ExecutorBuildItem executor) {
String rootPath = httpBuildConfig.rootPath;
List<String> ignoredPaths = new ArrayList<>(); List<String> ignoredPaths = new ArrayList<>();
if (isHealthEnabled()) { if (isHealthEnabled()) {
ignoredPaths.add(rootPath + "health"); ignoredPaths.add(nonApplicationRootPathBuildItem.
resolvePath(Configuration.getOptionalValue(QUARKUS_HEALTH_ROOT_PROPERTY)
.orElse(QUARKUS_DEFAULT_HEALTH_PATH)));
} }
if (isMetricsEnabled()) { 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)); filters.produce(new FilterBuildItem(recorder.createRequestFilter(ignoredPaths, executor.getExecutorProxy()),FilterBuildItem.AUTHORIZATION - 10));

View file

@ -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);
}
}