From 3abcc699a10428c7afde8de051cedb4d7a47f3a7 Mon Sep 17 00:00:00 2001 From: andreaTP Date: Thu, 2 Jun 2022 15:17:41 +0100 Subject: [PATCH] Move all dist options to the new module Co-authored-by: Pedro Igor --- config-api/pom.xml | 7 + .../java/org/keycloak/config/AllOptions.java | 12 +- .../keycloak/config/ClusteringOptions.java | 54 +++++++ .../org/keycloak/config/DatabaseOptions.java | 104 ++++++++++++ .../org/keycloak/config/FeatureOptions.java | 42 +++++ .../org/keycloak/config/HealthOptions.java | 21 +++ .../org/keycloak/config/HostnameOptions.java | 57 +++++++ .../java/org/keycloak/config/HttpOptions.java | 118 +++++++++++++- .../org/keycloak/config/LoggingOptions.java | 116 ++++++++++++++ .../org/keycloak/config/MetricsOptions.java | 21 +++ .../java/org/keycloak/config/MultiOption.java | 19 +++ .../main/java/org/keycloak/config/Option.java | 19 ++- .../org/keycloak/config/OptionBuilder.java | 54 +++++-- .../org/keycloak/config/ProxyOptions.java | 36 +++++ .../keycloak/config/TransactionOptions.java | 21 +++ .../org/keycloak/config/VaultOptions.java | 56 +++++++ .../keycloak/config}/database/Database.java | 14 +- operator/app/pom.xml | 2 +- .../operator/maven/ServerConfigGen.java | 8 +- .../deployment/LiquibaseProcessor.java | 2 +- .../keycloak/quarkus/runtime/cli/Picocli.java | 4 + .../mappers/ClusteringPropertyMappers.java | 67 +++----- .../mappers/DatabasePropertyMappers.java | 96 +++++------ .../mappers/FeaturePropertyMappers.java | 32 +--- .../mappers/HealthPropertyMappers.java | 13 +- .../mappers/HostnamePropertyMappers.java | 34 ++-- .../mappers/HttpPropertyMappers.java | 67 ++------ .../mappers/LoggingPropertyMappers.java | 149 +++++++----------- .../mappers/MetricsPropertyMappers.java | 13 +- .../configuration/mappers/PropertyMapper.java | 147 ++++------------- .../mappers/PropertyMappers.java | 4 +- .../mappers/ProxyPropertyMappers.java | 53 +++---- .../mappers/TransactionPropertyMappers.java | 16 +- .../mappers/VaultPropertyMappers.java | 34 +--- .../keycloak/it/cli/dist/LoggingDistTest.java | 7 +- 35 files changed, 985 insertions(+), 534 deletions(-) create mode 100644 config-api/src/main/java/org/keycloak/config/ClusteringOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/DatabaseOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/FeatureOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/HealthOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/HostnameOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/LoggingOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/MetricsOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/MultiOption.java create mode 100644 config-api/src/main/java/org/keycloak/config/ProxyOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/TransactionOptions.java create mode 100644 config-api/src/main/java/org/keycloak/config/VaultOptions.java rename {quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage => config-api/src/main/java/org/keycloak/config}/database/Database.java (97%) diff --git a/config-api/pom.xml b/config-api/pom.xml index 0608f0e2762b..28f9cb0deb6a 100755 --- a/config-api/pom.xml +++ b/config-api/pom.xml @@ -38,4 +38,11 @@ 11 + + + org.keycloak + keycloak-common + + + diff --git a/config-api/src/main/java/org/keycloak/config/AllOptions.java b/config-api/src/main/java/org/keycloak/config/AllOptions.java index a33e0657d140..7db3a08fe70c 100644 --- a/config-api/src/main/java/org/keycloak/config/AllOptions.java +++ b/config-api/src/main/java/org/keycloak/config/AllOptions.java @@ -5,9 +5,19 @@ public class AllOptions { - public final static List> ALL_OPTIONS = new ArrayList<>(); + public static final List> ALL_OPTIONS = new ArrayList<>(); static { + ALL_OPTIONS.addAll(ClusteringOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(DatabaseOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(FeatureOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(HealthOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(HostnameOptions.ALL_OPTIONS); ALL_OPTIONS.addAll(HttpOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(LoggingOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(MetricsOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(ProxyOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(TransactionOptions.ALL_OPTIONS); + ALL_OPTIONS.addAll(VaultOptions.ALL_OPTIONS); } } diff --git a/config-api/src/main/java/org/keycloak/config/ClusteringOptions.java b/config-api/src/main/java/org/keycloak/config/ClusteringOptions.java new file mode 100644 index 000000000000..ba4120220261 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/ClusteringOptions.java @@ -0,0 +1,54 @@ +package org.keycloak.config; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class ClusteringOptions { + + public enum Mechanism { + ispn, + local + } + + public static final Option CACHE = new OptionBuilder<>("cache", Mechanism.class) + .category(OptionCategory.CLUSTERING) + .description("Defines the cache mechanism for high-availability. " + + "By default, a 'ispn' cache is used to create a cluster between multiple server nodes. " + + "A 'local' cache disables clustering and is intended for development and testing purposes.") + .defaultValue(Mechanism.ispn) + .buildTime(true) + .build(); + + public enum Stack { + tcp, + udp, + kubernetes, + ec2, + azure, + google; + } + + public static final Option CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class) + .category(OptionCategory.CLUSTERING) + .description("Define the default stack to use for cluster communication and node discovery. This option only takes effect " + + "if 'cache' is set to 'ispn'. Default: udp.") + .buildTime(true) + .expectedValues(Stack.values()) + .build(); + + public static final Option CACHE_CONFIG_FILE = new OptionBuilder<>("cache-config-file", File.class) + .category(OptionCategory.CLUSTERING) + .description("Defines the file from which cache configuration should be loaded from. " + + "The configuration file is relative to the 'conf/' directory.") + .buildTime(true) + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(CACHE); + ALL_OPTIONS.add(CACHE_STACK); + ALL_OPTIONS.add(CACHE_CONFIG_FILE); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/DatabaseOptions.java b/config-api/src/main/java/org/keycloak/config/DatabaseOptions.java new file mode 100644 index 000000000000..bf7a68c17690 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/DatabaseOptions.java @@ -0,0 +1,104 @@ +package org.keycloak.config; + +import org.keycloak.config.database.Database; + +import java.util.ArrayList; +import java.util.List; + +public class DatabaseOptions { + + public static final Option DB_DIALECT = new OptionBuilder<>("db-dialect", String.class) + .category(OptionCategory.DATABASE) + .runtimes(Option.Runtime.OPERATOR) + .buildTime(true) + .build(); + + public static final Option DB_DRIVER = new OptionBuilder<>("db-driver", String.class) + .category(OptionCategory.DATABASE) + .runtimes(Option.Runtime.OPERATOR) + .defaultValue(Database.getDriver("dev-file", true).get()) + .build(); + + public static final Option DB = new OptionBuilder<>("db", Database.Vendor.class) + .category(OptionCategory.DATABASE) + .description("The database vendor. Possible values are: " + String.join(", ", Database.getAliases())) + .expectedStringValues(Database.getAliases()) + .buildTime(true) + .build(); + + public static final Option DB_URL = new OptionBuilder<>("db-url", String.class) + .category(OptionCategory.DATABASE) + .description("The full database JDBC URL. If not provided, a default URL is set based on the selected database vendor. " + + "For instance, if using 'postgres', the default JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. ") + .build(); + + public static final Option DB_URL_HOST = new OptionBuilder<>("db-url-host", String.class) + .category(OptionCategory.DATABASE) + .description("Sets the hostname of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") + .build(); + + public static final Option DB_URL_DATABASE = new OptionBuilder<>("db-url-database", String.class) + .category(OptionCategory.DATABASE) + .description("Sets the database name of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") + .build(); + + public static final Option DB_URL_PORT = new OptionBuilder<>("db-url-port", Integer.class) + .category(OptionCategory.DATABASE) + .description("Sets the port of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") + .build(); + + public static final Option DB_URL_PROPERTIES = new OptionBuilder<>("db-url-properties", String.class) + .category(OptionCategory.DATABASE) + .description("Sets the properties of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") + .build(); + + public static final Option DB_USERNAME = new OptionBuilder<>("db-username", String.class) + .category(OptionCategory.DATABASE) + .description("The username of the database user.") + .build(); + + public static final Option DB_PASSWORD = new OptionBuilder<>("db-password", String.class) + .category(OptionCategory.DATABASE) + .description("The password of the database user.") + .build(); + + public static final Option DB_SCHEMA = new OptionBuilder<>("db-schema", String.class) + .category(OptionCategory.DATABASE) + .description("The database schema to be used.") + .build(); + + public static final Option DB_POOL_INITIAL_SIZE = new OptionBuilder<>("db-pool-initial-size", Integer.class) + .category(OptionCategory.DATABASE) + .description("The initial size of the connection pool.") + .build(); + + public static final Option DB_POOL_MIN_SIZE = new OptionBuilder<>("db-pool-min-size", Integer.class) + .category(OptionCategory.DATABASE) + .description("The minimal size of the connection pool.") + .build(); + + public static final Option DB_POOL_MAX_SIZE = new OptionBuilder<>("db-pool-max-size", Integer.class) + .category(OptionCategory.DATABASE) + .defaultValue(100) + .description("The maximum size of the connection pool.") + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(DB_DIALECT); + ALL_OPTIONS.add(DB_DRIVER); + ALL_OPTIONS.add(DB); + ALL_OPTIONS.add(DB_URL); + ALL_OPTIONS.add(DB_URL_HOST); + ALL_OPTIONS.add(DB_URL_DATABASE); + ALL_OPTIONS.add(DB_URL_PORT); + ALL_OPTIONS.add(DB_URL_PROPERTIES); + ALL_OPTIONS.add(DB_USERNAME); + ALL_OPTIONS.add(DB_PASSWORD); + ALL_OPTIONS.add(DB_SCHEMA); + ALL_OPTIONS.add(DB_POOL_INITIAL_SIZE); + ALL_OPTIONS.add(DB_POOL_MIN_SIZE); + ALL_OPTIONS.add(DB_POOL_MAX_SIZE); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/FeatureOptions.java b/config-api/src/main/java/org/keycloak/config/FeatureOptions.java new file mode 100644 index 000000000000..80c72f5315fd --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/FeatureOptions.java @@ -0,0 +1,42 @@ +package org.keycloak.config; + +import org.keycloak.common.Profile; + +import java.util.ArrayList; +import java.util.List; + +public class FeatureOptions { + + public static final Option FEATURES = new OptionBuilder("features", List.class, Profile.Feature.class) + .category(OptionCategory.FEATURE) + .description("Enables a set of one or more features.") + .expectedStringValues(getFeatureValues()) + .buildTime(true) + .build(); + + public static final Option FEATURES_DISABLED = new OptionBuilder("features-disabled", List.class, Profile.Feature.class) + .category(OptionCategory.FEATURE) + .description("Disables a set of one or more features.") + .expectedStringValues(getFeatureValues()) + .buildTime(true) + .build(); + + private static List getFeatureValues() { + List features = new ArrayList<>(); + + for (Profile.Feature value : Profile.Feature.values()) { + features.add(value.name().toLowerCase().replace('_', '-')); + } + + features.add(Profile.Type.PREVIEW.name().toLowerCase()); + + return features; + } + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(FEATURES); + ALL_OPTIONS.add(FEATURES_DISABLED); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/HealthOptions.java b/config-api/src/main/java/org/keycloak/config/HealthOptions.java new file mode 100644 index 000000000000..1f75d62f9b82 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/HealthOptions.java @@ -0,0 +1,21 @@ +package org.keycloak.config; + +import java.util.ArrayList; +import java.util.List; + +public class HealthOptions { + + public static final Option HEALTH_ENABLED = new OptionBuilder<>("health-enabled", Boolean.class) + .category(OptionCategory.HEALTH) + .description("If the server should expose health check endpoints. If enabled, health checks are available at the '/health', '/health/ready' and '/health/live' endpoints.") + .defaultValue(Boolean.FALSE) + .buildTime(true) + .expectedValues(Boolean.TRUE, Boolean.FALSE) + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(HEALTH_ENABLED); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/HostnameOptions.java b/config-api/src/main/java/org/keycloak/config/HostnameOptions.java new file mode 100644 index 000000000000..39723ad847b0 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/HostnameOptions.java @@ -0,0 +1,57 @@ +package org.keycloak.config; + +import java.util.ArrayList; +import java.util.List; + +public class HostnameOptions { + + public static final Option HOSTNAME = new OptionBuilder<>("hostname", String.class) + .category(OptionCategory.HOSTNAME) + .description("Hostname for the Keycloak server.") + .build(); + + public static final Option HOSTNAME_ADMIN = new OptionBuilder<>("hostname-admin", String.class) + .category(OptionCategory.HOSTNAME) + .description("The hostname for accessing the administration console. Use this option if you are exposing the administration console using a hostname other than the value set to the 'hostname' option.") + .build(); + + public static final Option HOSTNAME_STRICT = new OptionBuilder<>("hostname-strict", Boolean.class) + .category(OptionCategory.HOSTNAME) + .description("Disables dynamically resolving the hostname from request headers. Should always be set to true in production, unless proxy verifies the Host header.") + .defaultValue(Boolean.TRUE) + .build(); + + public static final Option HOSTNAME_STRICT_HTTPS = new OptionBuilder<>("hostname-strict-https", Boolean.class) + .category(OptionCategory.HOSTNAME) + .description("Forces URLs to use HTTPS. Only needed if proxy does not properly set the X-Forwarded-Proto header.") + .runtimes(Option.Runtime.OPERATOR) + .defaultValue(Boolean.TRUE) + .build(); + + public static final Option HOSTNAME_STRICT_BACKCHANNEL = new OptionBuilder<>("hostname-strict-backchannel", Boolean.class) + .category(OptionCategory.HOSTNAME) + .description("By default backchannel URLs are dynamically resolved from request headers to allow internal and external applications. If all applications use the public URL this option should be enabled.") + .build(); + + public static final Option HOSTNAME_PATH = new OptionBuilder<>("hostname-path", String.class) + .category(OptionCategory.HOSTNAME) + .description("This should be set if proxy uses a different context-path for Keycloak.") + .build(); + + public static final Option HOSTNAME_PORT = new OptionBuilder<>("hostname-port", Integer.class) + .category(OptionCategory.HOSTNAME) + .description("The port used by the proxy when exposing the hostname. Set this option if the proxy uses a port other than the default HTTP and HTTPS ports.") + .defaultValue(-1) + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(HOSTNAME); + ALL_OPTIONS.add(HOSTNAME_STRICT); + ALL_OPTIONS.add(HOSTNAME_STRICT_HTTPS); + ALL_OPTIONS.add(HOSTNAME_STRICT_BACKCHANNEL); + ALL_OPTIONS.add(HOSTNAME_PATH); + ALL_OPTIONS.add(HOSTNAME_PORT); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/HttpOptions.java b/config-api/src/main/java/org/keycloak/config/HttpOptions.java index 7dd7b1a8c56e..a720cddaa85e 100644 --- a/config-api/src/main/java/org/keycloak/config/HttpOptions.java +++ b/config-api/src/main/java/org/keycloak/config/HttpOptions.java @@ -1,20 +1,128 @@ package org.keycloak.config; +import java.io.File; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; public class HttpOptions { - public final static Option httpPort = new OptionBuilder("http-port", Integer.class) - .description("The used HTTP port.") + public static final Option HTTP_ENABLED = new OptionBuilder<>("http-enabled", Boolean.class) + .category(OptionCategory.HTTP) + .description("Enables the HTTP listener.") + .defaultValue(Boolean.FALSE) + .expectedValues(Boolean.TRUE, Boolean.FALSE) + .build(); + + public static final Option HTTP_HOST = new OptionBuilder<>("http-host", String.class) + .category(OptionCategory.HTTP) + .description("The used HTTP Host.") + .defaultValue("0.0.0.0") + .build(); + + public static final Option HTTP_RELATIVE_PATH = new OptionBuilder<>("http-relative-path", String.class) .category(OptionCategory.HTTP) + .description("Set the path relative to '/' for serving resources.") + .defaultValue("/") + .buildTime(true) + .build(); + + public static final Option HTTP_PORT = new OptionBuilder<>("http-port", Integer.class) + .category(OptionCategory.HTTP) + .description("The used HTTP port.") .defaultValue(8080) .build(); - public final static List> ALL_OPTIONS = new ArrayList<>(); + public static final Option HTTPS_PORT = new OptionBuilder<>("https-port", Integer.class) + .category(OptionCategory.HTTP) + .description("The used HTTPS port.") + .defaultValue(8443) + .build(); + + public enum ClientAuth { + none, + request, + required + } + + public static final Option HTTPS_CLIENT_AUTH = new OptionBuilder<>("https-client-auth", ClientAuth.class) + .category(OptionCategory.HTTP) + .description("Configures the server to require/request client authentication. Possible Values: none, request, required.") + .defaultValue(ClientAuth.none) + .expectedValues(ClientAuth.values()) + .build(); + + public static final Option HTTPS_CIPHER_SUITES = new OptionBuilder<>("https-cipher-suites", String.class) + .category(OptionCategory.HTTP) + .description("The cipher suites to use. If none is given, a reasonable default is selected.") + .build(); + + public static final Option HTTPS_PROTOCOLS = new OptionBuilder<>("https-protocols", String.class) + .category(OptionCategory.HTTP) + .description("The list of protocols to explicitly enable.") + .defaultValue("TLSv1.3") + .build(); + + public static final Option HTTPS_CERTIFICATE_FILE = new OptionBuilder<>("https-certificate-file", File.class) + .category(OptionCategory.HTTP) + .description("The file path to a server certificate or certificate chain in PEM format.") + .build(); + + public static final Option HTTPS_CERTIFICATE_KEY_FILE = new OptionBuilder<>("https-certificate-key-file", File.class) + .category(OptionCategory.HTTP) + .description("The file path to a private key in PEM format.") + .build(); + + public static final Option HTTPS_KEY_STORE_FILE = new OptionBuilder<>("https-key-store-file", File.class) + .category(OptionCategory.HTTP) + .description("The key store which holds the certificate information instead of specifying separate files.") + .build(); + + public static final Option HTTPS_KEY_STORE_PASSWORD = new OptionBuilder<>("https-key-store-password", String.class) + .category(OptionCategory.HTTP) + .description("The password of the key store file.") + .defaultValue("password") + .build(); + + public static final Option HTTPS_KEY_STORE_TYPE = new OptionBuilder<>("https-key-store-type", String.class) + .category(OptionCategory.HTTP) + .description("The type of the key store file. " + + "If not given, the type is automatically detected based on the file name.") + .build(); + + public static final Option HTTPS_TRUST_STORE_FILE = new OptionBuilder<>("https-trust-store-file", File.class) + .category(OptionCategory.HTTP) + .description("The trust store which holds the certificate information of the certificates to trust.") + .build(); + + public static final Option HTTPS_TRUST_STORE_PASSWORD = new OptionBuilder<>("https-trust-store-password", String.class) + .category(OptionCategory.HTTP) + .description("The password of the trust store file.") + .build(); + + public static final Option HTTPS_TRUST_STORE_TYPE = new OptionBuilder<>("https-trust-store-type", File.class) + .category(OptionCategory.HTTP) + .description("The type of the trust store file. " + + "If not given, the type is automatically detected based on the file name.") + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); static { - ALL_OPTIONS.add(httpPort); + ALL_OPTIONS.add(HTTP_ENABLED); + ALL_OPTIONS.add(HTTP_HOST); + ALL_OPTIONS.add(HTTP_RELATIVE_PATH); + ALL_OPTIONS.add(HTTP_PORT); + ALL_OPTIONS.add(HTTPS_PORT); + ALL_OPTIONS.add(HTTPS_CLIENT_AUTH); + ALL_OPTIONS.add(HTTPS_CIPHER_SUITES); + ALL_OPTIONS.add(HTTPS_PROTOCOLS); + ALL_OPTIONS.add(HTTPS_CERTIFICATE_FILE); + ALL_OPTIONS.add(HTTPS_CERTIFICATE_KEY_FILE); + ALL_OPTIONS.add(HTTPS_KEY_STORE_FILE); + ALL_OPTIONS.add(HTTPS_KEY_STORE_PASSWORD); + ALL_OPTIONS.add(HTTPS_KEY_STORE_TYPE); + ALL_OPTIONS.add(HTTPS_TRUST_STORE_FILE); + ALL_OPTIONS.add(HTTPS_TRUST_STORE_PASSWORD); + ALL_OPTIONS.add(HTTPS_TRUST_STORE_TYPE); } } diff --git a/config-api/src/main/java/org/keycloak/config/LoggingOptions.java b/config-api/src/main/java/org/keycloak/config/LoggingOptions.java new file mode 100644 index 000000000000..0cf17d3cbc74 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/LoggingOptions.java @@ -0,0 +1,116 @@ +package org.keycloak.config; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public class LoggingOptions { + + public static final Handler DEFAULT_LOG_HANDLER = Handler.console; + public static final Level DEFAULT_LOG_LEVEL = Level.INFO; + public static final Output DEFAULT_CONSOLE_OUTPUT = Output.DEFAULT; + public static final String DEFAULT_LOG_FILENAME = "keycloak.log"; + public static final String DEFAULT_LOG_PATH = "data" + File.separator + "log" + File.separator + DEFAULT_LOG_FILENAME; + + public enum Handler { + console, + file; + } + + public static final Option log = new OptionBuilder("log", List.class, Handler.class) + .category(OptionCategory.LOGGING) + .description("Enable one or more log handlers in a comma-separated list. Available log handlers are: " + Arrays.stream(Handler.values()).limit(2).map(h -> h.toString()).collect(Collectors.joining(","))) + .defaultValue(DEFAULT_LOG_HANDLER) + .expectedValues(Handler.values()) + .build(); + + public enum Level { + OFF, + FATAL, + ERROR, + WARN, + INFO, + DEBUG, + TRACE, + ALL; + + @Override + public String toString() { + return super.toString().toLowerCase(Locale.ROOT); + } + } + + public static final Option LOG_LEVEL = new OptionBuilder<>("log-level", Level.class) + .category(OptionCategory.LOGGING) + .defaultValue(DEFAULT_LOG_LEVEL) + .description("The log level of the root category or a comma-separated list of individual categories and their levels. For the root category, you don't need to specify a category.") + .build(); + + public enum Output { + DEFAULT, + JSON; + + @Override + public String toString() { + return super.toString().toLowerCase(Locale.ROOT); + } + } + public static final Option LOG_CONSOLE_OUTPUT = new OptionBuilder<>("log-console-output", Output.class) + .category(OptionCategory.LOGGING) + .defaultValue(DEFAULT_CONSOLE_OUTPUT) + .description("Set the log output to JSON or default (plain) unstructured logging.") + .expectedValues(Output.values()) + .build(); + + public static final Option LOG_CONSOLE_FORMAT = new OptionBuilder<>("log-console-format", String.class) + .category(OptionCategory.LOGGING) + .description("The format of unstructured console log entries. If the format has spaces in it, escape the value using \"\".") + .defaultValue("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n") + .build(); + + public static final Option LOG_CONSOLE_COLOR = new OptionBuilder<>("log-console-color", Boolean.class) + .category(OptionCategory.LOGGING) + .description("Enable or disable colors when logging to console.") + .defaultValue(Boolean.FALSE) // :-( + .build(); + + public static final Option LOG_CONSOLE_ENABLED = new OptionBuilder<>("log-console-enabled", Boolean.class) + .category(OptionCategory.LOGGING) + .runtimes(Collections.emptySet()) + .build(); + + public static final Option LOG_FILE_ENABLED = new OptionBuilder<>("log-file-enabled", Boolean.class) + .category(OptionCategory.LOGGING) + .runtimes(Collections.emptySet()) + .build(); + + public static final Option LOG_FILE = new OptionBuilder<>("log-file", File.class) + .category(OptionCategory.LOGGING) + .description("Set the log file path and filename.") + .defaultValue(new File(DEFAULT_LOG_PATH)) + .build(); + + public static final Option LOG_FILE_FORMAT = new OptionBuilder<>("log-file-format", String.class) + .category(OptionCategory.LOGGING) + .description("Set a format specific to file log entries.") + .defaultValue("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n") + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(log); + ALL_OPTIONS.add(LOG_LEVEL); + ALL_OPTIONS.add(LOG_CONSOLE_OUTPUT); + ALL_OPTIONS.add(LOG_CONSOLE_FORMAT); + ALL_OPTIONS.add(LOG_CONSOLE_COLOR); + ALL_OPTIONS.add(LOG_CONSOLE_ENABLED); + ALL_OPTIONS.add(LOG_FILE_ENABLED); + ALL_OPTIONS.add(LOG_FILE); + ALL_OPTIONS.add(LOG_FILE_FORMAT); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/MetricsOptions.java b/config-api/src/main/java/org/keycloak/config/MetricsOptions.java new file mode 100644 index 000000000000..94d1a7eae34b --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/MetricsOptions.java @@ -0,0 +1,21 @@ +package org.keycloak.config; + +import java.util.ArrayList; +import java.util.List; + +public class MetricsOptions { + + public static final Option METRICS_ENABLED = new OptionBuilder<>("metrics-enabled", Boolean.class) + .category(OptionCategory.METRICS) + .description("If the server should expose metrics. If enabled, metrics are available at the '/metrics' endpoint.") + .buildTime(true) + .defaultValue(Boolean.FALSE) + .expectedValues(Boolean.TRUE, Boolean.FALSE) + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(METRICS_ENABLED); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/MultiOption.java b/config-api/src/main/java/org/keycloak/config/MultiOption.java new file mode 100644 index 000000000000..7fcea84f46ed --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/MultiOption.java @@ -0,0 +1,19 @@ +package org.keycloak.config; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class MultiOption extends Option { + + private final Class auxiliaryType; + + public MultiOption(Class type, Class auxiliaryType, String key, OptionCategory category, Set supportedRuntimes, boolean buildTime, String description, Optional defaultValue, List expectedValues) { + super(type, key, category, supportedRuntimes, buildTime, description, defaultValue, expectedValues); + this.auxiliaryType = auxiliaryType; + } + + public Class getAuxiliaryType() { + return auxiliaryType; + } +} diff --git a/config-api/src/main/java/org/keycloak/config/Option.java b/config-api/src/main/java/org/keycloak/config/Option.java index 2c67bf7a73a8..105d4cd16491 100644 --- a/config-api/src/main/java/org/keycloak/config/Option.java +++ b/config-api/src/main/java/org/keycloak/config/Option.java @@ -19,9 +19,9 @@ public enum Runtime { private final boolean buildTime; private final String description; private final Optional defaultValue; - private final List expectedValues; + private final List expectedValues; - public Option(Class type, String key, OptionCategory category, Set supportedRuntimes, boolean buildTime, String description, Optional defaultValue, List expectedValues) { + public Option(Class type, String key, OptionCategory category, Set supportedRuntimes, boolean buildTime, String description, Optional defaultValue, List expectedValues) { this.type = type; this.key = key; this.category = category; @@ -58,8 +58,21 @@ public Optional getDefaultValue() { return defaultValue; } - public List getExpectedValues() { + public List getExpectedValues() { return expectedValues; } + public Option withRuntimeSpecificDefault(T defaultValue) { + return new Option( + this.type, + this.key, + this.category, + this.supportedRuntimes, + this.buildTime, + this.description, + Optional.ofNullable(defaultValue), + this.expectedValues + ); + } + } diff --git a/config-api/src/main/java/org/keycloak/config/OptionBuilder.java b/config-api/src/main/java/org/keycloak/config/OptionBuilder.java index 89f440c9adc2..ae84959f0155 100644 --- a/config-api/src/main/java/org/keycloak/config/OptionBuilder.java +++ b/config-api/src/main/java/org/keycloak/config/OptionBuilder.java @@ -2,31 +2,50 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; public class OptionBuilder { - private Class type; - private String key; + private final Class type; + private final Class auxiliaryType; + private final String key; private OptionCategory category; private Set supportedRuntimes; private boolean build; private String description; private Optional defaultValue; - private List expectedValues; + private List expectedValues; public OptionBuilder(String key, Class type) { this.type = type; + this.auxiliaryType = null; this.key = key; category = OptionCategory.GENERAL; supportedRuntimes = Arrays.stream(Option.Runtime.values()).collect(Collectors.toSet()); build = false; - description = ""; - defaultValue = Optional.empty(); + description = null; + defaultValue = Boolean.class.equals(type) ? Optional.of((T) Boolean.FALSE) : Optional.empty(); expectedValues = new ArrayList<>(); + if (Boolean.class.equals(type)) { + expectedStringValues(Boolean.TRUE.toString(), Boolean.FALSE.toString()); + } + } + + public OptionBuilder(String key, Class type, Class auxiliaryType) { + this.type = type; + this.auxiliaryType = auxiliaryType; + this.key = key; + category = OptionCategory.GENERAL; + supportedRuntimes = Arrays.stream(Option.Runtime.values()).collect(Collectors.toSet()); + build = false; + description = null; + defaultValue = Boolean.class.equals(type) ? Optional.of((T) Boolean.FALSE) : Optional.empty(); + expectedValues = new ArrayList<>(); + if (Boolean.class.equals(type)) { + expectedStringValues(Boolean.TRUE.toString(), Boolean.FALSE.toString()); + } } public OptionBuilder category(OptionCategory category) { @@ -66,20 +85,37 @@ public OptionBuilder defaultValue(T defaultV) { return this; } - public OptionBuilder expectedValues(List expected) { + public OptionBuilder expectedStringValues(List expected) { this.expectedValues.clear(); this.expectedValues.addAll(expected); return this; } - public OptionBuilder expectedValues(T ... expected) { + public OptionBuilder expectedStringValues(String ... expected) { this.expectedValues.clear(); this.expectedValues.addAll(Arrays.asList(expected)); return this; } + public OptionBuilder expectedValues(List expected) { + this.expectedValues.clear(); + this.expectedValues.addAll(expected.stream().map(v -> v.toString()).collect(Collectors.toList())); + return this; + } + + public OptionBuilder expectedValues(T ... expected) { + this.expectedValues.clear(); + this.expectedValues.addAll(Arrays.asList(expected).stream().map(v -> v.toString()).collect(Collectors.toList())); + return this; + } + + public Option build() { - return new Option(type, key, category, supportedRuntimes, build, description, defaultValue, expectedValues); + if (auxiliaryType != null) { + return new MultiOption(type, auxiliaryType, key, category, supportedRuntimes, build, description, defaultValue, expectedValues); + } else { + return new Option(type, key, category, supportedRuntimes, build, description, defaultValue, expectedValues); + } } } diff --git a/config-api/src/main/java/org/keycloak/config/ProxyOptions.java b/config-api/src/main/java/org/keycloak/config/ProxyOptions.java new file mode 100644 index 000000000000..974c25a9be31 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/ProxyOptions.java @@ -0,0 +1,36 @@ +package org.keycloak.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ProxyOptions { + + public enum Mode { + none, + edge, + reencrypt, + passthrough; + } + + public static final Option proxy = new OptionBuilder<>("proxy", Mode.class) + .category(OptionCategory.PROXY) + .description("The proxy address forwarding mode if the server is behind a reverse proxy. " + + "Possible values are: " + String.join(",", Arrays.stream(Mode.values()).skip(1).map(m -> m.name()).collect(Collectors.joining(",")))) + .defaultValue(Mode.none) + .expectedValues(Mode.values()) + .build(); + + public static final Option proxyForwardedHost = new OptionBuilder<>("proxy-forwarded-host", Boolean.class) + .category(OptionCategory.PROXY) + .defaultValue(Boolean.FALSE) + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(proxy); + ALL_OPTIONS.add(proxyForwardedHost); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/TransactionOptions.java b/config-api/src/main/java/org/keycloak/config/TransactionOptions.java new file mode 100644 index 000000000000..459ac1467314 --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/TransactionOptions.java @@ -0,0 +1,21 @@ +package org.keycloak.config; + +import java.util.ArrayList; +import java.util.List; + +public class TransactionOptions { + + public static final Option TRANSACTION_XA_ENABLED = new OptionBuilder<>("transaction-xa-enabled", Boolean.class) + .category(OptionCategory.TRANSACTION) + .description("Manually override the transaction type. Transaction type XA and the appropriate driver is used by default.") + .buildTime(true) + .defaultValue(Boolean.TRUE) + .expectedValues(Boolean.TRUE, Boolean.FALSE) + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(TRANSACTION_XA_ENABLED); + } +} diff --git a/config-api/src/main/java/org/keycloak/config/VaultOptions.java b/config-api/src/main/java/org/keycloak/config/VaultOptions.java new file mode 100644 index 000000000000..862eea33e7db --- /dev/null +++ b/config-api/src/main/java/org/keycloak/config/VaultOptions.java @@ -0,0 +1,56 @@ +package org.keycloak.config; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class VaultOptions { + + public enum Provider { + file, + hashicorp; + } + + public static final Option VAULT = new OptionBuilder<>("vault", Provider.class) + .category(OptionCategory.VAULT) + .description("Enables a vault provider.") + .buildTime(true) + .expectedValues(Provider.values()) + .build(); + + public static final Option VAULT_DIR = new OptionBuilder<>("vault-dir", File.class) + .category(OptionCategory.VAULT) + .description("If set, secrets can be obtained by reading the content of files within the given directory.") + .build(); + + public static final Option VAULT_UNMAPPED = new OptionBuilder<>("vault-", String.class) + .category(OptionCategory.VAULT) + .description("Maps any vault option to their corresponding properties in quarkus-vault extension.") + .runtimes() + .buildTime(true) + .build(); + + public static final Option VAULT_URL = new OptionBuilder<>("vault-url", String.class) + .category(OptionCategory.VAULT) + .description("The vault server url.") + .runtimes() + .buildTime(true) + .build(); + + public static final Option VAULT_KV_PATHS = new OptionBuilder("vault-kv-paths", Map.class, String.class) + .category(OptionCategory.VAULT) + .description("A set of one or more key/value paths that should be used when looking up secrets.") + .runtimes() + .build(); + + public static final List> ALL_OPTIONS = new ArrayList<>(); + + static { + ALL_OPTIONS.add(VAULT); + ALL_OPTIONS.add(VAULT_DIR); + ALL_OPTIONS.add(VAULT_UNMAPPED); + ALL_OPTIONS.add(VAULT_URL); + ALL_OPTIONS.add(VAULT_KV_PATHS); + } +} diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/Database.java b/config-api/src/main/java/org/keycloak/config/database/Database.java similarity index 97% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/Database.java rename to config-api/src/main/java/org/keycloak/config/database/Database.java index f4476341685f..b2fc14c97978 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/Database.java +++ b/config-api/src/main/java/org/keycloak/config/database/Database.java @@ -15,17 +15,18 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.database; - -import static java.util.Arrays.asList; +package org.keycloak.config.database; import java.io.File; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.function.Function; +import static java.util.Arrays.asList; + public final class Database { private static final Map DATABASES = new HashMap<>(); @@ -96,7 +97,7 @@ public static String[] getAliases() { return DATABASES.keySet().stream().sorted().toArray(String[]::new); } - private enum Vendor { + public enum Vendor { H2("h2", "org.h2.jdbcx.JdbcDataSource", "org.h2.Driver", @@ -187,5 +188,10 @@ public String apply(String alias) { public boolean isOfKind(String dbKind) { return databaseKind.equals(dbKind); } + + @Override + public String toString() { + return databaseKind.toLowerCase(Locale.ROOT); + } } } diff --git a/operator/app/pom.xml b/operator/app/pom.xml index 5fa823fae869..095ae4165ebd 100644 --- a/operator/app/pom.xml +++ b/operator/app/pom.xml @@ -111,7 +111,7 @@ org.keycloak - keycloak-common + keycloak-config-api diff --git a/operator/maven-plugin/src/main/java/org/keycloak/operator/maven/ServerConfigGen.java b/operator/maven-plugin/src/main/java/org/keycloak/operator/maven/ServerConfigGen.java index 3a608e5778d9..fcc996a13b85 100644 --- a/operator/maven-plugin/src/main/java/org/keycloak/operator/maven/ServerConfigGen.java +++ b/operator/maven-plugin/src/main/java/org/keycloak/operator/maven/ServerConfigGen.java @@ -43,9 +43,11 @@ public void generate(Log log, File destination) { field.addSingleMemberAnnotation( ANNOTATION_JSON_PROPERTY, new StringLiteralExpr(o.getKey())); - field.addSingleMemberAnnotation( - ANNOTATION_JSON_PROPERTY_DESCRIPTION, - new StringLiteralExpr(StringEscapeUtils.escapeJava(o.getDescription()))); + if (o.getDescription() != null) { + field.addSingleMemberAnnotation( + ANNOTATION_JSON_PROPERTY_DESCRIPTION, + new StringLiteralExpr(StringEscapeUtils.escapeJava(o.getDescription()))); + } field.createGetter(); field.createSetter(); } diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/LiquibaseProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/LiquibaseProcessor.java index 7ddb23c8649a..62538406a445 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/LiquibaseProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/LiquibaseProcessor.java @@ -81,7 +81,7 @@ void configure(KeycloakRecorder recorder, List jdbcData private void filterImplementations(Class types, String dbKind, Set classes) { if (Database.class.equals(types)) { // removes unsupported databases - classes.removeIf(classInfo -> !org.keycloak.quarkus.runtime.storage.database.Database.isLiquibaseDatabaseSupported(classInfo.name().toString(), dbKind)); + classes.removeIf(classInfo -> !org.keycloak.config.database.Database.isLiquibaseDatabaseSupported(classInfo.name().toString(), dbKind)); } } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java index 5814fa5b3c77..5ca8f115be3c 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java @@ -47,6 +47,7 @@ import java.util.stream.Collectors; import org.eclipse.microprofile.config.spi.ConfigSource; +import org.keycloak.config.MultiOption; import org.keycloak.config.OptionCategory; import org.keycloak.quarkus.runtime.cli.command.Build; import org.keycloak.quarkus.runtime.cli.command.ImportRealmMixin; @@ -420,6 +421,9 @@ private static void addMappedOptionsToArgGroups(CommandSpec cSpec, List) mapper.getOption()).getAuxiliaryType()); + } } else { optBuilder.type(String.class); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java index 33f824944f68..acd862745188 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java @@ -1,13 +1,12 @@ package org.keycloak.quarkus.runtime.configuration.mappers; -import java.util.Arrays; -import java.util.function.BiFunction; - -import org.keycloak.config.OptionCategory; +import org.keycloak.config.ClusteringOptions; import org.keycloak.quarkus.runtime.Environment; import io.smallrye.config.ConfigSourceInterceptorContext; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; + final class ClusteringPropertyMappers { private ClusteringPropertyMappers() { @@ -15,56 +14,38 @@ private ClusteringPropertyMappers() { public static PropertyMapper[] getClusteringPropertyMappers() { return new PropertyMapper[] { - builder().from("cache") - .defaultValue("ispn") - .description("Defines the cache mechanism for high-availability. " - + "By default, a 'ispn' cache is used to create a cluster between multiple server nodes. " - + "A 'local' cache disables clustering and is intended for development and testing purposes.") + fromOption(ClusteringOptions.CACHE) .paramLabel("type") - .isBuildTimeProperty(true) - .expectedValues("local", "ispn") .build(), - builder().from("cache-stack") + fromOption(ClusteringOptions.CACHE_STACK) .to("kc.spi-connections-infinispan-quarkus-stack") - .description("Define the default stack to use for cluster communication and node discovery. This option only takes effect " - + "if 'cache' is set to 'ispn'. Default: udp.") .paramLabel("stack") - .isBuildTimeProperty(true) - .expectedValues(Arrays.asList("tcp", "udp", "kubernetes", "ec2", "azure", "google")) .build(), - builder().from("cache-config-file") + fromOption(ClusteringOptions.CACHE_CONFIG_FILE) .mapFrom("cache") .to("kc.spi-connections-infinispan-quarkus-config-file") - .description("Defines the file from which cache configuration should be loaded from. " - + "The configuration file is relative to the 'conf/' directory.") - .transformer(new BiFunction() { - @Override - public String apply(String value, ConfigSourceInterceptorContext context) { - if ("local".equals(value)) { - return "cache-local.xml"; - } else if ("ispn".equals(value)) { - return "cache-ispn.xml"; - } - - String pathPrefix; - String homeDir = Environment.getHomeDir(); - - if (homeDir == null) { - pathPrefix = ""; - } else { - pathPrefix = homeDir + "/conf/"; - } - - return pathPrefix + value; - } - }) + .transformer(ClusteringPropertyMappers::resolveConfigFile) .paramLabel("file") - .isBuildTimeProperty(true) .build() }; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.CLUSTERING); + private static String resolveConfigFile(String value, ConfigSourceInterceptorContext context) { + if ("local".equals(value)) { + return "cache-local.xml"; + } else if ("ispn".equals(value)) { + return "cache-ispn.xml"; + } + + String pathPrefix; + String homeDir = Environment.getHomeDir(); + + if (homeDir == null) { + pathPrefix = ""; + } else { + pathPrefix = homeDir + "/conf/"; + } + + return pathPrefix + value; } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java index ba19b5ae6f73..e280bfca0674 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java @@ -3,14 +3,14 @@ import io.quarkus.datasource.common.runtime.DatabaseKind; import io.smallrye.config.ConfigSourceInterceptorContext; import io.smallrye.config.ConfigValue; -import org.keycloak.config.OptionCategory; -import org.keycloak.quarkus.runtime.storage.database.Database; +import org.keycloak.config.DatabaseOptions; +import org.keycloak.config.database.Database; import java.util.Optional; import java.util.function.BiFunction; -import static java.util.Arrays.asList; import static org.keycloak.quarkus.runtime.Messages.invalidDatabaseVendor; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; final class DatabasePropertyMappers { @@ -19,119 +19,99 @@ private DatabasePropertyMappers(){} public static PropertyMapper[] getDatabasePropertyMappers() { return new PropertyMapper[] { - builder().from("db-dialect") + fromOption(DatabaseOptions.DB_DIALECT) .mapFrom("db") .to("quarkus.hibernate-orm.dialect") - .isBuildTimeProperty(true) .transformer(DatabasePropertyMappers::transformDialect) - .hidden(true) .build(), - builder().from("db-driver") + fromOption(DatabaseOptions.DB_DRIVER) .mapFrom("db") - .defaultValue(Database.getDriver("dev-file", true).get()) .to("quarkus.datasource.jdbc.driver") - .transformer(DatabasePropertyMappers::getXaOrNonXaDriver) - .hidden(true) + .transformer(DatabasePropertyMappers.getXaOrNonXaDriver()) .build(), - builder().from("db"). - to("quarkus.datasource.db-kind") - .isBuildTimeProperty(true) - .transformer(toDatabaseKind()) - .description("The database vendor. Possible values are: " + String.join(", ", Database.getAliases())) + fromOption(DatabaseOptions.DB) + .to("quarkus.datasource.db-kind") + .transformer(DatabasePropertyMappers::toDatabaseKind) .paramLabel("vendor") - .expectedValues(asList(Database.getAliases())) .build(), - builder().from("db-url") + fromOption(DatabaseOptions.DB_URL) .to("quarkus.datasource.jdbc.url") .mapFrom("db") - .transformer((value, context) -> Database.getDefaultUrl(value).orElse(value)) - .description("The full database JDBC URL. If not provided, a default URL is set based on the selected database vendor. " + - "For instance, if using 'postgres', the default JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. ") + .transformer(DatabasePropertyMappers.getDatabaseUrl()) .paramLabel("jdbc-url") .build(), - builder().from("db-url-host") + fromOption(DatabaseOptions.DB_URL_HOST) .to("kc.db-url-host") - .description("Sets the hostname of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") .paramLabel("hostname") .build(), - builder().from("db-url-database") + fromOption(DatabaseOptions.DB_URL_DATABASE) .to("kc.db-url-database") - .description("Sets the database name of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") .paramLabel("dbname") .build(), - builder().from("db-url-port") + fromOption(DatabaseOptions.DB_URL_PORT) .to("kc.db-url-port") - .description("Sets the port of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") .paramLabel("port") .build(), - builder().from("db-url-properties") + fromOption(DatabaseOptions.DB_URL_PROPERTIES) .to("kc.db-url-properties") - .description("Sets the properties of the default JDBC URL of the chosen vendor. If the `db-url` option is set, this option is ignored.") .paramLabel("properties") .build(), - builder().from("db-username") + fromOption(DatabaseOptions.DB_USERNAME) .to("quarkus.datasource.username") .mapFrom("db") .transformer(DatabasePropertyMappers::resolveUsername) - .description("The username of the database user.") .paramLabel("username") .build(), - builder().from("db-password") + fromOption(DatabaseOptions.DB_PASSWORD) .to("quarkus.datasource.password") .mapFrom("db") .transformer(DatabasePropertyMappers::resolvePassword) - .description("The password of the database user.") .paramLabel("password") .isMasked(true) .build(), - builder().from("db-schema") + fromOption(DatabaseOptions.DB_SCHEMA) .to("quarkus.hibernate-orm.database.default-schema") - .description("The database schema to be used.") .paramLabel("schema") .build(), - builder().from("db-pool-initial-size") + fromOption(DatabaseOptions.DB_POOL_INITIAL_SIZE) .to("quarkus.datasource.jdbc.initial-size") - .description("The initial size of the connection pool.") .paramLabel("size") .build(), - builder().from("db-pool-min-size") + fromOption(DatabaseOptions.DB_POOL_MIN_SIZE) .to("quarkus.datasource.jdbc.min-size") - .description("The minimal size of the connection pool.") .paramLabel("size") .build(), - builder().from("db-pool-max-size") + fromOption(DatabaseOptions.DB_POOL_MAX_SIZE) .to("quarkus.datasource.jdbc.max-size") - .defaultValue(String.valueOf(100)) - .description("The maximum size of the connection pool.") .paramLabel("size") .build() }; } - private static String getXaOrNonXaDriver(String db, ConfigSourceInterceptorContext context) { - ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled"); - - boolean isXaEnabled = xaEnabledConfigValue == null || Boolean.parseBoolean(xaEnabledConfigValue.getValue()); - - return Database.getDriver(db, isXaEnabled).orElse(db); + private static BiFunction getDatabaseUrl() { + return (s, c) -> Database.getDefaultUrl(s).orElse(s); } - private static BiFunction toDatabaseKind() { - return (db, context) -> { - Optional databaseKind = Database.getDatabaseKind(db); + private static BiFunction getXaOrNonXaDriver() { + return (String db, ConfigSourceInterceptorContext context) -> { + ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled"); - if (databaseKind.isPresent()) { - return databaseKind.get(); - } + boolean isXaEnabled = xaEnabledConfigValue == null || Boolean.parseBoolean(xaEnabledConfigValue.getValue()); - addInitializationException(invalidDatabaseVendor(db, Database.getAliases())); - - return "h2"; + return Database.getDriver(db, isXaEnabled).orElse(db); }; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.DATABASE); + private static String toDatabaseKind(String db, ConfigSourceInterceptorContext context) { + Optional databaseKind = Database.getDatabaseKind(db); + + if (databaseKind.isPresent()) { + return databaseKind.get(); + } + + addInitializationException(invalidDatabaseVendor(db, Database.getAliases())); + + return "h2"; } private static String resolveUsername(String value, ConfigSourceInterceptorContext context) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java index 0b47184e9edd..e184d20da8ff 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java @@ -1,9 +1,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers; -import java.util.ArrayList; -import java.util.List; -import org.keycloak.common.Profile; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.FeatureOptions; + +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; final class FeaturePropertyMappers { @@ -12,34 +11,13 @@ private FeaturePropertyMappers() { public static PropertyMapper[] getMappers() { return new PropertyMapper[] { - builder() - .from("features") - .description("Enables a set of one or more features.") - .expectedValues(getFeatureValues()) + fromOption(FeatureOptions.FEATURES) .paramLabel("feature") .build(), - builder() - .from("features-disabled") - .expectedValues(getFeatureValues()) + fromOption(FeatureOptions.FEATURES_DISABLED) .paramLabel("feature") - .description("Disables a set of one or more features.") .build() }; } - private static List getFeatureValues() { - List features = new ArrayList<>(); - - for (Profile.Feature value : Profile.Feature.values()) { - features.add(value.name().toLowerCase().replace('_', '-')); - } - - features.add(Profile.Type.PREVIEW.name().toLowerCase()); - - return features; - } - - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.FEATURE).isBuildTimeProperty(true); - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java index 8f1ef351c712..2bc4fec8ca08 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java @@ -1,8 +1,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.HealthOptions; -import java.util.Arrays; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; final class HealthPropertyMappers { @@ -11,18 +11,11 @@ private HealthPropertyMappers(){} public static PropertyMapper[] getHealthPropertyMappers() { return new PropertyMapper[] { - builder().from("health-enabled") + fromOption(HealthOptions.HEALTH_ENABLED) .to("quarkus.datasource.health.enabled") - .isBuildTimeProperty(true) - .defaultValue(Boolean.FALSE.toString()) - .description("If the server should expose health check endpoints. If enabled, health checks are available at the '/health', '/health/ready' and '/health/live' endpoints.") .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) - .expectedValues(Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString())) .build() }; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.HEALTH); - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HostnamePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HostnamePropertyMappers.java index ee43eedab405..92fb9faffb55 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HostnamePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HostnamePropertyMappers.java @@ -1,7 +1,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers; +import org.keycloak.config.HostnameOptions; -import org.keycloak.config.OptionCategory; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; final class HostnamePropertyMappers { @@ -9,49 +10,32 @@ private HostnamePropertyMappers(){} public static PropertyMapper[] getHostnamePropertyMappers() { return new PropertyMapper[] { - builder().from("hostname") + fromOption(HostnameOptions.HOSTNAME) .to("kc.spi-hostname-default-hostname") - .description("Hostname for the Keycloak server.") .paramLabel("hostname") .build(), - builder().from("hostname-admin") + fromOption(HostnameOptions.HOSTNAME_ADMIN) .to("kc.spi-hostname-default-admin") - .description("The hostname for accessing the administration console. Use this option if you are exposing the administration console using a hostname other than the value set to the 'hostname' option.") .paramLabel("hostname") .build(), - builder().from("hostname-strict") + fromOption(HostnameOptions.HOSTNAME_STRICT) .to("kc.spi-hostname-default-strict") - .description("Disables dynamically resolving the hostname from request headers. Should always be set to true in production, unless proxy verifies the Host header.") - .type(Boolean.class) - .defaultValue(Boolean.TRUE.toString()) .build(), - builder().from("hostname-strict-https") + fromOption(HostnameOptions.HOSTNAME_STRICT_HTTPS) .to("kc.spi-hostname-default-strict-https") - .description("Forces URLs to use HTTPS. Only needed if proxy does not properly set the X-Forwarded-Proto header.") - .hidden(true) - .defaultValue(Boolean.TRUE.toString()) - .type(Boolean.class) .build(), - builder().from("hostname-strict-backchannel") + fromOption(HostnameOptions.HOSTNAME_STRICT_BACKCHANNEL) .to("kc.spi-hostname-default-strict-backchannel") - .description("By default backchannel URLs are dynamically resolved from request headers to allow internal and external applications. If all applications use the public URL this option should be enabled.") - .type(Boolean.class) .build(), - builder().from("hostname-path") + fromOption(HostnameOptions.HOSTNAME_PATH) .to("kc.spi-hostname-default-path") - .description("This should be set if proxy uses a different context-path for Keycloak.") .paramLabel("path") .build(), - builder().from("hostname-port") + fromOption(HostnameOptions.HOSTNAME_PORT) .to("kc.spi-hostname-default-hostname-port") - .defaultValue("-1") - .description("The port used by the proxy when exposing the hostname. Set this option if the proxy uses a port other than the default HTTP and HTTPS ports.") .paramLabel("port") .build() }; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.HOSTNAME); - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java index ac04228d9d81..590c532a58f7 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java @@ -3,15 +3,12 @@ import io.smallrye.config.ConfigSourceInterceptorContext; import io.smallrye.config.ConfigValue; import org.keycloak.config.HttpOptions; -import org.keycloak.config.Option; -import org.keycloak.config.OptionCategory; import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.Messages; import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider; import java.io.File; import java.nio.file.Paths; -import java.util.Arrays; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.getMapper; @@ -23,103 +20,74 @@ private HttpPropertyMappers(){} public static PropertyMapper[] getHttpPropertyMappers() { return new PropertyMapper[] { - builder().from("http-enabled") + fromOption(HttpOptions.HTTP_ENABLED) .to("quarkus.http.insecure-requests") - .defaultValue(Boolean.FALSE.toString()) .transformer(HttpPropertyMappers::getHttpEnabledTransformer) - .description("Enables the HTTP listener.") .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) - .expectedValues(Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString())) .build(), - builder().from("http-host") + fromOption(HttpOptions.HTTP_HOST) .to("quarkus.http.host") - .defaultValue("0.0.0.0") - .description("The used HTTP Host.") .paramLabel("host") .build(), - builder().from("http-relative-path") + fromOption(HttpOptions.HTTP_RELATIVE_PATH) .to("quarkus.http.root-path") - .defaultValue("/") - .description("Set the path relative to '/' for serving resources.") .paramLabel("path") - .isBuildTimeProperty(true) .build(), - fromOption(HttpOptions.httpPort) + fromOption(HttpOptions.HTTP_PORT) .to("quarkus.http.port") .paramLabel("port") .build(), - builder().from("https-port") + fromOption(HttpOptions.HTTPS_PORT) .to("quarkus.http.ssl-port") - .defaultValue(String.valueOf(8443)) - .description("The used HTTPS port.") .paramLabel("port") .build(), - builder().from("https-client-auth") + fromOption(HttpOptions.HTTPS_CLIENT_AUTH) .to("quarkus.http.ssl.client-auth") - .defaultValue("none") - .description("Configures the server to require/request client authentication. Possible Values: none, request, required.") .paramLabel("auth") - .expectedValues(Arrays.asList("none", "request", "required")) .build(), - builder().from("https-cipher-suites") + fromOption(HttpOptions.HTTPS_CIPHER_SUITES) .to("quarkus.http.ssl.cipher-suites") - .description("The cipher suites to use. If none is given, a reasonable default is selected.") .paramLabel("ciphers") .build(), - builder().from("https-protocols") + fromOption(HttpOptions.HTTPS_PROTOCOLS) .to("quarkus.http.ssl.protocols") - .description("The list of protocols to explicitly enable.") .paramLabel("protocols") - .defaultValue("TLSv1.3") .build(), - builder().from("https-certificate-file") + fromOption(HttpOptions.HTTPS_CERTIFICATE_FILE) .to("quarkus.http.ssl.certificate.file") - .description("The file path to a server certificate or certificate chain in PEM format.") .paramLabel("file") .build(), - builder().from("https-certificate-key-file") + fromOption(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE) .to("quarkus.http.ssl.certificate.key-file") - .description("The file path to a private key in PEM format.") .paramLabel("file") .build(), - builder().from("https-key-store-file") + fromOption(HttpOptions.HTTPS_KEY_STORE_FILE + .withRuntimeSpecificDefault(getDefaultKeystorePathValue())) .to("quarkus.http.ssl.certificate.key-store-file") - .defaultValue(getDefaultKeystorePathValue()) - .description("The key store which holds the certificate information instead of specifying separate files.") .paramLabel("file") .build(), - builder().from("https-key-store-password") + fromOption(HttpOptions.HTTPS_KEY_STORE_PASSWORD) .to("quarkus.http.ssl.certificate.key-store-password") - .description("The password of the key store file.") - .defaultValue("password") .paramLabel("password") .isMasked(true) .build(), - builder().from("https-key-store-type") + fromOption(HttpOptions.HTTPS_KEY_STORE_TYPE) .to("quarkus.http.ssl.certificate.key-store-file-type") - .description("The type of the key store file. " + - "If not given, the type is automatically detected based on the file name.") .paramLabel("type") .build(), - builder().from("https-trust-store-file") + fromOption(HttpOptions.HTTPS_TRUST_STORE_FILE) .to("quarkus.http.ssl.certificate.trust-store-file") - .description("The trust store which holds the certificate information of the certificates to trust.") .paramLabel("file") .build(), - builder().from("https-trust-store-password") + fromOption(HttpOptions.HTTPS_TRUST_STORE_PASSWORD) .to("quarkus.http.ssl.certificate.trust-store-password") - .description("The password of the trust store file.") .paramLabel("password") .isMasked(true) .build(), - builder().from("https-trust-store-type") + fromOption(HttpOptions.HTTPS_TRUST_STORE_TYPE) .to("quarkus.http.ssl.certificate.trust-store-file-type") - .defaultValue(getDefaultKeystorePathValue()) - .description("The type of the trust store file. " + - "If not given, the type is automatically detected based on the file name.") .paramLabel("type") .build() - }; } @@ -161,6 +129,5 @@ private static String getDefaultKeystorePathValue() { return null; } - private static PropertyMapper.Builder builder() { return PropertyMapper. builder(OptionCategory.HTTP); } } 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 5a1a653d7b10..367009fdd618 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 @@ -1,132 +1,72 @@ package org.keycloak.quarkus.runtime.configuration.mappers; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; import java.io.File; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.function.BiFunction; import java.util.logging.Level; +import java.util.stream.Collectors; import org.jboss.logmanager.LogContext; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.LoggingOptions; import org.keycloak.quarkus.runtime.Messages; import io.smallrye.config.ConfigSourceInterceptorContext; public final class LoggingPropertyMappers { - private static final String DEFAULT_LOG_LEVEL = "info"; - private static final String DEFAULT_LOG_HANDLER = "console"; - private static final String DEFAULT_LOG_FILENAME = "keycloak.log"; - public static final String DEFAULT_LOG_PATH = "data" + File.separator + "log" + File.separator + DEFAULT_LOG_FILENAME; - private static final List AVAILABLE_LOG_HANDLERS = List.of(DEFAULT_LOG_HANDLER,"file"); - private static final String DEFAULT_CONSOLE_OUTPUT = "default"; - private LoggingPropertyMappers(){} public static PropertyMapper[] getMappers() { return new PropertyMapper[] { - builder().from("log") - .defaultValue(DEFAULT_LOG_HANDLER) - .description("Enable one or more log handlers in a comma-separated list. Available log handlers are: " + String.join(",", AVAILABLE_LOG_HANDLERS)) + fromOption(LoggingOptions.log) .paramLabel("") - .expectedValues("console","file","console,file","file,console") .build(), - builder().from("log-level") - .to("quarkus.log.level") - .transformer(new BiFunction() { - @Override - public String apply(String value, ConfigSourceInterceptorContext configSourceInterceptorContext) { - String rootLevel = DEFAULT_LOG_LEVEL; - - for (String level : value.split(",")) { - String[] parts = level.split(":"); - String category = null; - String categoryLevel; - - if (parts.length == 1) { - categoryLevel = parts[0]; - } else if (parts.length == 2) { - category = parts[0]; - categoryLevel = parts[1]; - } else { - addInitializationException(Messages.invalidLogCategoryFormat(level)); - return rootLevel; - } - - Level levelType; - - try { - levelType = toLevel(categoryLevel); - } catch (IllegalArgumentException iae) { - addInitializationException(Messages.invalidLogLevel(categoryLevel)); - return rootLevel; - } - - if (category == null) { - rootLevel = levelType.getName(); - } else { - setCategoryLevel(category, levelType.getName()); - } - } - - return rootLevel; - } - }) - .defaultValue(DEFAULT_LOG_LEVEL) - .description("The log level of the root category or a comma-separated list of individual categories and their levels. For the root category, you don't need to specify a category.") - .paramLabel("category:level") - .build(), - builder().from("log-console-output") + fromOption(LoggingOptions.LOG_CONSOLE_OUTPUT) .to("quarkus.log.console.json") - .defaultValue(DEFAULT_CONSOLE_OUTPUT) - .description("Set the log output to JSON or default (plain) unstructured logging.") .paramLabel("default|json") - .expectedValues(DEFAULT_CONSOLE_OUTPUT,"json") .transformer((value, context) -> { - if(value.equals(DEFAULT_CONSOLE_OUTPUT)) { + if(value.equals(LoggingOptions.DEFAULT_CONSOLE_OUTPUT.name().toLowerCase(Locale.ROOT))) { return Boolean.FALSE.toString(); } return Boolean.TRUE.toString(); }) .build(), - builder().from("log-console-format") + fromOption(LoggingOptions.LOG_CONSOLE_FORMAT) .to("quarkus.log.console.format") - .defaultValue("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n") - .description("The format of unstructured console log entries. If the format has spaces in it, escape the value using \"\".") .paramLabel("format") .build(), - builder().from("log-console-color") + fromOption(LoggingOptions.LOG_CONSOLE_COLOR) .to("quarkus.log.console.color") - .defaultValue(Boolean.FALSE.toString()) - .description("Enable or disable colors when logging to console.") .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) .build(), - builder().from("log-console-enabled") + fromOption(LoggingOptions.LOG_CONSOLE_ENABLED) .mapFrom("log") .to("quarkus.log.console.enable") - .hidden(true) - .transformer(resolveLogHandler(DEFAULT_LOG_HANDLER)) + .transformer(LoggingPropertyMappers.resolveLogHandler(LoggingOptions.DEFAULT_LOG_HANDLER.name())) .build(), - builder().from("log-file-enabled") + fromOption(LoggingOptions.LOG_FILE_ENABLED) .mapFrom("log") .to("quarkus.log.file.enable") - .hidden(true) - .transformer(resolveLogHandler("file")) + .transformer(LoggingPropertyMappers.resolveLogHandler("file")) .build(), - builder().from("log-file") + fromOption(LoggingOptions.LOG_FILE) .to("quarkus.log.file.path") - .defaultValue(DEFAULT_LOG_PATH) - .description("Set the log file path and filename.") .paramLabel("/.log") .transformer(LoggingPropertyMappers::resolveFileLogLocation) .build(), - builder().from("log-file-format") + fromOption(LoggingOptions.LOG_FILE_FORMAT) .to("quarkus.log.file.format") - .defaultValue("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n") - .description("Set a format specific to file log entries.") .paramLabel("") + .build(), + fromOption(LoggingOptions.LOG_LEVEL) + .to("quarkus.log.level") + .transformer(LoggingPropertyMappers::resolveLogLevel) + .paramLabel("category:level") .build() }; } @@ -135,7 +75,7 @@ private static BiFunction resolv return (parentValue, context) -> { //we want to fall back to console to not have nothing shown up when wrong values are set. - String consoleDependantErrorResult = handler.equals(DEFAULT_LOG_HANDLER) ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + String consoleDependantErrorResult = handler.equals(LoggingOptions.DEFAULT_LOG_HANDLER.name()) ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); if(parentValue.isBlank()) { addInitializationException(Messages.emptyValueForKey("log")); @@ -143,9 +83,10 @@ private static BiFunction resolv } String[] logHandlerValues = parentValue.split(","); + List availableLogHandlers = Arrays.stream(LoggingOptions.Handler.values()).map(h -> h.name()).collect(Collectors.toList()); - if (!AVAILABLE_LOG_HANDLERS.containsAll(List.of(logHandlerValues))) { - addInitializationException(Messages.notRecognizedValueInList("log", parentValue, String.join(",", AVAILABLE_LOG_HANDLERS))); + if (!availableLogHandlers.containsAll(List.of(logHandlerValues))) { + addInitializationException(Messages.notRecognizedValueInList("log", parentValue, String.join(",", availableLogHandlers))); return consoleDependantErrorResult; } @@ -160,9 +101,8 @@ private static BiFunction resolv } private static String resolveFileLogLocation(String value, ConfigSourceInterceptorContext configSourceInterceptorContext) { - if (value.endsWith(File.separator)) - { - return value + DEFAULT_LOG_FILENAME; + if (value.endsWith(File.separator)) { + return value + LoggingOptions.DEFAULT_LOG_FILENAME; } return value; @@ -176,7 +116,40 @@ private static void setCategoryLevel(String category, String level) { LogContext.getLogContext().getLogger(category).setLevel(toLevel(level)); } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.LOGGING); + private static String resolveLogLevel(String value, ConfigSourceInterceptorContext configSourceInterceptorContext) { + String rootLevel = LoggingOptions.DEFAULT_LOG_LEVEL.name(); + + for (String level : value.split(",")) { + String[] parts = level.split(":"); + String category = null; + String categoryLevel; + + if (parts.length == 1) { + categoryLevel = parts[0]; + } else if (parts.length == 2) { + category = parts[0]; + categoryLevel = parts[1]; + } else { + addInitializationException(Messages.invalidLogCategoryFormat(level)); + return rootLevel; + } + + Level levelType; + + try { + levelType = toLevel(categoryLevel); + } catch (IllegalArgumentException iae) { + addInitializationException(Messages.invalidLogLevel(categoryLevel)); + return rootLevel; + } + + if (category == null) { + rootLevel = levelType.getName(); + } else { + setCategoryLevel(category, levelType.getName()); + } + } + + return rootLevel; } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/MetricsPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/MetricsPropertyMappers.java index 5c51402573d0..a9653921298d 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/MetricsPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/MetricsPropertyMappers.java @@ -1,8 +1,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.MetricsOptions; -import java.util.Arrays; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; final class MetricsPropertyMappers { @@ -11,18 +11,11 @@ private MetricsPropertyMappers(){} public static PropertyMapper[] getMetricsPropertyMappers() { return new PropertyMapper[] { - builder().from("metrics-enabled") + fromOption(MetricsOptions.METRICS_ENABLED) .to("quarkus.datasource.metrics.enabled") - .isBuildTimeProperty(true) - .defaultValue(Boolean.FALSE.toString()) - .description("If the server should expose metrics. If enabled, metrics are available at the '/metrics' endpoint.") .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) - .expectedValues(Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString())) .build() }; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.METRICS); - } } 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 cfe16de79b2d..3f3fb098e562 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 @@ -22,13 +22,8 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.toCliFormat; import static org.keycloak.quarkus.runtime.configuration.Configuration.toEnvVarFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -42,8 +37,13 @@ public class PropertyMapper { - static PropertyMapper IDENTITY = new PropertyMapper(String.class, null, null, Optional.empty(), null, null, - false,null, null, false,Collections.emptyList(),null, true) { + static PropertyMapper IDENTITY = new PropertyMapper( + new OptionBuilder(null, String.class).build(), + null, + null, + null, + null, + false) { @Override public ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) { return context.proceed(name); @@ -55,43 +55,20 @@ public ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext co private final BiFunction mapper; private final String mapFrom; private final boolean mask; - private final List expectedValues; private final String paramLabel; private final String envVarFormat; private String cliFormat; - // Backward compatible constructor - PropertyMapper(Class type, String from, String to, Optional defaultValue, BiFunction mapper, - String mapFrom, boolean buildTime, String description, String paramLabel, boolean mask, List expectedValues, - OptionCategory category, boolean hidden) { - Set runtimes = new HashSet<>(); - if (!hidden) { - runtimes.add(Option.Runtime.QUARKUS); - } - this.option = new OptionBuilder(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + from, type) - .buildTime(buildTime) - .category(category != null ? category : OptionCategory.GENERAL) - .defaultValue(defaultValue) - .description(description) - .expectedValues(expectedValues) - .runtimes(runtimes) - .build(); - this.to = to == null ? option.getKey() : to; + PropertyMapper(Option option, String to, BiFunction mapper, + String mapFrom, String paramLabel, boolean mask) { + this.option = option; + this.to = to == null ? getFrom() : to; this.mapper = mapper == null ? PropertyMapper::defaultTransformer : mapper; this.mapFrom = mapFrom; this.paramLabel = paramLabel; this.mask = mask; - this.expectedValues = expectedValues == null ? Collections.emptyList() : expectedValues; - this.cliFormat = toCliFormat(from); - this.envVarFormat = toEnvVarFormat(option.getKey()); - } - - public static PropertyMapper.Builder builder(String fromProp, String toProp) { - return new PropertyMapper.Builder(fromProp, toProp); - } - - public static PropertyMapper.Builder builder(OptionCategory category) { - return new PropertyMapper.Builder(category); + this.cliFormat = toCliFormat(option.getKey()); + this.envVarFormat = toEnvVarFormat(getFrom()); } private static String defaultTransformer(String value, ConfigSourceInterceptorContext context) { @@ -103,7 +80,7 @@ ConfigValue getConfigValue(ConfigSourceInterceptorContext context) { } ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) { - String from = this.option.getKey(); + String from = getFrom(); if (to != null && to.endsWith(OPTION_PART_SEPARATOR)) { // in case mapping is based on prefixes instead of full property names @@ -166,16 +143,18 @@ ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) return value; } + public Option getOption() { return this.option; } + public Class getType() { return this.option.getType(); } public String getFrom() { - return this.option.getKey(); + return MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + this.option.getKey(); } public String getDescription() { return this.option.getDescription(); } - public List getExpectedValues() { - return expectedValues; + public List getExpectedValues() { + return this.option.getExpectedValues().stream().map(v -> v.toString()).collect(Collectors.toList()); } public Optional getDefaultValue() {return this.option.getDefaultValue(); } @@ -236,32 +215,15 @@ private ConfigValue transformValue(String value, ConfigSourceInterceptorContext public static class Builder { - private Class type; - private String from; + private final Option option; private String to; - private T defaultValue; private BiFunction mapper; - private String description; private String mapFrom = null; - private List expectedValues = new ArrayList<>(); - private boolean isBuildTimeProperty = false; private boolean isMasked = false; - private OptionCategory category = OptionCategory.GENERAL; private String paramLabel; - private boolean hidden; - - public Builder(OptionCategory category) { - this.category = category; - } - - public Builder(String fromProp, String toProp) { - this.from = fromProp; - this.to = toProp; - } - public Builder from(String from) { - this.from = from; - return this; + public Builder(Option option) { + this.option = option; } public Builder to(String to) { @@ -269,22 +231,11 @@ public Builder to(String to) { return this; } - - public Builder defaultValue(T defaultValue) { - this.defaultValue = defaultValue; - return this; - } - public Builder transformer(BiFunction mapper) { this.mapper = mapper; return this; } - public Builder description(String description) { - this.description = description; - return this; - } - public Builder paramLabel(String label) { this.paramLabel = label; return this; @@ -295,63 +246,21 @@ public Builder mapFrom(String mapFrom) { return this; } - public Builder expectedValues(List expectedValues) { - this.expectedValues = new ArrayList<>(expectedValues); - return this; - } - - public Builder expectedValues(T... expectedValues) { - this.expectedValues = new ArrayList<>(Arrays.asList(expectedValues)); - return this; - } - - public Builder isBuildTimeProperty(boolean isBuildTime) { - this.isBuildTimeProperty = isBuildTime; - return this; - } - public Builder isMasked(boolean isMasked) { this.isMasked = isMasked; return this; } - public Builder category(OptionCategory category) { - this.category = category; - return this; - } - - public Builder type(Class type) { - if (Boolean.class.equals(type)) { - expectedValues((T) Boolean.TRUE.toString(), (T) Boolean.FALSE.toString()); - paramLabel(defaultValue == null ? "true|false" : defaultValue.toString()); - defaultValue(defaultValue == null ? (T) Boolean.FALSE : defaultValue); - } - this.type = type; - return this; - } - - public Builder hidden(boolean hidden) { - this.hidden = hidden; - return this; - } - public PropertyMapper build() { - return new PropertyMapper(type, from, to, Optional.ofNullable(defaultValue), mapper, mapFrom, isBuildTimeProperty, description, paramLabel, - isMasked, expectedValues, category, hidden); + if (paramLabel == null && Boolean.class.equals(option.getType())) { + paramLabel = Boolean.TRUE + "|" + Boolean.FALSE; + } + return new PropertyMapper(option, to, mapper, mapFrom, paramLabel, isMasked); } } public static PropertyMapper.Builder fromOption(Option opt) { - Builder builder = PropertyMapper.builder(opt.getCategory()) - .type(opt.getType()) - .from(opt.getKey()) - .hidden(!opt.getSupportedRuntimes().contains(Option.Runtime.QUARKUS)) - .description(opt.getDescription()) - .isBuildTimeProperty(opt.isBuildTime()) - .expectedValues(opt.getExpectedValues()); - if (opt.getDefaultValue().isPresent()) { - builder.defaultValue(opt.getDefaultValue().get()); - } - return builder; + return new PropertyMapper.Builder<>(opt); } + } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java index 127f98d73ada..dfdaa6699538 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java @@ -1,12 +1,9 @@ package org.keycloak.quarkus.runtime.configuration.mappers; -import io.quarkus.runtime.QuarkusApplication; -import io.quarkus.runtime.configuration.ConfigurationRuntimeConfig; import io.smallrye.config.ConfigSourceInterceptorContext; import io.smallrye.config.ConfigValue; import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource; -import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider; import java.util.Collection; import java.util.HashMap; @@ -173,4 +170,5 @@ public PropertyMapper put(String key, PropertyMapper value) { return super.put(key, value); } } + } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java index ec6ea4f36b29..fc9aad98857d 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java @@ -2,62 +2,49 @@ import io.smallrye.config.ConfigSourceInterceptorContext; -import java.util.Arrays; import java.util.function.BiFunction; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.ProxyOptions; import org.keycloak.quarkus.runtime.Messages; final class ProxyPropertyMappers { - private static final String[] possibleProxyValues = {"edge", "reencrypt", "passthrough"}; - private ProxyPropertyMappers(){} public static PropertyMapper[] getProxyPropertyMappers() { return new PropertyMapper[] { - builder().from("proxy") + fromOption(ProxyOptions.proxy) .to("quarkus.http.proxy.proxy-address-forwarding") - .defaultValue("none") - .transformer(getValidProxyModeValue()) - .expectedValues(Arrays.asList(possibleProxyValues)) - .description("The proxy address forwarding mode if the server is behind a reverse proxy. " + - "Possible values are: " + String.join(",",possibleProxyValues)) + .transformer(ProxyPropertyMappers::getValidProxyModeValue) .paramLabel("mode") - .category(OptionCategory.PROXY) .build(), - builder().to("quarkus.http.proxy.enable-forwarded-host") + fromOption(ProxyOptions.proxyForwardedHost) + .to("quarkus.http.proxy.enable-forwarded-host") .mapFrom("proxy") - .defaultValue("false") - .transformer(ProxyPropertyMappers::resolveEnableForwardedHost) - .category(OptionCategory.PROXY) + .transformer(ProxyPropertyMappers::getResolveEnableForwardedHost) .build() }; } - private static BiFunction getValidProxyModeValue() { - return (mode, context) -> { - switch (mode) { - case "none": - return "false"; - case "edge": - case "reencrypt": - case "passthrough": - return "true"; - default: - addInitializationException(Messages.invalidProxyMode(mode)); - return "false"; - } - }; + private static String getValidProxyModeValue(String mode, ConfigSourceInterceptorContext context) { + switch (mode) { + case "none": + return "false"; + case "edge": + case "reencrypt": + case "passthrough": + return "true"; + default: + addInitializationException(Messages.invalidProxyMode(mode)); + return "false"; + } } - private static String resolveEnableForwardedHost(String proxy, ConfigSourceInterceptorContext context) { + private static String getResolveEnableForwardedHost(String proxy, ConfigSourceInterceptorContext context) { return String.valueOf(!"none".equals(proxy)); } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.PROXY); - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java index c85a2cc04a67..1ce5def33880 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java @@ -1,9 +1,9 @@ package org.keycloak.quarkus.runtime.configuration.mappers; import io.smallrye.config.ConfigSourceInterceptorContext; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.TransactionOptions; -import java.util.Arrays; +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; public class TransactionPropertyMappers { @@ -11,15 +11,11 @@ private TransactionPropertyMappers(){} public static PropertyMapper[] getTransactionPropertyMappers() { return new PropertyMapper[] { - builder().from("transaction-xa-enabled") + fromOption(TransactionOptions.TRANSACTION_XA_ENABLED) .to("quarkus.datasource.jdbc.transactions") - .defaultValue(Boolean.TRUE.toString()) - .description("Manually override the transaction type. Transaction type XA and the appropriate driver is used by default.") .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) - .expectedValues(Arrays.asList(Boolean.TRUE.toString(), Boolean.FALSE.toString())) - .isBuildTimeProperty(true) .transformer(TransactionPropertyMappers::getQuarkusTransactionsValue) - .build(), + .build() }; } @@ -33,8 +29,4 @@ private static String getQuarkusTransactionsValue(String txValue, ConfigSourceIn return "enabled"; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.TRANSACTION); - } - } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/VaultPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/VaultPropertyMappers.java index bf16eeaff4cf..9e4af24bd772 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/VaultPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/VaultPropertyMappers.java @@ -1,6 +1,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers; -import org.keycloak.config.OptionCategory; +import org.keycloak.config.VaultOptions; + +import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; final class VaultPropertyMappers { @@ -9,45 +11,25 @@ private VaultPropertyMappers() { public static PropertyMapper[] getVaultPropertyMappers() { return new PropertyMapper[] { - builder() - .from("vault") - .description("Enables a vault provider.") - .expectedValues("file", "hashicorp") + fromOption(VaultOptions.VAULT) .paramLabel("provider") - .isBuildTimeProperty(true) .build(), - builder() - .from("vault-dir") + fromOption(VaultOptions.VAULT_DIR) .to("kc.spi-vault-file-dir") - .description("If set, secrets can be obtained by reading the content of files within the given directory.") .paramLabel("dir") .build(), - builder() - .from("vault-") + fromOption(VaultOptions.VAULT_UNMAPPED) .to("quarkus.vault.") - .description("Maps any vault option to their corresponding properties in quarkus-vault extension.") - .hidden(true) - .isBuildTimeProperty(true) .build(), - builder() - .from("vault-url") + fromOption(VaultOptions.VAULT_URL) .to("quarkus.vault.url") - .description("The vault server url.") .paramLabel("paths") - .hidden(true) - .isBuildTimeProperty(true) .build(), - builder() - .from("vault-kv-paths") + fromOption(VaultOptions.VAULT_KV_PATHS) .to("kc.spi-vault-hashicorp-paths") - .description("A set of one or more key/value paths that should be used when looking up secrets.") .paramLabel("paths") - .hidden(true) .build() }; } - private static PropertyMapper.Builder builder() { - return PropertyMapper.builder(OptionCategory.VAULT); - } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java index 9b25963e3fed..8d1700ed9d81 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; +import org.keycloak.config.LoggingOptions; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; @@ -142,7 +143,7 @@ void testWinLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) { @EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.") @Launch({ "start-dev", "--log=console,file"}) void testKeycloakLogFileCreated(RawDistRootPath path) { - Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH); + Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingOptions.DEFAULT_LOG_PATH); File logFile = new File(logFilePath.toString()); assertTrue(logFile.isFile(), "Log file does not exist!"); } @@ -151,7 +152,7 @@ void testKeycloakLogFileCreated(RawDistRootPath path) { @EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell escaping behaviour on Windows.") @Launch({ "start-dev", "--log=\"console,file\""}) void testWinKeycloakLogFileCreated(RawDistRootPath path) { - Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH); + Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingOptions.DEFAULT_LOG_PATH); File logFile = new File(logFilePath.toString()); assertTrue(logFile.isFile(), "Log file does not exist!"); } @@ -160,7 +161,7 @@ void testWinKeycloakLogFileCreated(RawDistRootPath path) { @EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.") @Launch({ "start-dev", "--log=console,file", "--log-file-format=\"%d{HH:mm:ss} %-5p [%c{1.}] (%t) %s%e%n\""}) void testFileLoggingHasDifferentFormat(RawDistRootPath path) throws IOException { - Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH); + Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingOptions.DEFAULT_LOG_PATH); File logFile = new File(logFilePath.toString()); String data = FileUtils.readFileToString(logFile, Charset.defaultCharset());