Hibernate Validator is enabled by default when not used (#45681)

* Hibernate Validator is enabled by default when not used

Closes #45677

Signed-off-by: Martin Bartoš <mabartos@redhat.com>

* Disable Hibernate Validator factory customizer only for non testing

Signed-off-by: Martin Bartoš <mabartos@redhat.com>

---------

Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
Martin Bartoš 2026-01-22 16:59:39 +01:00 committed by GitHub
parent 50366f03a6
commit 44375e2178
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 52 additions and 13 deletions

View file

@ -100,6 +100,7 @@ import org.keycloak.quarkus.runtime.services.health.KeycloakClusterReadyHealthCh
import org.keycloak.quarkus.runtime.services.health.KeycloakReadyHealthCheck;
import org.keycloak.quarkus.runtime.storage.database.jpa.NamedJpaConnectionProviderFactory;
import org.keycloak.quarkus.runtime.themes.FlatClasspathThemeResourceProviderFactory;
import org.keycloak.quarkus.runtime.validation.HibernateValidatorFactoryCustomizer;
import org.keycloak.representations.provider.ScriptProviderDescriptor;
import org.keycloak.representations.provider.ScriptProviderMetadata;
import org.keycloak.representations.userprofile.config.UPConfig;
@ -130,6 +131,7 @@ import io.quarkus.bootstrap.logging.InitialConfigurator;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem;
import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsTest;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
@ -860,6 +862,16 @@ class KeycloakProcessor {
removeBeans.produce(new BuildTimeConditionBuildItem(disabledBean.asClass(), false));
}
@BuildStep(onlyIfNot = IsTest.class) // needed for embedded Keycloak
void disableHibernateValidatorCustomizer(BuildProducer<BuildTimeConditionBuildItem> removeBeans, CombinedIndexBuildItem index) {
if (!Profile.isFeatureEnabled(Profile.Feature.CLIENT_ADMIN_API_V2)) {
// disables the filter
ClassInfo disabledBean = index.getIndex()
.getClassByName(DotName.createSimple(HibernateValidatorFactoryCustomizer.class.getName()));
removeBeans.produce(new BuildTimeConditionBuildItem(disabledBean.asClass(), false));
}
}
@BuildStep
void disableMdcContextFilter(BuildProducer<BuildTimeConditionBuildItem> removeBeans, CombinedIndexBuildItem index) {
if (!Configuration.isTrue(LoggingOptions.LOG_MDC_ENABLED)) {

View file

@ -46,7 +46,8 @@ public class IgnoredArtifacts {
metrics(),
otelMetrics(),
openApi(),
openApiSwagger()
openApiSwagger(),
hibernateValidator()
)
.flatMap(Collection::stream)
.collect(Collectors.toUnmodifiableSet());
@ -212,4 +213,17 @@ public class IgnoredArtifacts {
boolean isEnabled = Configuration.isTrue(OpenApiOptions.OPENAPI_UI_ENABLED);
return !isEnabled ? OPENAPI_SWAGGER : emptySet();
}
// Hibernate Validator
public static Set<String> HIBERNATE_VALIDATOR = Set.of(
"io.quarkus:quarkus-hibernate-validator",
"io.quarkus:quarkus-hibernate-validator-deployment",
"io.quarkus:quarkus-hibernate-validator-spi",
"org.hibernate.validator:hibernate-validator"
);
private static Set<String> hibernateValidator() {
boolean isEnabled = Profile.isFeatureEnabled(Profile.Feature.CLIENT_ADMIN_API_V2);
return !isEnabled ? HIBERNATE_VALIDATOR : emptySet();
}
}

View file

@ -44,8 +44,11 @@ import static org.keycloak.quarkus.runtime.configuration.IgnoredArtifacts.JDBC_O
import static org.keycloak.quarkus.runtime.configuration.IgnoredArtifacts.JDBC_POSTGRES;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
import static org.hamcrest.CoreMatchers.everyItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.in;
import static org.junit.Assert.assertTrue;
public class IgnoredArtifactsTest extends AbstractConfigurationTest {
@ -61,10 +64,7 @@ public class IgnoredArtifactsTest extends AbstractConfigurationTest {
@Test
public void fipsEnabled() {
Properties properties = new Properties();
properties.setProperty("keycloak.profile.feature.fips", "enabled");
var profile = Profile.configure(new PropertiesProfileConfigResolver(properties));
var profile = getProfileWithEnabledFeature(Profile.Feature.FIPS);
assertThat(profile.isFeatureEnabled(Profile.Feature.FIPS), is(true));
var ignoredArtifacts = IgnoredArtifacts.getDefaultIgnoredArtifacts();
@ -172,6 +172,21 @@ public class IgnoredArtifactsTest extends AbstractConfigurationTest {
assertIgnoredArtifacts(IgnoredArtifacts.OPENAPI_SWAGGER, OpenApiOptions.OPENAPI_UI_ENABLED);
}
@Test
public void hibernateValidator() {
var profile = Profile.defaults();
assertThat(profile.isFeatureEnabled(Profile.Feature.CLIENT_ADMIN_API_V2), is(false));
var ignoredArtifacts = IgnoredArtifacts.getDefaultIgnoredArtifacts();
assertThat(IgnoredArtifacts.HIBERNATE_VALIDATOR, everyItem(in(ignoredArtifacts)));
profile = getProfileWithEnabledFeature(Profile.Feature.CLIENT_ADMIN_API_V2);
assertThat(profile.isFeatureEnabled(Profile.Feature.CLIENT_ADMIN_API_V2), is(true));
ignoredArtifacts = IgnoredArtifacts.getDefaultIgnoredArtifacts();
assertThat(IgnoredArtifacts.HIBERNATE_VALIDATOR, everyItem(not(in(ignoredArtifacts))));
}
private void assertIgnoredArtifacts(Set<String> artifactsSet, Option<Boolean> enabledOption) {
assertIgnoredArtifacts(artifactsSet, enabledOption, true);
}
@ -185,4 +200,10 @@ public class IgnoredArtifactsTest extends AbstractConfigurationTest {
assertThat(artifacts.containsAll(artifactsSet), is(!disabledByDefault));
});
}
private Profile getProfileWithEnabledFeature(Profile.Feature feature) {
Properties properties = new Properties();
properties.setProperty("keycloak.profile.feature.%s".formatted(feature.name().toLowerCase()), "enabled");
return Profile.configure(new PropertiesProfileConfigResolver(properties));
}
}

View file

@ -81,11 +81,6 @@
<groupId>org.twitter4j</groupId>
<artifactId>twitter4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version> <!--Not sure why we need to set it as it should be part of dependencyManagement-->
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>

View file

@ -8,7 +8,6 @@ import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.validation.ValidationException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
@ -102,8 +101,6 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
error.setErrorDescription("Cannot parse the JSON");
} else if (isServerError) {
error.setErrorDescription("For more on this error consult the server log.");
} else if (throwable instanceof ValidationException) {
error.setErrorDescription(throwable.getMessage());
}
return Response.status(responseStatus)