fix: moving multi-option validation to propertymappergrouping interface (#42125)

* fix: moving multi-option validation to propertymappergrouping interface

closes: #27025

Signed-off-by: Steve Hawkins <shawkins@redhat.com>

* refinements based upon review comments

Signed-off-by: Steve Hawkins <shawkins@redhat.com>

---------

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins
2025-09-09 04:32:15 -04:00
committed by GitHub
parent d13c953fe4
commit fcedd14e40
49 changed files with 292 additions and 259 deletions

View File

@@ -20,7 +20,7 @@ package org.keycloak.quarkus.runtime;
import static org.keycloak.quarkus.runtime.Environment.getKeycloakModeFromProfile;
import static org.keycloak.quarkus.runtime.Environment.isNonServerMode;
import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import java.util.ArrayList;
import java.util.List;

View File

@@ -22,7 +22,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
import org.keycloak.quarkus.runtime.cli.command.Build;
import picocli.CommandLine;
@@ -49,7 +49,7 @@ public final class Messages {
}
public static String optimizedUsedForFirstStartup() {
return String.format("The '%s' flag was used for first ever server start. Please don't use this flag for the first startup or use '%s %s' to build the server first.", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, Environment.getCommand(), Build.NAME);
return String.format("The '%s' flag was used for first ever server start. Please don't use this flag for the first startup or use '%s %s' to build the server first.", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, Environment.getCommand(), Build.NAME);
}
public static String invalidLogLevel(String logLevel) {

View File

@@ -22,7 +22,7 @@ import static org.keycloak.quarkus.runtime.Environment.getProviderFiles;
import static org.keycloak.quarkus.runtime.Environment.isRebuild;
import static org.keycloak.quarkus.runtime.Environment.isRebuildCheck;
import static org.keycloak.quarkus.runtime.cli.OptionRenderer.decorateDuplicitOptionName;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.configuration.Configuration.isUserModifiable;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
@@ -54,7 +54,7 @@ import org.keycloak.quarkus.runtime.KeycloakMain;
import org.keycloak.quarkus.runtime.Messages;
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractNonServerCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
import org.keycloak.quarkus.runtime.cli.command.Main;
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
import org.keycloak.quarkus.runtime.configuration.Configuration;
@@ -256,7 +256,6 @@ public class Picocli {
final boolean disabledMappersInterceptorEnabled = DisabledMappersInterceptor.isEnabled(); // return to the state before the disable
try {
PropertyMappingInterceptor.disable(); // we don't want the mapped / transformed properties, we want what the user effectively supplied
DisabledMappersInterceptor.disable(); // we want all properties, even disabled ones
final List<String> ignoredRunTime = new ArrayList<>();
@@ -295,7 +294,7 @@ public class Picocli {
}
String from = mapper.forKey(name).getFrom();
if (!name.equals(from)) {
ConfigValue value = Configuration.getConfigValue(name);
ConfigValue value = getUnmappedValue(name);
if (value.getValue() != null && isUserModifiable(value)) {
secondClassOptions.put(name, from);
}
@@ -313,8 +312,8 @@ public class Picocli {
});
// second pass validate any property mapper not seen in the first pass
// - this will catch required values, anything missing from the property names, or disabled
List<PropertyMapper<?>> mappers = new ArrayList<>(disabledMappers);
// - this will catch required values, anything missing from the property names
List<PropertyMapper<?>> mappers = new ArrayList<>();
for (OptionCategory category : categories) {
Optional.ofNullable(PropertyMappers.getRuntimeMappers().get(category)).ifPresent(mappers::addAll);
Optional.ofNullable(PropertyMappers.getBuildTimeMappers().get(category)).ifPresent(mappers::addAll);
@@ -327,6 +326,16 @@ public class Picocli {
}
}
PropertyMappers.getPropertyMapperGroupings().forEach(g -> g.validateConfig(this));
// third pass check for disabled mappers
for (PropertyMapper<?> mapper : disabledMappers) {
if (!mapper.hasWildcard()) {
validateProperty(abstractCommand, options, ignoredRunTime, disabledBuildTime, disabledRunTime,
deprecatedInUse, missingOption, disabledMappers, mapper, mapper.getFrom());
}
}
if (!missingOption.isEmpty()) {
throw new PropertyException("The following options are required: \n%s".formatted(String.join("\n", missingOption)));
}
@@ -355,7 +364,6 @@ public class Picocli {
});
} finally {
DisabledMappersInterceptor.enable(disabledMappersInterceptorEnabled);
PropertyMappingInterceptor.enable();
}
}
@@ -366,11 +374,20 @@ public class Picocli {
return ((longNewValue / 1000) * 1000) != longNewValue || ((longOldValue / 1000) * 1000) != longNewValue;
}
private ConfigValue getUnmappedValue(String key) {
PropertyMappingInterceptor.disable();
try {
return Configuration.getConfigValue(key);
} finally {
PropertyMappingInterceptor.enable();
}
}
private void validateProperty(AbstractCommand abstractCommand, IncludeOptions options,
final List<String> ignoredRunTime, final Set<String> disabledBuildTime, final Set<String> disabledRunTime,
final Set<String> deprecatedInUse, final Set<String> missingOption,
final Set<PropertyMapper<?>> disabledMappers, PropertyMapper<?> mapper, String from) {
ConfigValue configValue = Configuration.getConfigValue(from);
ConfigValue configValue = getUnmappedValue(from);
String configValueStr = configValue.getValue();
// don't consider missing or anything below standard env properties
@@ -408,12 +425,7 @@ public class Picocli {
return;
}
PropertyMappingInterceptor.enable();
try {
mapper.validate(configValue);
} finally {
PropertyMappingInterceptor.disable();
}
mapper.validate(configValue);
mapper.getDeprecatedMetadata().ifPresent(metadata -> handleDeprecated(deprecatedInUse, mapper, configValueStr, metadata));
}
@@ -909,7 +921,7 @@ public class Picocli {
}
this.parsedCommand = Optional.ofNullable(command);
if (!Environment.isRebuilt() && command instanceof AbstractStartCommand
if (!Environment.isRebuilt() && command instanceof AbstractAutoBuildCommand
&& !cliArgs.contains(OPTIMIZED_BUILD_OPTION_LONG)) {
Environment.setRebuildCheck();
}

View File

@@ -1,7 +1,7 @@
package org.keycloak.quarkus.runtime.cli;
import static java.lang.String.format;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import java.io.PrintWriter;
import java.util.Optional;

View File

@@ -35,15 +35,13 @@ import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.cli.Picocli;
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.configuration.mappers.HostnameV2PropertyMappers;
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
import picocli.CommandLine;
import picocli.CommandLine.Help.Ansi;
public abstract class AbstractStartCommand extends AbstractCommand {
public abstract class AbstractAutoBuildCommand extends AbstractCommand {
public static final String OPTIMIZED_BUILD_OPTION_LONG = "--optimized";
@@ -148,15 +146,6 @@ public abstract class AbstractStartCommand extends AbstractCommand {
}
@Override
protected void validateConfig() {
super.validateConfig(); // we want to run the generic validation here first to check for unknown options
if (shouldStart()) { // if not starting, we aren't accepting http requests so no validation needed
HttpPropertyMappers.validateConfig();
HostnameV2PropertyMappers.validateConfig(picocli);
}
}
@Override
public List<OptionCategory> getOptionCategories() {
EnumSet<OptionCategory> excludedCategories = excludedCategories();

View File

@@ -137,4 +137,11 @@ public abstract class AbstractCommand implements Callable<Integer> {
return Environment.PROD_PROFILE_VALUE;
}
/**
* @return true if the command starts an http server
*/
public boolean isServing() {
return false;
}
}

View File

@@ -26,7 +26,7 @@ import picocli.CommandLine;
import java.util.List;
import java.util.stream.Collectors;
public abstract class AbstractNonServerCommand extends AbstractStartCommand {
public abstract class AbstractNonServerCommand extends AbstractAutoBuildCommand {
@CommandLine.Mixin
OptimizedMixin optimizedMixin = new OptimizedMixin();
@@ -56,4 +56,5 @@ public abstract class AbstractNonServerCommand extends AbstractStartCommand {
public void onStart(QuarkusKeycloakApplication application) {
}
}

View File

@@ -30,7 +30,7 @@ import org.keycloak.config.ConfigProviderFactory;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import picocli.CommandLine;
public abstract class AbstractUpdatesCommand extends AbstractStartCommand {
public abstract class AbstractUpdatesCommand extends AbstractAutoBuildCommand {
@CommandLine.Mixin
OptimizedMixin optimizedMixin = new OptimizedMixin();

View File

@@ -21,7 +21,6 @@ import static org.keycloak.exportimport.ExportImportConfig.ACTION_EXPORT;
import org.keycloak.config.OptionCategory;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.quarkus.runtime.configuration.mappers.ExportPropertyMappers;
import picocli.CommandLine.Command;
import java.util.EnumSet;
@@ -38,12 +37,6 @@ public final class Export extends AbstractNonServerCommand {
System.setProperty(ExportImportConfig.ACTION, ACTION_EXPORT);
}
@Override
public void validateConfig() {
ExportPropertyMappers.validateConfig();
super.validateConfig();
}
@Override
public String getName() {
return NAME;

View File

@@ -21,7 +21,6 @@ import static org.keycloak.exportimport.ExportImportConfig.ACTION_IMPORT;
import org.keycloak.config.OptionCategory;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.quarkus.runtime.configuration.mappers.ImportPropertyMappers;
import picocli.CommandLine.Command;
import java.util.EnumSet;
@@ -41,12 +40,6 @@ public final class Import extends AbstractNonServerCommand {
ExportImportConfig.setAction(ACTION_IMPORT);
}
@Override
public void validateConfig() {
ImportPropertyMappers.validateConfig();
super.validateConfig();
}
@Override
public String getName() {
return NAME;

View File

@@ -20,7 +20,7 @@ package org.keycloak.quarkus.runtime.cli.command;
import picocli.CommandLine;
import static org.keycloak.quarkus.runtime.cli.Picocli.NO_PARAM_LABEL;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
public final class OptimizedMixin {

View File

@@ -17,7 +17,7 @@
package org.keycloak.quarkus.runtime.cli.command;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import java.util.List;
@@ -38,7 +38,7 @@ import picocli.CommandLine.Command;
footer = "%nBy default, this command tries to update the server configuration by running a '" + Build.NAME + "' before starting the server. You can disable this behavior by using the '" + OPTIMIZED_BUILD_OPTION_LONG + "' option:%n%n"
+ " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} '" + OPTIMIZED_BUILD_OPTION_LONG + "'%n%n"
+ "By doing that, the server should start faster based on any previous configuration you have set when manually running the '" + Build.NAME + "' command.")
public final class Start extends AbstractStartCommand {
public final class Start extends AbstractAutoBuildCommand {
public static final String NAME = "start";
@@ -77,4 +77,9 @@ public final class Start extends AbstractStartCommand {
picocli.usageException(e.getMessage(), e.getCause());
}
}
@Override
public boolean isServing() {
return true;
}
}

View File

@@ -29,7 +29,7 @@ import picocli.CommandLine.Command;
},
footer = "%nDo NOT start the server using this command when deploying to production.%n%n"
+ "Use '${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --help-all' to list all available options, including build options.")
public final class StartDev extends AbstractStartCommand {
public final class StartDev extends AbstractAutoBuildCommand {
public static final String NAME = "start-dev";
@@ -55,4 +55,9 @@ public final class StartDev extends AbstractStartCommand {
public String getName() {
return NAME;
}
@Override
public boolean isServing() {
return true;
}
}

View File

@@ -22,17 +22,17 @@ import org.keycloak.config.BootstrapAdminOptions;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class BootstrapAdminPropertyMappers {
import java.util.List;
public final class BootstrapAdminPropertyMappers implements PropertyMapperGrouping {
private static final String PASSWORD_SET = "bootstrap admin password is set";
private static final String CLIENT_SECRET_SET = "bootstrap admin client secret is set";
private BootstrapAdminPropertyMappers() {
}
// We prefer validators here to isEnabled so that the options show up in help
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(BootstrapAdminOptions.USERNAME)
.paramLabel("username")
.addValidateEnabled(BootstrapAdminPropertyMappers::isPasswordSet, PASSWORD_SET)
@@ -52,8 +52,8 @@ public final class BootstrapAdminPropertyMappers {
fromOption(BootstrapAdminOptions.CLIENT_SECRET)
.paramLabel("client secret")
.isMasked(true)
.build(),
};
.build()
);
}
private static boolean isPasswordSet() {

View File

@@ -11,7 +11,6 @@ import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import org.hibernate.boot.model.source.spi.Caching;
import org.keycloak.common.Profile;
import org.keycloak.config.CachingOptions;
import org.keycloak.config.Option;
@@ -25,7 +24,7 @@ import com.google.common.base.CaseFormat;
import io.smallrye.config.ConfigSourceInterceptorContext;
final class CachingPropertyMappers {
final class CachingPropertyMappers implements PropertyMapperGrouping {
private static final String REMOTE_HOST_SET = "remote host is set";
private static final String MULTI_SITE_OR_EMBEDDED_REMOTE_FEATURE_SET = "feature '%s' or '%s' is set".formatted(Profile.Feature.MULTI_SITE.getKey(), Profile.Feature.CLUSTERLESS.getKey());
@@ -33,10 +32,8 @@ final class CachingPropertyMappers {
private static final String CACHE_STACK_SET_TO_ISPN = "'cache' type is set to '" + CachingOptions.Mechanism.ispn.name() + "'";
private CachingPropertyMappers() {
}
public static PropertyMapper<?>[] getClusteringPropertyMappers() {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
List<PropertyMapper<?>> staticMappers = List.of(
fromOption(CachingOptions.CACHE)
.paramLabel("type")
@@ -170,7 +167,7 @@ final class CachingPropertyMappers {
mappers.add(maxCountOpt(cache, InfinispanUtils::isEmbeddedInfinispan, "embedded Infinispan clusters configured"));
}
return mappers.toArray(new PropertyMapper[0]);
return mappers;
}
private static boolean getDefaultMtlsEnabled() {

View File

@@ -8,17 +8,18 @@ import org.keycloak.quarkus.runtime.configuration.IgnoredArtifacts;
import static org.keycloak.config.ClassLoaderOptions.QUARKUS_REMOVED_ARTIFACTS_PROPERTY;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
final class ClassLoaderPropertyMappers {
import java.util.List;
private ClassLoaderPropertyMappers(){}
final class ClassLoaderPropertyMappers implements PropertyMapperGrouping {
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(ClassLoaderOptions.IGNORE_ARTIFACTS)
.to(QUARKUS_REMOVED_ARTIFACTS_PROPERTY)
.transformer(ClassLoaderPropertyMappers::resolveIgnoredArtifacts)
.build()
};
);
}
private static String resolveIgnoredArtifacts(String value, ConfigSourceInterceptorContext context) {

View File

@@ -6,19 +6,18 @@ import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class ConfigKeystorePropertyMappers {
public final class ConfigKeystorePropertyMappers implements PropertyMapperGrouping {
private static final String SMALLRYE_KEYSTORE_PATH = "smallrye.config.source.keystore.kc-default.path";
private static final String SMALLRYE_KEYSTORE_PASSWORD = "smallrye.config.source.keystore.kc-default.password";
private ConfigKeystorePropertyMappers() {
}
public static PropertyMapper<?>[] getConfigKeystorePropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(ConfigKeystoreOptions.CONFIG_KEYSTORE)
.to(SMALLRYE_KEYSTORE_PATH)
.transformer(ConfigKeystorePropertyMappers::validatePath)
@@ -34,7 +33,7 @@ public final class ConfigKeystorePropertyMappers {
.to("smallrye.config.source.keystore.kc-default.type")
.paramLabel("config-keystore-type")
.build()
};
);
}
private static String validatePath(String option, ConfigSourceInterceptorContext context) {

View File

@@ -28,14 +28,12 @@ import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvi
import static org.keycloak.quarkus.runtime.configuration.mappers.DatabasePropertyMappers.Datasources.appendDatasourceMappers;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
final class DatabasePropertyMappers {
final class DatabasePropertyMappers implements PropertyMapperGrouping {
private static final Logger log = Logger.getLogger(DatabasePropertyMappers.class);
private DatabasePropertyMappers() {
}
public static PropertyMapper<?>[] getDatabasePropertyMappers() {
var mappers = new PropertyMapper<?>[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
List<PropertyMapper<?>> mappers = List.of(
fromOption(DatabaseOptions.DB_DIALECT)
.mapFrom(DatabaseOptions.DB, DatabasePropertyMappers::transformDialect)
.build(),
@@ -106,7 +104,7 @@ final class DatabasePropertyMappers {
.build(),
fromOption(DB_URL_PATH)
.build()
};
);
return appendDatasourceMappers(mappers, Map.of(
// Inherit options from the DB mappers
@@ -186,8 +184,8 @@ final class DatabasePropertyMappers {
/**
* Automatically create mappers for datasource options
*/
static PropertyMapper<?>[] appendDatasourceMappers(PropertyMapper<?>[] mappers, Map<Option<?>, Consumer<PropertyMapper.Builder<?>>> transformDatasourceMappers) {
List<PropertyMapper<?>> datasourceMappers = new ArrayList<>(OPTIONS_DATASOURCES.size() + mappers.length);
static List<PropertyMapper<?>> appendDatasourceMappers(List<PropertyMapper<?>> mappers, Map<Option<?>, Consumer<PropertyMapper.Builder<?>>> transformDatasourceMappers) {
List<PropertyMapper<?>> datasourceMappers = new ArrayList<>(OPTIONS_DATASOURCES.size() + mappers.size());
for (var parent : mappers) {
var parentOption = parent.getOption();
@@ -225,9 +223,9 @@ final class DatabasePropertyMappers {
datasourceMappers.add(created.build());
}
datasourceMappers.addAll(List.of(mappers));
datasourceMappers.addAll(mappers);
return datasourceMappers.toArray(new PropertyMapper[0]);
return datasourceMappers;
}
private static String transformDatasourceTo(String to) {

View File

@@ -10,13 +10,14 @@ import static org.keycloak.quarkus.runtime.configuration.mappers.MetricsProperty
import static org.keycloak.quarkus.runtime.configuration.mappers.MetricsPropertyMappers.metricsEnabled;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.util.List;
final class EventPropertyMappers {
private EventPropertyMappers(){}
final class EventPropertyMappers implements PropertyMapperGrouping {
public static PropertyMapper<?>[] getMetricsPropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(USER_EVENT_METRICS_ENABLED)
.to("kc.spi-events-listener--micrometer-user-event-metrics--enabled")
.isEnabled(EventPropertyMappers::userEventsMetricsEnabled, METRICS_ENABLED_MSG + " and feature " + Profile.Feature.USER_EVENT_METRICS.getKey() + " is enabled")
@@ -30,8 +31,8 @@ final class EventPropertyMappers {
.to("kc.spi-events-listener--micrometer-user-event-metrics--events")
.paramLabel("events")
.isEnabled(EventPropertyMappers::userEventsMetricsTags, "user event metrics are enabled")
.build(),
};
.build()
);
}
private static boolean userEventsMetricsEnabled() {

View File

@@ -24,7 +24,9 @@ import org.keycloak.config.Option;
import org.keycloak.config.OptionBuilder;
import org.keycloak.config.OptionCategory;
import org.keycloak.exportimport.UsersExportStrategy;
import org.keycloak.quarkus.runtime.cli.Picocli;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.cli.command.Export;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import static org.keycloak.exportimport.ExportImportConfig.PROVIDER;
@@ -32,16 +34,16 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.getOption
import static org.keycloak.quarkus.runtime.configuration.Configuration.isBlank;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class ExportPropertyMappers {
import java.util.List;
public final class ExportPropertyMappers implements PropertyMapperGrouping {
private static final String EXPORTER_PROPERTY = "kc.spi-export--exporter";
private static final String SINGLE_FILE = "singleFile";
private static final String DIR = "dir";
private ExportPropertyMappers() {
}
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(EXPORTER_PLACEHOLDER)
.to(EXPORTER_PROPERTY)
.transformer(ExportPropertyMappers::transformExporter)
@@ -75,7 +77,7 @@ public final class ExportPropertyMappers {
.isEnabled(ExportPropertyMappers::isDirProvider)
.paramLabel("number")
.build()
};
);
}
private static void validateUsersUsage(PropertyMapper<?> mapper, ConfigValue value) {
@@ -88,8 +90,9 @@ public final class ExportPropertyMappers {
}
}
public static void validateConfig() {
if (getOptionalValue(EXPORTER_PROPERTY).isEmpty() && System.getProperty(PROVIDER) == null) {
@Override
public void validateConfig(Picocli picocli) {
if (picocli.getParsedCommand().orElse(null) instanceof Export && getOptionalValue(EXPORTER_PROPERTY).isEmpty() && System.getProperty(PROVIDER) == null) {
throw new PropertyException("Must specify either --dir or --file options.");
}
}

View File

@@ -5,6 +5,7 @@ import org.keycloak.common.Profile.Feature;
import org.keycloak.config.FeatureOptions;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -12,15 +13,14 @@ import java.util.stream.Collectors;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class FeaturePropertyMappers {
public final class FeaturePropertyMappers implements PropertyMapperGrouping {
private static final Pattern VERSIONED_PATTERN = Pattern.compile("([^:]+):v(\\d+)");
private FeaturePropertyMappers() {
}
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(FeatureOptions.FEATURES)
.paramLabel("feature")
.validator(FeaturePropertyMappers::validateEnabledFeature)
@@ -28,7 +28,7 @@ public final class FeaturePropertyMappers {
fromOption(FeatureOptions.FEATURES_DISABLED)
.paramLabel("feature")
.build()
};
);
}
public static void validateEnabledFeature(String feature) {

View File

@@ -4,18 +4,20 @@ import org.keycloak.config.HealthOptions;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.util.List;
final class HealthPropertyMappers {
private HealthPropertyMappers(){}
final class HealthPropertyMappers implements PropertyMapperGrouping {
public static PropertyMapper<?>[] getHealthPropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(HealthOptions.HEALTH_ENABLED)
// no need to map to a quarkus option, this option exists to
// to control artifact / extension inclusion. Quarkus will default to enabled
.build()
};
);
}
}

View File

@@ -13,18 +13,18 @@ import org.keycloak.common.Profile;
import org.keycloak.config.HostnameV2Options;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.cli.Picocli;
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
import org.keycloak.config.ProxyOptions;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.utils.SecureContextResolver;
public final class HostnameV2PropertyMappers {
public final class HostnameV2PropertyMappers implements PropertyMapperGrouping {
private static final String CONTEXT_WARNING = "the server is running in an insecure context. Secure contexts are required for full functionality, including cross-origin cookies.";
private static final List<String> REMOVED_OPTIONS = Arrays.asList("hostname-admin-url", "hostname-path", "hostname-port", "hostname-strict-backchannel", "hostname-url", "proxy", "hostname-strict-https");
private HostnameV2PropertyMappers(){}
public static PropertyMapper<?>[] getHostnamePropertyMappers() {
@Override
public List<? extends PropertyMapper<?>> getPropertyMappers() {
return Stream.of(
fromOption(HostnameV2Options.HOSTNAME)
.to("kc.spi-hostname--v2--hostname")
@@ -39,11 +39,14 @@ public final class HostnameV2PropertyMappers {
fromOption(HostnameV2Options.HOSTNAME_DEBUG)
)
.map(b -> b.isEnabled(() -> Profile.isFeatureEnabled(Profile.Feature.HOSTNAME_V2), "hostname:v2 feature is enabled").build())
.toArray(s -> new PropertyMapper<?>[s]);
.toList();
}
public static void validateConfig(Picocli picocli) {
validateConfig(picocli::warn);
@Override
public void validateConfig(Picocli picocli) {
if (picocli.getParsedCommand().filter(AbstractCommand::isServing).isPresent()) {
validateConfig(picocli::warn);
}
}
public static void validateConfig(Consumer<String> warn) {

View File

@@ -5,11 +5,14 @@ import org.keycloak.quarkus.runtime.configuration.Configuration;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public class HttpAccessLogPropertyMappers {
import java.util.List;
public class HttpAccessLogPropertyMappers implements PropertyMapperGrouping {
public static final String ACCESS_LOG_ENABLED_MSG = "HTTP Access log is enabled";
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(HttpAccessLogOptions.HTTP_ACCESS_LOG_ENABLED)
.to("quarkus.http.access-log.enabled")
.build(),
@@ -20,8 +23,8 @@ public class HttpAccessLogPropertyMappers {
fromOption(HttpAccessLogOptions.HTTP_ACCESS_LOG_EXCLUDE)
.isEnabled(HttpAccessLogPropertyMappers::isHttpAccessLogEnabled, ACCESS_LOG_ENABLED_MSG)
.to("quarkus.http.access-log.exclude-pattern")
.build(),
};
.build()
);
}
static boolean isHttpAccessLogEnabled() {

View File

@@ -10,19 +10,22 @@ import org.keycloak.config.SecurityOptions;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.Messages;
import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler;
import org.keycloak.quarkus.runtime.cli.Picocli;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class HttpPropertyMappers {
public final class HttpPropertyMappers implements PropertyMapperGrouping {
private static final int MIN_MAX_THREADS = 50;
private static final String QUARKUS_HTTPS_CERT_FILES = "quarkus.http.ssl.certificate.files";
private static final String QUARKUS_HTTPS_CERT_KEY_FILES = "quarkus.http.ssl.certificate.key-files";
@@ -31,8 +34,6 @@ public final class HttpPropertyMappers {
public static final String QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE = "quarkus.http.ssl.certificate.trust-store-file-type";
private static final String QUARKUS_HTTPS_KEY_STORE_FILE_TYPE = "quarkus.http.ssl.certificate.key-store-file-type";
private HttpPropertyMappers(){}
// Transform runtime exceptions obtained from Quarkus to ours with a relevant message
private static void setCustomExceptionTransformer() {
ExecutionExceptionHandler.addExceptionTransformer(TlsUtils.class, exception -> {
@@ -53,9 +54,10 @@ public final class HttpPropertyMappers {
});
}
public static PropertyMapper<?>[] getHttpPropertyMappers() {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
setCustomExceptionTransformer();
return new PropertyMapper[] {
return List.of(
fromOption(HttpOptions.HTTP_ENABLED)
.to("quarkus.http.insecure-requests")
.transformer(HttpPropertyMappers::getHttpEnabledTransformer)
@@ -153,13 +155,16 @@ public final class HttpPropertyMappers {
.isEnabled(MetricsPropertyMappers::metricsEnabled, MetricsPropertyMappers.METRICS_ENABLED_MSG)
.paramLabel("list of buckets")
.build()
};
);
}
public static void validateConfig() {
boolean enabled = isHttpEnabled(getOptionalKcValue(HttpOptions.HTTP_ENABLED.getKey()).orElse(null));
if (!enabled && !isHttpsEnabled()) {
throw new PropertyException(Messages.httpsConfigurationNotSet());
@Override
public void validateConfig(Picocli picocli) {
if (picocli.getParsedCommand().filter(AbstractCommand::isServing).isPresent()) {
boolean enabled = isHttpEnabled(getOptionalKcValue(HttpOptions.HTTP_ENABLED.getKey()).orElse(null));
if (!enabled && !isHttpsEnabled()) {
throw new PropertyException(Messages.httpsConfigurationNotSet());
}
}
}

View File

@@ -24,22 +24,24 @@ import org.keycloak.config.Option;
import org.keycloak.config.OptionBuilder;
import org.keycloak.config.OptionCategory;
import org.keycloak.exportimport.Strategy;
import org.keycloak.quarkus.runtime.cli.Picocli;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.cli.command.Import;
import static org.keycloak.exportimport.ExportImportConfig.PROVIDER;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class ImportPropertyMappers {
import java.util.List;
public final class ImportPropertyMappers implements PropertyMapperGrouping {
private static final String IMPORTER_PROPERTY = "kc.spi-import--importer";
private static final String SINGLE_FILE = "singleFile";
private static final String DIR = "dir";
private ImportPropertyMappers() {
}
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(IMPORTER_PLACEHOLDER)
.to(IMPORTER_PROPERTY)
.transformer(ImportPropertyMappers::transformImporter)
@@ -62,12 +64,13 @@ public final class ImportPropertyMappers {
.to("kc.spi-import--dir--strategy")
.transformer(ImportPropertyMappers::transformOverride)
.isEnabled(ImportPropertyMappers::isDirProvider)
.build(),
};
.build()
);
}
public static void validateConfig() {
if (getOptionalValue(IMPORTER_PROPERTY).isEmpty() && System.getProperty(PROVIDER) == null) {
@Override
public void validateConfig(Picocli picocli) {
if (picocli.getParsedCommand().orElse(null) instanceof Import && getOptionalValue(IMPORTER_PROPERTY).isEmpty() && System.getProperty(PROVIDER) == null) {
throw new PropertyException("Must specify either --dir or --file options.");
}
}

View File

@@ -12,6 +12,7 @@ import java.io.File;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -32,7 +33,7 @@ import org.keycloak.quarkus.runtime.configuration.Configuration;
import io.smallrye.config.ConfigSourceInterceptorContext;
public final class LoggingPropertyMappers {
public final class LoggingPropertyMappers implements PropertyMapperGrouping {
private static final String CONSOLE_ENABLED_MSG = "Console log handler is activated";
private static final String FILE_ENABLED_MSG = "File log handler is activated";
@@ -41,12 +42,11 @@ public final class LoggingPropertyMappers {
private final static Map<String, Map<String, String>> rootLogLevels = new HashMap<String, Map<String,String>>();
private LoggingPropertyMappers() {
}
public static PropertyMapper<?>[] getMappers() {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
rootLogLevels.clear(); // reset the cached root log level and categories
PropertyMapper<?>[] defaultMappers = new PropertyMapper[]{
return List.of(
fromOption(LoggingOptions.LOG)
.paramLabel("<handler>")
.build(),
@@ -258,11 +258,9 @@ public final class LoggingPropertyMappers {
.isEnabled(LoggingPropertyMappers::isMdcActive, "MDC logging is enabled")
.to("kc.spi-mapped-diagnostic-context--default--mdc-keys")
.paramLabel("keys")
.build(),
.build()
};
return defaultMappers;
);
}
public static boolean isConsoleEnabled() {

View File

@@ -27,15 +27,15 @@ import static org.keycloak.config.ManagementOptions.LEGACY_OBSERVABILITY_INTERFA
import static org.keycloak.quarkus.runtime.configuration.Configuration.isTrue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public class ManagementPropertyMappers {
import java.util.List;
public class ManagementPropertyMappers implements PropertyMapperGrouping {
private static final String HTTP_MANAGEMENT_SCHEME_IS_INHERITED = "http-management-scheme is inherited";
private ManagementPropertyMappers() {
}
public static PropertyMapper<?>[] getManagementPropertyMappers() {
return new PropertyMapper[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(ManagementOptions.HTTP_MANAGEMENT_ENABLED)
.to("quarkus.management.enabled")
.transformer((val, ctx) -> managementEnabledTransformer())
@@ -118,8 +118,8 @@ public class ManagementPropertyMappers {
.mapFrom(HttpOptions.HTTPS_KEY_STORE_TYPE)
.to("quarkus.management.ssl.certificate.key-store-file-type")
.paramLabel("type")
.build(),
};
.build()
);
}
public static boolean isManagementEnabled() {

View File

@@ -5,19 +5,20 @@ import org.keycloak.config.MetricsOptions;
import static org.keycloak.quarkus.runtime.configuration.Configuration.isTrue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.util.List;
final class MetricsPropertyMappers {
final class MetricsPropertyMappers implements PropertyMapperGrouping {
public static final String METRICS_ENABLED_MSG = "metrics are enabled";
private MetricsPropertyMappers(){}
public static PropertyMapper<?>[] getMetricsPropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(MetricsOptions.METRICS_ENABLED)
.to("quarkus.micrometer.enabled")
.build()
};
);
}
public static boolean metricsEnabled() {

View File

@@ -0,0 +1,15 @@
package org.keycloak.quarkus.runtime.configuration.mappers;
import java.util.List;
import org.keycloak.quarkus.runtime.cli.Picocli;
public interface PropertyMapperGrouping {
List<? extends PropertyMapper<?>> getPropertyMappers();
default void validateConfig(Picocli picocli) {
}
}

View File

@@ -44,6 +44,21 @@ public final class PropertyMappers {
public static String VALUE_MASK = "*******";
private static MappersConfig MAPPERS;
private static final Logger log = Logger.getLogger(PropertyMappers.class);
private final static List<PropertyMapperGrouping> GROUPINGS;
static {
GROUPINGS = List.of(new CachingPropertyMappers(), new DatabasePropertyMappers(),
new ConfigKeystorePropertyMappers(), new EventPropertyMappers(), new ClassLoaderPropertyMappers(),
new ExportPropertyMappers(), new BootstrapAdminPropertyMappers(), new HostnameV2PropertyMappers(),
new HttpPropertyMappers(), new HttpAccessLogPropertyMappers(), new HealthPropertyMappers(),
new FeaturePropertyMappers(), new ImportPropertyMappers(), new ManagementPropertyMappers(),
new MetricsPropertyMappers(), new LoggingPropertyMappers(), new ProxyPropertyMappers(),
new VaultPropertyMappers(), new TracingPropertyMappers(), new TransactionPropertyMappers(),
new SecurityPropertyMappers(), new TruststorePropertyMappers());
}
public static List<PropertyMapperGrouping> getPropertyMapperGroupings() {
return GROUPINGS;
}
private PropertyMappers(){}
@@ -53,28 +68,7 @@ public final class PropertyMappers {
public static void reset() {
MAPPERS = new MappersConfig();
MAPPERS.addAll(CachingPropertyMappers.getClusteringPropertyMappers());
MAPPERS.addAll(DatabasePropertyMappers.getDatabasePropertyMappers());
MAPPERS.addAll(HostnameV2PropertyMappers.getHostnamePropertyMappers());
MAPPERS.addAll(HttpPropertyMappers.getHttpPropertyMappers());
MAPPERS.addAll(HttpAccessLogPropertyMappers.getMappers());
MAPPERS.addAll(HealthPropertyMappers.getHealthPropertyMappers());
MAPPERS.addAll(ConfigKeystorePropertyMappers.getConfigKeystorePropertyMappers());
MAPPERS.addAll(ManagementPropertyMappers.getManagementPropertyMappers());
MAPPERS.addAll(MetricsPropertyMappers.getMetricsPropertyMappers());
MAPPERS.addAll(EventPropertyMappers.getMetricsPropertyMappers());
MAPPERS.addAll(ProxyPropertyMappers.getProxyPropertyMappers());
MAPPERS.addAll(VaultPropertyMappers.getVaultPropertyMappers());
MAPPERS.addAll(FeaturePropertyMappers.getMappers());
MAPPERS.addAll(LoggingPropertyMappers.getMappers());
MAPPERS.addAll(TracingPropertyMappers.getMappers());
MAPPERS.addAll(TransactionPropertyMappers.getTransactionPropertyMappers());
MAPPERS.addAll(ClassLoaderPropertyMappers.getMappers());
MAPPERS.addAll(SecurityPropertyMappers.getMappers());
MAPPERS.addAll(ExportPropertyMappers.getMappers());
MAPPERS.addAll(ImportPropertyMappers.getMappers());
MAPPERS.addAll(TruststorePropertyMappers.getMappers());
MAPPERS.addAll(BootstrapAdminPropertyMappers.getMappers());
GROUPINGS.forEach(g -> MAPPERS.addAll(g.getPropertyMappers()));
}
public static ConfigValue getValue(ConfigSourceInterceptorContext context, String name) {
@@ -245,7 +239,7 @@ public final class PropertyMappers {
private final WildcardMappersConfig wildcardConfig = new WildcardMappersConfig();
public void addAll(PropertyMapper<?>[] mappers) {
public void addAll(List<? extends PropertyMapper<?>> mappers) {
for (PropertyMapper<?> mapper : mappers) {
addMapper(mapper);

View File

@@ -8,12 +8,13 @@ import org.keycloak.quarkus.runtime.configuration.Configuration;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
final class ProxyPropertyMappers {
import java.util.List;
private ProxyPropertyMappers(){}
final class ProxyPropertyMappers implements PropertyMapperGrouping{
public static PropertyMapper<?>[] getProxyPropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(ProxyOptions.PROXY_HEADERS)
.to("quarkus.http.proxy.proxy-address-forwarding")
.transformer((v, c) -> proxyEnabled(null, v, c))
@@ -49,7 +50,7 @@ final class ProxyPropertyMappers {
.addValidateEnabled(() -> !Configuration.isBlank(ProxyOptions.PROXY_HEADERS), "proxy-headers is set")
.paramLabel("trusted proxies")
.build()
};
);
}
private static void validateAddress(String address) {

View File

@@ -2,6 +2,8 @@ package org.keycloak.quarkus.runtime.configuration.mappers;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.util.List;
import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature;
import org.keycloak.common.crypto.FipsMode;
@@ -9,17 +11,16 @@ import org.keycloak.config.SecurityOptions;
import io.smallrye.config.ConfigSourceInterceptorContext;
final class SecurityPropertyMappers {
final class SecurityPropertyMappers implements PropertyMapperGrouping {
private SecurityPropertyMappers() {
}
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(SecurityOptions.FIPS_MODE).transformer(SecurityPropertyMappers::resolveFipsMode)
.paramLabel("mode")
.build(),
};
.build()
);
}
private static String resolveFipsMode(String value, ConfigSourceInterceptorContext context) {

View File

@@ -26,6 +26,7 @@ import org.keycloak.utils.StringUtil;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import static org.keycloak.config.TracingOptions.TRACING_COMPRESSION;
import static org.keycloak.config.TracingOptions.TRACING_ENABLED;
@@ -39,15 +40,13 @@ import static org.keycloak.config.TracingOptions.TRACING_SAMPLER_TYPE;
import static org.keycloak.config.TracingOptions.TRACING_SERVICE_NAME;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public class TracingPropertyMappers {
public class TracingPropertyMappers implements PropertyMapperGrouping {
private static final String OTEL_FEATURE_ENABLED_MSG = "'opentelemetry' feature is enabled";
private static final String TRACING_ENABLED_MSG = "Tracing is enabled";
private TracingPropertyMappers() {
}
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[]{
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(TRACING_ENABLED)
.isEnabled(TracingPropertyMappers::isFeatureEnabled, OTEL_FEATURE_ENABLED_MSG)
.to("quarkus.otel.enabled") // enable/disable whole OTel, tracing is enabled by default
@@ -99,7 +98,7 @@ public class TracingPropertyMappers {
.to("kc.spi-cache-embedded--default--tracing-enabled")
.isEnabled(TracingPropertyMappers::isTracingAndEmbeddedInfinispanEnabled, "tracing and embedded Infinispan is enabled")
.build()
};
);
}
private static void validateEndpoint(String value) {

View File

@@ -6,12 +6,13 @@ import org.keycloak.config.TransactionOptions;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public class TransactionPropertyMappers {
import java.util.List;
private TransactionPropertyMappers(){}
public class TransactionPropertyMappers implements PropertyMapperGrouping {
public static PropertyMapper<?>[] getTransactionPropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(TransactionOptions.TRANSACTION_XA_ENABLED)
.to("quarkus.datasource.jdbc.transactions")
.transformer(TransactionPropertyMappers::getQuarkusTransactionsValue)
@@ -20,7 +21,7 @@ public class TransactionPropertyMappers {
.to("quarkus.datasource.\"<datasource>\".jdbc.transactions")
.transformer(TransactionPropertyMappers::getQuarkusTransactionsValue)
.build()
};
);
}
private static String getQuarkusTransactionsValue(String txValue, ConfigSourceInterceptorContext context) {

View File

@@ -4,18 +4,21 @@ import org.keycloak.config.TruststoreOptions;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public class TruststorePropertyMappers {
import java.util.List;
public static PropertyMapper<?>[] getMappers() {
return new PropertyMapper[] {
public class TruststorePropertyMappers implements PropertyMapperGrouping {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(TruststoreOptions.TRUSTSTORE_PATHS)
.paramLabel(TruststoreOptions.TRUSTSTORE_PATHS.getKey())
.build(),
fromOption(TruststoreOptions.HOSTNAME_VERIFICATION_POLICY)
.paramLabel(TruststoreOptions.HOSTNAME_VERIFICATION_POLICY.getKey())
.to("kc.spi-truststore--file--hostname-verification-policy")
.build(),
};
.build()
);
}
}

View File

@@ -4,13 +4,13 @@ import org.keycloak.config.VaultOptions;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
final class VaultPropertyMappers {
import java.util.List;
private VaultPropertyMappers() {
}
final class VaultPropertyMappers implements PropertyMapperGrouping {
public static PropertyMapper<?>[] getVaultPropertyMappers() {
return new PropertyMapper[] {
@Override
public List<PropertyMapper<?>> getPropertyMappers() {
return List.of(
fromOption(VaultOptions.VAULT)
.paramLabel("provider")
.build(),
@@ -30,7 +30,7 @@ final class VaultPropertyMappers {
.to("kc.spi-vault--keystore--type")
.paramLabel("type")
.build()
};
);
}
}

View File

@@ -46,7 +46,7 @@ import org.keycloak.config.LoggingOptions;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.KeycloakMain;
import org.keycloak.quarkus.runtime.cli.command.AbstractCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
import org.keycloak.quarkus.runtime.configuration.AbstractConfigurationTest;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider;
@@ -358,7 +358,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("build", "--db=dev-file");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start-dev", "--hostname=name", "--http-enabled=true");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertTrue(nonRunningPicocli.reaug);
assertEquals("dev", nonRunningPicocli.buildProps.getProperty(org.keycloak.common.util.Environment.PROFILE));
}
@@ -378,7 +378,7 @@ public class PicocliTest extends AbstractConfigurationTest {
int code = CommandLine.ExitCode.OK;
if (Stream.of(args).anyMatch("start-dev"::equals)) {
Environment.setRebuildCheck();
code = AbstractStartCommand.REBUILT_EXIT_CODE;
code = AbstractAutoBuildCommand.REBUILT_EXIT_CODE;
}
NonRunningPicocli nonRunningPicocli = pseudoLaunch(args);
assertTrue(nonRunningPicocli.getErrString(), nonRunningPicocli.reaug);
@@ -394,7 +394,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("build", "--db=dev-file");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "export", "--file=file");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertTrue(nonRunningPicocli.reaug);
}
@@ -403,7 +403,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("build", "--db=dev-file");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--db=dev-file", "--file=file");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertFalse(nonRunningPicocli.reaug);
}
@@ -421,7 +421,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("start-dev");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db=dev-file", "--hostname=name", "--http-enabled=true");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertTrue(nonRunningPicocli.reaug);
}
@@ -430,7 +430,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("start-dev");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("--profile=dev", "export", "--file=file");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertFalse(nonRunningPicocli.reaug);
}
@@ -439,7 +439,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("start-dev", "-v");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("export", "--db=dev-file", "--file=file");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertTrue(nonRunningPicocli.reaug);
assertEquals("prod", nonRunningPicocli.buildProps.getProperty(org.keycloak.common.util.Environment.PROFILE));
}
@@ -449,7 +449,7 @@ public class PicocliTest extends AbstractConfigurationTest {
build("build", "--db=dev-file");
NonRunningPicocli nonRunningPicocli = pseudoLaunch("start", "--db=dev-file", "--features=docker", "--hostname=name", "--http-enabled=true");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getOutString(), containsString("features=<unset> > features=docker"));
assertTrue(nonRunningPicocli.reaug);
}
@@ -899,7 +899,7 @@ public class PicocliTest extends AbstractConfigurationTest {
putEnvVar("KC_SPI_EVENTS_LISTENER_PROVIDER", "new-jboss-logging");
nonRunningPicocli = pseudoLaunch("start", "--http-enabled=true", "--hostname-strict=false");
assertEquals(AbstractStartCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertEquals(AbstractAutoBuildCommand.REBUILT_EXIT_CODE, nonRunningPicocli.exitCode);
assertTrue(nonRunningPicocli.reaug);
assertThat(nonRunningPicocli.getOutString(), containsString("The following SPI options"));
}

View File

@@ -32,7 +32,7 @@ import org.keycloak.it.utils.KeycloakDistribution;
import org.keycloak.it.utils.RawKeycloakDistribution;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
@WithEnvVars({"KC_CACHE", "local"}) // avoid flakey port conflicts
@DistributionTest

View File

@@ -19,7 +19,7 @@ package org.keycloak.it.cli.dist;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAME;
import org.junit.jupiter.api.Test;

View File

@@ -22,7 +22,7 @@ import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
@DistributionTest
@RawDistOnly(reason = "Containers are immutable")

View File

@@ -17,7 +17,7 @@
package org.keycloak.it.cli.dist;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

View File

@@ -32,7 +32,7 @@ import org.keycloak.it.utils.KeycloakDistribution;
import com.acme.provider.legacy.jpa.user.CustomUserProvider;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
@DistributionTest
@RawDistOnly(reason = "Containers are immutable")

View File

@@ -22,7 +22,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAME;
import org.junit.jupiter.api.MethodOrderer;

View File

@@ -26,7 +26,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.utils.RawDistRootPath;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
import java.io.File;
import java.io.IOException;
@@ -44,13 +44,13 @@ import static org.hamcrest.MatcherAssert.assertThat;
public abstract class BasicDatabaseTest {
@Test
@Launch({ "start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
@Launch({ "start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
protected void testSuccessful(CLIResult cliResult) {
cliResult.assertStarted();
}
@Test
@Launch({ "start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false", "--db-username=wrong" })
@Launch({ "start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false", "--db-username=wrong" })
protected void testWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("ERROR: Failed to obtain JDBC connection");
assertWrongUsername(cliResult);
@@ -59,7 +59,7 @@ public abstract class BasicDatabaseTest {
protected abstract void assertWrongUsername(CLIResult cliResult);
@Test
@Launch({ "start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false", "--db-password=wrong" })
@Launch({ "start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false", "--db-password=wrong" })
protected void testWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("ERROR: Failed to obtain JDBC connection");
assertWrongPassword(cliResult);
@@ -69,7 +69,7 @@ public abstract class BasicDatabaseTest {
@Order(1)
@Test
@Launch({ "export", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--dir=./target/export"})
@Launch({ "export", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--dir=./target/export"})
public void testExportSucceeds(CLIResult cliResult) {
cliResult.assertMessage("Full model export requested");
cliResult.assertMessage("Export finished successfully");
@@ -77,7 +77,7 @@ public abstract class BasicDatabaseTest {
@Order(2)
@Test
@Launch({ "import", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--dir=./target/export" })
@Launch({ "import", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--dir=./target/export" })
void testImportSucceeds(CLIResult cliResult) {
cliResult.assertMessage("target/export");
cliResult.assertMessage("Realm 'master' imported");

View File

@@ -24,7 +24,7 @@ import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.WithDatabase;
import org.keycloak.it.storage.database.MariaDBTest;
import org.keycloak.it.utils.RawDistRootPath;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
import io.quarkus.test.junit.main.Launch;
@@ -35,14 +35,14 @@ public class MariaDBDistTest extends MariaDBTest {
@Override
@Tag(DistributionTest.STORAGE)
@Test
@Launch({ "start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
@Launch({ "start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
protected void testSuccessful(CLIResult result) {
super.testSuccessful(result);
}
@Tag(DistributionTest.STORAGE)
@Test
@Launch({"start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
@Launch({"start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
public void testKeycloakDbUpdateScript(CLIResult cliResult, RawDistRootPath rawDistRootPath) {
assertManualDbInitialization(cliResult, rawDistRootPath);
}

View File

@@ -7,7 +7,7 @@ import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.WithDatabase;
import org.keycloak.it.storage.database.MySQLTest;
import org.keycloak.it.utils.RawDistRootPath;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
import io.quarkus.test.junit.main.Launch;
@@ -18,14 +18,14 @@ public class MySQLDistTest extends MySQLTest {
@Override
@Tag(DistributionTest.STORAGE)
@Test
@Launch({ "start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
@Launch({ "start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--http-enabled=true", "--hostname-strict=false" })
protected void testSuccessful(CLIResult result) {
super.testSuccessful(result);
}
@Tag(DistributionTest.STORAGE)
@Test
@Launch({"start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
@Launch({"start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
public void testKeycloakDbUpdateScript(CLIResult cliResult, RawDistRootPath rawDistRootPath) {
assertManualDbInitialization(cliResult, rawDistRootPath);
}

View File

@@ -29,7 +29,7 @@ import org.keycloak.it.storage.database.PostgreSQLTest;
import io.quarkus.test.junit.main.Launch;
import org.keycloak.it.utils.RawDistRootPath;
import org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand;
import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand;
@DistributionTest(removeBuildOptionsAfterBuild = true)
@WithDatabase(alias = "postgres")
@@ -44,7 +44,7 @@ public class PostgreSQLDistTest extends PostgreSQLTest {
@Tag(DistributionTest.STORAGE)
@Test
@Launch({"start", AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
@Launch({"start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
public void testKeycloakDbUpdateScript(CLIResult cliResult, RawDistRootPath rawDistRootPath) {
assertManualDbInitialization(cliResult, rawDistRootPath);
}

View File

@@ -17,7 +17,7 @@
package org.keycloak.it.junit5.extension;
import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTIMIZED_BUILD_OPTION_LONG;
import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG;
import java.util.ArrayList;
import java.util.HashMap;