From 4a63fcffaf99666867789562d4aff492089432e9 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Thu, 6 Nov 2025 12:01:18 -0500 Subject: [PATCH] fix: considering source ordinality with spi options (#43805) closes: #43793 Signed-off-by: Steve Hawkins --- docs/guides/server/db.adoc | 6 ++--- .../runtime/configuration/Configuration.java | 2 +- .../MicroProfileConfigProvider.java | 23 +++++++++++++------ .../configuration/mappers/PropertyMapper.java | 4 ++-- .../src/main/resources/application.properties | 6 ++--- .../configuration/ConfigurationTest.java | 6 +++++ .../src/test/resources/conf/keycloak.conf | 1 + 7 files changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/guides/server/db.adoc b/docs/guides/server/db.adoc index a35ecf29549..f848b2251a8 100644 --- a/docs/guides/server/db.adoc +++ b/docs/guides/server/db.adoc @@ -377,17 +377,17 @@ NOTE: Enabling XA transactions in a containerized environment does not fully sup To setup the JPA migrationStrategy (manual/update/validate) you should setup JPA provider as follows: .Setting the `migration-strategy` for the `quarkus` provider of the `connections-jpa` SPI -<@kc.start parameters="--spi-connections--jpa--quarkus-migration-strategy=manual"/> +<@kc.start parameters="--spi-connections-jpa--quarkus--migration-strategy=manual"/> If you want to get a SQL file for DB initialization, too, you have to add this additional SPI initializeEmpty (true/false): .Setting the `initialize-empty` for the `quarkus` provider of the `connections-jpa` SPI -<@kc.start parameters="--spi-connections--jpa--quarkus-initialize-empty=false"/> +<@kc.start parameters="--spi-connections-jpa--quarkus--initialize-empty=false"/> In the same way the migrationExport to point to a specific file and location: .Setting the `migration-export` for the `quarkus` provider of the `connections-jpa` SPI -<@kc.start parameters="--spi-connections--jpa--quarkus-migration-export=/"/> +<@kc.start parameters="--spi-connections-jpa--quarkus--migration-export=/"/> For more information, check the link:{upgrading_guide_link}#_migrate_db[Migrating the database] documentation. diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java index c909a3b24a1..08d085d90eb 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/Configuration.java @@ -54,7 +54,7 @@ public final class Configuration { public static boolean isUserModifiable(ConfigValue configValue) { // This could check as low as SysPropConfigSource DEFAULT_ORDINAL, which is 400 // for now we won't validate these as it's not expected for the user to specify options via system properties - return configValue.getConfigSourceOrdinal() >= KeycloakPropertiesConfigSource.PROPERTIES_FILE_ORDINAL; + return configValue.getConfigSourceName() != null && configValue.getConfigSourceOrdinal() >= KeycloakPropertiesConfigSource.PROPERTIES_FILE_ORDINAL; } public static boolean isSet(Option option) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java index fc0dab64bbe..7bafaad2957 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java @@ -20,14 +20,16 @@ package org.keycloak.quarkus.runtime.configuration; import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR; import static org.keycloak.quarkus.runtime.configuration.Configuration.toDashCase; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import org.eclipse.microprofile.config.ConfigProvider; - +import org.eclipse.microprofile.config.ConfigValue; import org.keycloak.Config; import org.keycloak.Config.Scope; +import io.smallrye.config.SmallRyeConfig; + public class MicroProfileConfigProvider implements Config.ConfigProvider { public static final String NS_KEYCLOAK = "kc"; @@ -36,13 +38,13 @@ public class MicroProfileConfigProvider implements Config.ConfigProvider { public static final String NS_QUARKUS = "quarkus"; public static final String NS_QUARKUS_PREFIX = "quarkus" + "."; - private final org.eclipse.microprofile.config.Config config; + private final SmallRyeConfig config; public MicroProfileConfigProvider() { - this(ConfigProvider.getConfig()); + this(Configuration.getConfig()); } - public MicroProfileConfigProvider(org.eclipse.microprofile.config.Config config) { + public MicroProfileConfigProvider(SmallRyeConfig config) { this.config = config; } @@ -137,8 +139,15 @@ public class MicroProfileConfigProvider implements Config.ConfigProvider { return config.getOptionalValue(separatorPrefix.concat(key), clazz).orElse(defaultValue); } String dashCase = toDashCase(key); - return config.getOptionalValue(separatorPrefix.concat(dashCase), clazz) - .or(() -> config.getOptionalValue(prefix.concat(dashCase), clazz)).orElse(defaultValue); + String name = separatorPrefix.concat(dashCase); + String oldName = prefix.concat(dashCase); + ConfigValue value = config.getConfigValue(name); + ConfigValue oldValue = config.getConfigValue(oldName); + if (value.getValue() == null + || (oldValue.getValue() != null && oldValue.getSourceOrdinal() > value.getSourceOrdinal())) { + value = oldValue; + } + return Optional.ofNullable(config.convert(value.getValue(), clazz)).orElse(defaultValue); } @Override diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java index 1e02c9c99ee..efa31685732 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java @@ -311,8 +311,8 @@ public class PropertyMapper { return configValue; } - // by unsetting the ordinal this will not be seen as directly modified by the user - return configValue.from().withName(name).withValue(mappedValue).withRawValue(value).withConfigSourceOrdinal(0).build(); + // by unsetting the name this will not be seen as directly modified by the user + return configValue.from().withName(name).withValue(mappedValue).withRawValue(value).withConfigSourceName(null).build(); } private ConfigValue convertValue(ConfigValue configValue) { diff --git a/quarkus/runtime/src/main/resources/application.properties b/quarkus/runtime/src/main/resources/application.properties index a7b9fe6b557..9345aa38f5e 100644 --- a/quarkus/runtime/src/main/resources/application.properties +++ b/quarkus/runtime/src/main/resources/application.properties @@ -77,9 +77,9 @@ quarkus.http.limits.max-header-size=65535 %dev.kc.http-enabled=true %dev.kc.hostname-strict=false %dev.kc.cache=local -%dev.kc.spi-theme--cache-themes=false -%dev.kc.spi-theme--cache-templates=false -%dev.kc.spi-theme--static-max-age=-1 +%dev.kc.spi-theme--cache--themes=false +%dev.kc.spi-theme--cache--templates=false +%dev.kc.spi-theme--static--max-age=-1 # The default configuration when running import, export, bootstrap-admin %nonserver.kc.http-enabled=true diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/ConfigurationTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/ConfigurationTest.java index 4988d63c022..866c3529b11 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/ConfigurationTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/ConfigurationTest.java @@ -86,6 +86,12 @@ public class ConfigurationTest extends AbstractConfigurationTest { assertEquals("http://envvar.unittest", initConfig("hostname", "default").get("frontendUrl")); } + @Test + public void testEnvVarPriorityOverPropertiesFileMixedSpiNamingConventions() { + putEnvVar("KC_SPI_HOSTNAME_OTHER_FRONTEND_URL", "http://envvar.unittest"); + assertEquals("http://envvar.unittest", initConfig("hostname", "other").get("frontendUrl")); + } + @Test public void testKeycloakConfPlaceholder() { assertEquals("info", createConfig().getRawValue("kc.log-level")); diff --git a/quarkus/runtime/src/test/resources/conf/keycloak.conf b/quarkus/runtime/src/test/resources/conf/keycloak.conf index c8bd9b1d8ae..383af3ed488 100644 --- a/quarkus/runtime/src/test/resources/conf/keycloak.conf +++ b/quarkus/runtime/src/test/resources/conf/keycloak.conf @@ -1,4 +1,5 @@ spi-hostname-default-frontend-url = ${keycloak.frontendUrl:http://filepropdefault.unittest} +spi-hostname--other--frontend-url = ${keycloak.frontendUrl:http://filepropdefault.unittest} log-level=${SOME_LOG_LEVEL:info} log-level-io.k8s=${SOME_CATEGORY_LOG_LEVEL} KC_LOG_LEVEL_IO_QUARKUS=trace