diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java index e1a573d6025..6df0857947f 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java @@ -1,5 +1,6 @@ package org.keycloak.config; +import io.quarkus.runtime.logging.LogRuntimeConfig; import org.jboss.logmanager.handlers.SyslogHandler; import java.io.File; @@ -296,14 +297,12 @@ public class LoggingOptions { .description("Set the Syslog output to JSON or default (plain) unstructured logging.") .build(); - // we can use SyslogConfig.CountingFraming type once https://github.com/quarkusio/quarkus/pull/48479 is present - public static final String SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT = "protocol-dependent"; - public static final Option LOG_SYSLOG_COUNTING_FRAMING = new OptionBuilder<>("log-syslog-counting-framing", String.class) + public static final Option LOG_SYSLOG_COUNTING_FRAMING = new OptionBuilder<>("log-syslog-counting-framing", LogRuntimeConfig.SyslogConfig.CountingFraming.class) .category(OptionCategory.LOGGING) - .expectedValues(Boolean.TRUE.toString(), Boolean.FALSE.toString(), SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT) - .defaultValue(SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT) + .transformEnumValues(true) + .defaultValue(LogRuntimeConfig.SyslogConfig.CountingFraming.PROTOCOL_DEPENDENT) .description("If 'true', the message being sent is prefixed with the size of the message. If '%s', the default value is 'true' when '%s' is 'tcp' or 'ssl-tcp', otherwise 'false'." - .formatted(SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT, LOG_SYSLOG_PROTOCOL.getKey())) + .formatted(Option.transformEnumValue(LogRuntimeConfig.SyslogConfig.CountingFraming.PROTOCOL_DEPENDENT.name()), LOG_SYSLOG_PROTOCOL.getKey())) .build(); // Syslog async diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/Option.java b/quarkus/config-api/src/main/java/org/keycloak/config/Option.java index 77f241bcfe0..5f77156bcba 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/Option.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/Option.java @@ -1,5 +1,7 @@ package org.keycloak.config; +import com.google.common.base.CaseFormat; + import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -128,4 +130,12 @@ public class Option { } return String.valueOf(value); } + + /** + * Transform enum values from upper underscore to lower hyphen + * Transform enum type HAS_SOMETHING -> has-something + */ + public static String transformEnumValue(String value) { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, value); + } } diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/OptionBuilder.java b/quarkus/config-api/src/main/java/org/keycloak/config/OptionBuilder.java index c9e3baa328f..266e321a99a 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/OptionBuilder.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/OptionBuilder.java @@ -23,6 +23,7 @@ public class OptionBuilder { private String description; private Optional defaultValue; private List expectedValues; + private boolean transformEnumValues; // Denotes whether a custom value can be provided among the expected values private boolean strictExpectedValues; private boolean caseInsensitiveExpectedValues; @@ -100,6 +101,14 @@ public class OptionBuilder { return expectedValues(Stream.of(expected).map(Object::toString).collect(Collectors.toList())); } + /** + * For more details, see the {@link Option#transformEnumValue(String)} + */ + public OptionBuilder transformEnumValues(boolean transform) { + this.transformEnumValues = transform; + return this; + } + public OptionBuilder strictExpectedValues(boolean strictExpectedValues) { this.strictExpectedValues = strictExpectedValues; return this; @@ -135,10 +144,12 @@ public class OptionBuilder { expected = auxiliaryType; } + boolean isEnumType = Enum.class.isAssignableFrom(expected); + if (expectedValues == null) { if (Boolean.class.equals(expected)) { expectedValues(BOOLEAN_TYPE_VALUES); - } else if (Enum.class.isAssignableFrom(expected)) { + } else if (isEnumType) { expectedValues((Class) expected); } else { expectedValues = List.of(); @@ -149,6 +160,15 @@ public class OptionBuilder { defaultValue = Optional.of((T) Boolean.FALSE); } + if (transformEnumValues) { + if (isEnumType) { + expectedValues(expectedValues.stream().map(Option::transformEnumValue).toList()); + defaultValue.ifPresent(t -> defaultValue(Optional.of((T) Option.transformEnumValue(t.toString())))); + } else { + throw new IllegalArgumentException("You can use 'transformEnumValues' only for Enum types"); + } + } + return new Option(type, key, category, hidden, build, description, defaultValue, expectedValues, strictExpectedValues, caseInsensitiveExpectedValues, deprecatedMetadata); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java index 063637930e2..269d59d722c 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java @@ -4,7 +4,6 @@ import static org.keycloak.config.LoggingOptions.DEFAULT_LOG_FORMAT; import static org.keycloak.config.LoggingOptions.LOG_CONSOLE_ENABLED; import static org.keycloak.config.LoggingOptions.LOG_FILE_ENABLED; import static org.keycloak.config.LoggingOptions.LOG_SYSLOG_ENABLED; -import static org.keycloak.config.LoggingOptions.SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT; import static org.keycloak.quarkus.runtime.configuration.Configuration.isSet; import static org.keycloak.quarkus.runtime.configuration.Configuration.isTrue; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; @@ -236,7 +235,6 @@ public final class LoggingPropertyMappers { .build(), fromOption(LoggingOptions.LOG_SYSLOG_COUNTING_FRAMING) .isEnabled(LoggingPropertyMappers::isSyslogEnabled, SYSLOG_ENABLED_MSG) - .transformer(LoggingPropertyMappers::resolveSyslogCountingFraming) .to("quarkus.log.syslog.use-counting-framing") .paramLabel("strategy") .build(), @@ -440,20 +438,4 @@ public final class LoggingPropertyMappers { throw new PropertyException(String.format("Invalid value for option '--log-syslog-max-length': %s", e.getMessage())); } } - - // Workaround BEGIN - for https://github.com/keycloak/keycloak/issues/39893 - // Remove once the https://github.com/quarkusio/quarkus/issues/48036 is included in Keycloak as Quarkus might handle it on its own - private static String resolveSyslogCountingFraming(String value, ConfigSourceInterceptorContext context) { - if (SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT.equals(value)) { - return Configuration.getOptionalKcValue(LoggingOptions.LOG_SYSLOG_PROTOCOL) - .map(protocol -> switch (protocol) { - case "tcp", "ssl-tcp" -> Boolean.TRUE.toString(); - case "udp" -> Boolean.FALSE.toString(); - default -> throw new PropertyException("Invalid Syslog protocol: " + protocol); - }) - .orElse(Boolean.FALSE.toString()); - } - return value; - } - // Workaround END } diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java index 394e1469beb..fc0e057e1cc 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/cli/PicocliTest.java @@ -549,7 +549,7 @@ public class PicocliTest extends AbstractConfigurationTest { onAfter(); nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-protocol=ssl-tcp", "--log-syslog-counting-framing=protocol-dependent"); assertThat(nonRunningPicocli.exitCode, is(CommandLine.ExitCode.OK)); - assertThat(nonRunningPicocli.config.getConfigValue("quarkus.log.syslog.use-counting-framing").getValue(), is("true")); + assertThat(nonRunningPicocli.config.getConfigValue("quarkus.log.syslog.use-counting-framing").getValue(), is("protocol-dependent")); onAfter(); nonRunningPicocli = pseudoLaunch("start-dev", "--log=syslog", "--log-syslog-counting-framing=wrong"); diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/LoggingConfigurationTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/LoggingConfigurationTest.java index 2638282e3bd..c9415993ad2 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/LoggingConfigurationTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/LoggingConfigurationTest.java @@ -17,22 +17,20 @@ package org.keycloak.quarkus.runtime.configuration; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.keycloak.config.LoggingOptions.DEFAULT_LOG_FORMAT; import static org.keycloak.config.LoggingOptions.DEFAULT_SYSLOG_OUTPUT; -import static org.keycloak.config.LoggingOptions.SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import io.quarkus.runtime.logging.LogRuntimeConfig; import org.hamcrest.CoreMatchers; import org.junit.Test; import org.keycloak.config.LoggingOptions; @@ -87,7 +85,7 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest { "log-syslog-protocol", "tcp", "log-syslog-format", DEFAULT_LOG_FORMAT, "log-syslog-output", DEFAULT_SYSLOG_OUTPUT.toString(), - "log-syslog-counting-framing", SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT + "log-syslog-counting-framing", "protocol-dependent" )); assertThat(Configuration.getOptionalKcValue(LoggingOptions.LOG_SYSLOG_MAX_LENGTH).orElse(null), CoreMatchers.nullValue()); @@ -97,7 +95,7 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest { "quarkus.log.syslog.syslog-type", "rfc5424", "quarkus.log.syslog.app-name", "keycloak", "quarkus.log.syslog.protocol", "tcp", - "quarkus.log.syslog.use-counting-framing", "true", + "quarkus.log.syslog.use-counting-framing", "protocol-dependent", "quarkus.log.syslog.format", DEFAULT_LOG_FORMAT, "quarkus.log.syslog.json.enabled", "false" )); @@ -175,34 +173,26 @@ public class LoggingConfigurationTest extends AbstractConfigurationTest { @Test public void syslogCountingFraming() { - assertSyslogCountingFramingProtocolDependent("tcp", true); - assertSyslogCountingFramingProtocolDependent("udp", false); - assertSyslogCountingFramingProtocolDependent("ssl-tcp", true); - try { - assertSyslogCountingFramingProtocolDependent("error", false); - fail("Wrong protocol name should throw an error"); - } catch (PropertyException expected) { - assertThat(expected.getMessage(), containsString("Invalid Syslog protocol: error")); - } + assertSyslogCountingFraming(LogRuntimeConfig.SyslogConfig.CountingFraming.TRUE); + assertSyslogCountingFraming(LogRuntimeConfig.SyslogConfig.CountingFraming.FALSE); + assertSyslogCountingFraming(LogRuntimeConfig.SyslogConfig.CountingFraming.PROTOCOL_DEPENDENT); } - protected void assertSyslogCountingFramingProtocolDependent(String protocol, boolean expectedCountingFraming) { + protected void assertSyslogCountingFraming(LogRuntimeConfig.SyslogConfig.CountingFraming countingFraming) { putEnvVars(Map.of( "KC_LOG", "syslog", - "KC_LOG_SYSLOG_PROTOCOL", protocol + "KC_LOG_SYSLOG_COUNTING_FRAMING", countingFraming.toString() )); initConfig(); assertConfig(Map.of( "log-syslog-enabled", "true", - "log-syslog-protocol", protocol, - "log-syslog-counting-framing", SYSLOG_COUNTING_FRAMING_PROTOCOL_DEPENDENT + "log-syslog-counting-framing", countingFraming.toString() )); assertExternalConfig(Map.of( "quarkus.log.syslog.enable", "true", - "quarkus.log.syslog.protocol", protocol, - "quarkus.log.syslog.use-counting-framing", Boolean.toString(expectedCountingFraming) + "quarkus.log.syslog.use-counting-framing", countingFraming.toString() )); onAfter(); }