Move all dist options to the new module
Co-authored-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
71e7982a49
commit
3abcc699a1
35 changed files with 985 additions and 534 deletions
|
@ -38,4 +38,11 @@
|
|||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -5,9 +5,19 @@ import java.util.List;
|
|||
|
||||
public class AllOptions {
|
||||
|
||||
public final static List<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
public static final List<Option<?>> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<File> 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<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(CACHE);
|
||||
ALL_OPTIONS.add(CACHE_STACK);
|
||||
ALL_OPTIONS.add(CACHE_CONFIG_FILE);
|
||||
}
|
||||
}
|
|
@ -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<String> DB_DIALECT = new OptionBuilder<>("db-dialect", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.runtimes(Option.Runtime.OPERATOR)
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> 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<Database.Vendor> 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<String> 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<String> 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<String> 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<Integer> 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<String> 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<String> DB_USERNAME = new OptionBuilder<>("db-username", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.description("The username of the database user.")
|
||||
.build();
|
||||
|
||||
public static final Option<String> DB_PASSWORD = new OptionBuilder<>("db-password", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.description("The password of the database user.")
|
||||
.build();
|
||||
|
||||
public static final Option<String> DB_SCHEMA = new OptionBuilder<>("db-schema", String.class)
|
||||
.category(OptionCategory.DATABASE)
|
||||
.description("The database schema to be used.")
|
||||
.build();
|
||||
|
||||
public static final Option<Integer> 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<Integer> 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<Integer> 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<Option<?>> 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);
|
||||
}
|
||||
}
|
|
@ -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<String> getFeatureValues() {
|
||||
List<String> 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<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(FEATURES);
|
||||
ALL_OPTIONS.add(FEATURES_DISABLED);
|
||||
}
|
||||
}
|
|
@ -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<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(HEALTH_ENABLED);
|
||||
}
|
||||
}
|
|
@ -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<Option<?>> 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);
|
||||
}
|
||||
}
|
|
@ -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<Integer>("http-port", Integer.class)
|
||||
.description("The used HTTP port.")
|
||||
public static final Option<Boolean> 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<Option<?>> 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<Option<?>> 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);
|
||||
}
|
||||
}
|
||||
|
|
116
config-api/src/main/java/org/keycloak/config/LoggingOptions.java
Normal file
116
config-api/src/main/java/org/keycloak/config/LoggingOptions.java
Normal file
|
@ -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<Level> 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 \"<format>\".")
|
||||
.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<Boolean> 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<File> 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<Option<?>> 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);
|
||||
}
|
||||
}
|
|
@ -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<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(METRICS_ENABLED);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.keycloak.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class MultiOption<T> extends Option<T> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -19,9 +19,9 @@ public class Option<T> {
|
|||
private final boolean buildTime;
|
||||
private final String description;
|
||||
private final Optional<T> defaultValue;
|
||||
private final List<T> expectedValues;
|
||||
private final List<String> expectedValues;
|
||||
|
||||
public Option(Class<T> type, String key, OptionCategory category, Set<Runtime> supportedRuntimes, boolean buildTime, String description, Optional<T> defaultValue, List<T> expectedValues) {
|
||||
public Option(Class<T> type, String key, OptionCategory category, Set<Runtime> supportedRuntimes, boolean buildTime, String description, Optional<T> defaultValue, List<String> expectedValues) {
|
||||
this.type = type;
|
||||
this.key = key;
|
||||
this.category = category;
|
||||
|
@ -58,8 +58,21 @@ public class Option<T> {
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
public List<T> getExpectedValues() {
|
||||
public List<String> getExpectedValues() {
|
||||
return expectedValues;
|
||||
}
|
||||
|
||||
public Option<T> withRuntimeSpecificDefault(T defaultValue) {
|
||||
return new Option<T>(
|
||||
this.type,
|
||||
this.key,
|
||||
this.category,
|
||||
this.supportedRuntimes,
|
||||
this.buildTime,
|
||||
this.description,
|
||||
Optional.ofNullable(defaultValue),
|
||||
this.expectedValues
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,31 +2,50 @@ package org.keycloak.config;
|
|||
|
||||
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<T> {
|
||||
private Class<T> type;
|
||||
private String key;
|
||||
private final Class<T> type;
|
||||
private final Class<T> auxiliaryType;
|
||||
private final String key;
|
||||
private OptionCategory category;
|
||||
private Set<Option.Runtime> supportedRuntimes;
|
||||
private boolean build;
|
||||
private String description;
|
||||
private Optional<T> defaultValue;
|
||||
private List<T> expectedValues;
|
||||
private List<String> expectedValues;
|
||||
|
||||
public OptionBuilder(String key, Class<T> 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<T> type, Class<T> 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<T> category(OptionCategory category) {
|
||||
|
@ -66,20 +85,37 @@ public class OptionBuilder<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public OptionBuilder<T> expectedValues(List<T> expected) {
|
||||
public OptionBuilder<T> expectedStringValues(List<String> expected) {
|
||||
this.expectedValues.clear();
|
||||
this.expectedValues.addAll(expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionBuilder<T> expectedValues(T ... expected) {
|
||||
public OptionBuilder<T> expectedStringValues(String ... expected) {
|
||||
this.expectedValues.clear();
|
||||
this.expectedValues.addAll(Arrays.asList(expected));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionBuilder<T> expectedValues(List<T> expected) {
|
||||
this.expectedValues.clear();
|
||||
this.expectedValues.addAll(expected.stream().map(v -> v.toString()).collect(Collectors.toList()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionBuilder<T> expectedValues(T ... expected) {
|
||||
this.expectedValues.clear();
|
||||
this.expectedValues.addAll(Arrays.asList(expected).stream().map(v -> v.toString()).collect(Collectors.toList()));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Option<T> build() {
|
||||
return new Option<T>(type, key, category, supportedRuntimes, build, description, defaultValue, expectedValues);
|
||||
if (auxiliaryType != null) {
|
||||
return new MultiOption<T>(type, auxiliaryType, key, category, supportedRuntimes, build, description, defaultValue, expectedValues);
|
||||
} else {
|
||||
return new Option<T>(type, key, category, supportedRuntimes, build, description, defaultValue, expectedValues);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Mode> 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<Boolean> proxyForwardedHost = new OptionBuilder<>("proxy-forwarded-host", Boolean.class)
|
||||
.category(OptionCategory.PROXY)
|
||||
.defaultValue(Boolean.FALSE)
|
||||
.build();
|
||||
|
||||
public static final List<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(proxy);
|
||||
ALL_OPTIONS.add(proxyForwardedHost);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.keycloak.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TransactionOptions {
|
||||
|
||||
public static final Option<Boolean> 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<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(TRANSACTION_XA_ENABLED);
|
||||
}
|
||||
}
|
|
@ -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<Option<?>> 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);
|
||||
}
|
||||
}
|
|
@ -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<String, Vendor> DATABASES = new HashMap<>();
|
||||
|
@ -96,7 +97,7 @@ public final class Database {
|
|||
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 final class Database {
|
|||
public boolean isOfKind(String dbKind) {
|
||||
return databaseKind.equals(dbKind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return databaseKind.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -111,7 +111,7 @@
|
|||
<!-- Keycloak -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-common</artifactId>
|
||||
<artifactId>keycloak-config-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
|
|
|
@ -43,9 +43,11 @@ public class ServerConfigGen {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ class LiquibaseProcessor {
|
|||
private void filterImplementations(Class<?> types, String dbKind, Set<ClassInfo> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import java.util.regex.Pattern;
|
|||
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 @@ public final class Picocli {
|
|||
|
||||
if (mapper.getType() != null) {
|
||||
optBuilder.type(mapper.getType());
|
||||
if (mapper.getOption() instanceof MultiOption) {
|
||||
optBuilder.auxiliaryTypes(((MultiOption<?>) mapper.getOption()).getAuxiliaryType());
|
||||
}
|
||||
} else {
|
||||
optBuilder.type(String.class);
|
||||
}
|
||||
|
|
|
@ -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 @@ final class 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<String, ConfigSourceInterceptorContext, String>() {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@ package org.keycloak.quarkus.runtime.configuration.mappers;
|
|||
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 @@ final class 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<String, ConfigSourceInterceptorContext, String> getDatabaseUrl() {
|
||||
return (s, c) -> Database.getDefaultUrl(s).orElse(s);
|
||||
}
|
||||
|
||||
private static BiFunction<String, ConfigSourceInterceptorContext, String> toDatabaseKind() {
|
||||
return (db, context) -> {
|
||||
Optional<String> databaseKind = Database.getDatabaseKind(db);
|
||||
private static BiFunction<String, ConfigSourceInterceptorContext, String> 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 <T> PropertyMapper.Builder<T> builder() {
|
||||
return PropertyMapper.builder(OptionCategory.DATABASE);
|
||||
private static String toDatabaseKind(String db, ConfigSourceInterceptorContext context) {
|
||||
Optional<String> 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) {
|
||||
|
|
|
@ -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 @@ final class 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<String> getFeatureValues() {
|
||||
List<String> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ final class 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ final class 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,12 @@ package org.keycloak.quarkus.runtime.configuration.mappers;
|
|||
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 @@ final class 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 @@ final class HttpPropertyMappers {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static <T> PropertyMapper.Builder<T> builder() { return PropertyMapper.<T> builder(OptionCategory.HTTP); }
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> 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("<handler>")
|
||||
.expectedValues("console","file","console,file","file,console")
|
||||
.build(),
|
||||
builder().from("log-level")
|
||||
.to("quarkus.log.level")
|
||||
.transformer(new BiFunction<String, ConfigSourceInterceptorContext, String>() {
|
||||
@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 \"<format>\".")
|
||||
.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("<path>/<file-name>.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("<format>")
|
||||
.build(),
|
||||
fromOption(LoggingOptions.LOG_LEVEL)
|
||||
.to("quarkus.log.level")
|
||||
.transformer(LoggingPropertyMappers::resolveLogLevel)
|
||||
.paramLabel("category:level")
|
||||
.build()
|
||||
};
|
||||
}
|
||||
|
@ -135,7 +75,7 @@ public final class LoggingPropertyMappers {
|
|||
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 @@ public final class LoggingPropertyMappers {
|
|||
}
|
||||
|
||||
String[] logHandlerValues = parentValue.split(",");
|
||||
List<String> 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 @@ public final class LoggingPropertyMappers {
|
|||
}
|
||||
|
||||
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 @@ public final class LoggingPropertyMappers {
|
|||
LogContext.getLogContext().getLogger(category).setLevel(toLevel(level));
|
||||
}
|
||||
|
||||
private static <T> PropertyMapper.Builder<T> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ final class 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,8 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PA
|
|||
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 @@ import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
|||
|
||||
public class PropertyMapper<T> {
|
||||
|
||||
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<String>(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 class PropertyMapper<T> {
|
|||
private final BiFunction<String, ConfigSourceInterceptorContext, String> mapper;
|
||||
private final String mapFrom;
|
||||
private final boolean mask;
|
||||
private final List<T> expectedValues;
|
||||
private final String paramLabel;
|
||||
private final String envVarFormat;
|
||||
private String cliFormat;
|
||||
|
||||
// Backward compatible constructor
|
||||
PropertyMapper(Class<T> type, String from, String to, Optional<T> defaultValue, BiFunction<String, ConfigSourceInterceptorContext, String> mapper,
|
||||
String mapFrom, boolean buildTime, String description, String paramLabel, boolean mask, List<T> expectedValues,
|
||||
OptionCategory category, boolean hidden) {
|
||||
Set<Option.Runtime> runtimes = new HashSet<>();
|
||||
if (!hidden) {
|
||||
runtimes.add(Option.Runtime.QUARKUS);
|
||||
}
|
||||
this.option = new OptionBuilder<T>(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<T> option, String to, BiFunction<String, ConfigSourceInterceptorContext, String> 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 @@ public class PropertyMapper<T> {
|
|||
}
|
||||
|
||||
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 @@ public class PropertyMapper<T> {
|
|||
return value;
|
||||
}
|
||||
|
||||
public Option<T> getOption() { return this.option; }
|
||||
|
||||
public Class<T> 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<T> getExpectedValues() {
|
||||
return expectedValues;
|
||||
public List<String> getExpectedValues() {
|
||||
return this.option.getExpectedValues().stream().map(v -> v.toString()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Optional<T> getDefaultValue() {return this.option.getDefaultValue(); }
|
||||
|
@ -236,32 +215,15 @@ public class PropertyMapper<T> {
|
|||
|
||||
public static class Builder<T> {
|
||||
|
||||
private Class<T> type;
|
||||
private String from;
|
||||
private final Option<T> option;
|
||||
private String to;
|
||||
private T defaultValue;
|
||||
private BiFunction<String, ConfigSourceInterceptorContext, String> mapper;
|
||||
private String description;
|
||||
private String mapFrom = null;
|
||||
private List<T> 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<T> from(String from) {
|
||||
this.from = from;
|
||||
return this;
|
||||
public Builder(Option<T> option) {
|
||||
this.option = option;
|
||||
}
|
||||
|
||||
public Builder<T> to(String to) {
|
||||
|
@ -269,22 +231,11 @@ public class PropertyMapper<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Builder<T> defaultValue(T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> transformer(BiFunction<String, ConfigSourceInterceptorContext, String> mapper) {
|
||||
this.mapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> paramLabel(String label) {
|
||||
this.paramLabel = label;
|
||||
return this;
|
||||
|
@ -295,63 +246,21 @@ public class PropertyMapper<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> expectedValues(List<T> expectedValues) {
|
||||
this.expectedValues = new ArrayList<>(expectedValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder expectedValues(T... expectedValues) {
|
||||
this.expectedValues = new ArrayList<>(Arrays.asList(expectedValues));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> isBuildTimeProperty(boolean isBuildTime) {
|
||||
this.isBuildTimeProperty = isBuildTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> isMasked(boolean isMasked) {
|
||||
this.isMasked = isMasked;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> category(OptionCategory category) {
|
||||
this.category = category;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> type(Class<T> 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<T> hidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyMapper<T> build() {
|
||||
return new PropertyMapper<T>(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<T>(option, to, mapper, mapFrom, paramLabel, isMasked);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> PropertyMapper.Builder<T> fromOption(Option<T> opt) {
|
||||
Builder<T> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 final class PropertyMappers {
|
|||
return super.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,62 +2,49 @@ package org.keycloak.quarkus.runtime.configuration.mappers;
|
|||
|
||||
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<String, ConfigSourceInterceptorContext, String> 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 <T> PropertyMapper.Builder<T> builder() {
|
||||
return PropertyMapper.builder(OptionCategory.PROXY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ public class 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 @@ public class TransactionPropertyMappers {
|
|||
return "enabled";
|
||||
}
|
||||
|
||||
private static <T> PropertyMapper.Builder<T> builder() {
|
||||
return PropertyMapper.builder(OptionCategory.TRANSACTION);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 @@ final class 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|||
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 @@ public class LoggingDistTest {
|
|||
@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 @@ public class LoggingDistTest {
|
|||
@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 @@ public class LoggingDistTest {
|
|||
@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());
|
||||
|
|
Loading…
Reference in a new issue