From 605b51890ec32908594c045c91f08717220c46e8 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Wed, 29 Jun 2022 11:00:55 -0300 Subject: [PATCH] Enables the new store and the concurrenthashmap provider Closes #12651 --- .../java/org/keycloak/common/Profile.java | 8 +- .../org/keycloak/config/FeatureOptions.java | 4 +- .../org/keycloak/config/LoggingOptions.java | 2 +- .../org/keycloak/config/ProxyOptions.java | 8 +- .../org/keycloak/config/StorageOptions.java | 134 +++++++++- .../deployment/CLusteringBuildSteps.java | 3 +- ...Enabled.java => IsLegacyStoreEnabled.java} | 4 +- .../quarkus/deployment/KeycloakProcessor.java | 20 +- quarkus/runtime/pom.xml | 10 + .../quarkus/runtime/KeycloakMain.java | 48 +++- .../quarkus/runtime/KeycloakRecorder.java | 2 +- .../MicroProfileConfigProvider.java | 2 +- .../mappers/ClusteringPropertyMappers.java | 15 +- .../mappers/DatabasePropertyMappers.java | 66 +++-- .../mappers/FeaturePropertyMappers.java | 22 ++ .../mappers/HttpPropertyMappers.java | 8 +- .../mappers/LoggingPropertyMappers.java | 51 ++-- .../configuration/mappers/PropertyMapper.java | 36 +-- .../mappers/ProxyPropertyMappers.java | 21 +- .../mappers/StoragePropertyMappers.java | 160 +++++++++++- .../mappers/TransactionPropertyMappers.java | 11 +- .../jaxrs/QuarkusKeycloakApplication.java | 61 +---- .../LegacyJpaConnectionProviderFactory.java} | 237 +----------------- .../infinispan/CacheManagerFactory.java | 2 +- .../QuarkusCacheManagerProvider.java | 2 +- .../QuarkusInfinispanConnectionFactory.java | 28 +-- .../liquibase/QuarkusJpaUpdaterProvider.java | 2 +- .../QuarkusJpaUpdaterProviderFactory.java | 2 +- .../QuarkusLiquibaseConnectionProvider.java | 2 +- .../src/main/resources/META-INF/keycloak.conf | 8 +- ...ycloak.cluster.ManagedCacheManagerProvider | 2 +- ...nispan.InfinispanConnectionProviderFactory | 2 +- ...nnections.jpa.JpaConnectionProviderFactory | 2 +- ...ions.jpa.updater.JpaUpdaterProviderFactory | 2 +- ...se.conn.LiquibaseConnectionProviderFactory | 2 +- .../configuration/test/ConfigurationTest.java | 6 + .../it/junit5/extension/CLITestExtension.java | 16 +- .../it/cli/dist/ImportAtStartupDistTest.java | 4 +- .../keycloak/it/cli/dist/LoggingDistTest.java | 6 +- .../it/storage/map/ChmStorageDistTest.java | 55 ++++ ...ommandTest.testBuildHelp.unix.approved.txt | 4 + ...andTest.testBuildHelp.windows.approved.txt | 4 + ...Test.testStartDevHelpAll.unix.approved.txt | 4 + ...t.testStartDevHelpAll.windows.approved.txt | 4 + .../resources/KeycloakApplication.java | 37 +-- 45 files changed, 686 insertions(+), 443 deletions(-) rename quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/{IsDefaultPersistenceUnitEnabled.java => IsLegacyStoreEnabled.java} (85%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{database/jpa/QuarkusJpaConnectionProviderFactory.java => legacy/database/LegacyJpaConnectionProviderFactory.java} (55%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{ => legacy}/infinispan/CacheManagerFactory.java (98%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{ => legacy}/infinispan/QuarkusCacheManagerProvider.java (94%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{ => legacy}/infinispan/QuarkusInfinispanConnectionFactory.java (51%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{database => legacy}/liquibase/QuarkusJpaUpdaterProvider.java (99%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{database => legacy}/liquibase/QuarkusJpaUpdaterProviderFactory.java (96%) rename quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/{database => legacy}/liquibase/QuarkusLiquibaseConnectionProvider.java (98%) create mode 100644 quarkus/tests/integration/src/test/java/org/keycloak/it/storage/map/ChmStorageDistTest.java diff --git a/common/src/main/java/org/keycloak/common/Profile.java b/common/src/main/java/org/keycloak/common/Profile.java index c8c540e7a0..5bfb154326 100755 --- a/common/src/main/java/org/keycloak/common/Profile.java +++ b/common/src/main/java/org/keycloak/common/Profile.java @@ -103,7 +103,13 @@ public class Profile { } public static void init() { - CURRENT = new Profile(null); + PropertyResolver resolver = null; + + if (CURRENT != null) { + resolver = CURRENT.propertyResolver; + } + + CURRENT = new Profile(resolver); } public static String getName() { diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/FeatureOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/FeatureOptions.java index 80c72f5315..82fe274684 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/FeatureOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/FeatureOptions.java @@ -4,13 +4,15 @@ import org.keycloak.common.Profile; import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class FeatureOptions { - public static final Option FEATURES = new OptionBuilder("features", List.class, Profile.Feature.class) + 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()) + .defaultValue(Optional.empty()) .buildTime(true) .build(); diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java index d3a04df939..26d19218d6 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/LoggingOptions.java @@ -59,7 +59,7 @@ public class LoggingOptions { return super.toString().toLowerCase(Locale.ROOT); } } - public static final Option LOG_CONSOLE_OUTPUT = new OptionBuilder<>("log-console-output", Output.class) + 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.") diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/ProxyOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/ProxyOptions.java index 974c25a9be..acee4f835e 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/ProxyOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/ProxyOptions.java @@ -14,7 +14,7 @@ public class ProxyOptions { passthrough; } - public static final Option proxy = new OptionBuilder<>("proxy", Mode.class) + public static final Option PROXY = new OptionBuilder<>("proxy", Mode.class) .category(OptionCategory.PROXY) .description("The proxy address forwarding mode if the server is behind a reverse proxy. " + "Possible values are: " + String.join(",", Arrays.stream(Mode.values()).skip(1).map(m -> m.name()).collect(Collectors.joining(",")))) @@ -22,7 +22,7 @@ public class ProxyOptions { .expectedValues(Mode.values()) .build(); - public static final Option proxyForwardedHost = new OptionBuilder<>("proxy-forwarded-host", Boolean.class) + public static final Option PROXY_FORWARDED_HOST = new OptionBuilder<>("proxy-forwarded-host", Boolean.class) .category(OptionCategory.PROXY) .defaultValue(Boolean.FALSE) .build(); @@ -30,7 +30,7 @@ public class ProxyOptions { public static final List> ALL_OPTIONS = new ArrayList<>(); static { - ALL_OPTIONS.add(proxy); - ALL_OPTIONS.add(proxyForwardedHost); + ALL_OPTIONS.add(PROXY); + ALL_OPTIONS.add(PROXY_FORWARDED_HOST); } } diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java index e97afeca32..84ce1e4e7a 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java @@ -17,13 +17,145 @@ package org.keycloak.config; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public class StorageOptions { - public static final Option DEFAULT_PERSISTENCE_UNIT_ENABLED = new OptionBuilder<>("storage-default-persistence-unit-enabled", Boolean.class) + public enum StorageType { + legacy, + chm + } + + public static final Option STORAGE_LEGACY_ENABLED = new OptionBuilder<>("storage-legacy-enabled", Boolean.class) .category(OptionCategory.STORAGE) .defaultValue(true) .hidden() .buildTime(true) .build(); + public static final Option STORAGE = new OptionBuilder<>("storage", StorageType.class) + .category(OptionCategory.STORAGE) + .description(String.format("Sets a storage mechanism. Possible values are: %s.", + String.join(",", String.join(", ", Arrays.stream(StorageType.values()).map(StorageType::name).collect(Collectors.toList()))))) + .expectedValues(StorageType.values()) + .defaultValue(StorageType.legacy) + .buildTime(true) + .build(); + + public static final Option STORAGE_PROVIDER = new OptionBuilder<>("storage-provider", StorageType.class) + .category(OptionCategory.STORAGE) + .buildTime(true) + .build(); + + public static final Option STORAGE_REALM = new OptionBuilder<>("storage-realm", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_CLIENT = new OptionBuilder<>("storage-client", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_CLIENT_SCOPE = new OptionBuilder<>("storage-client-scope", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_GROUP = new OptionBuilder<>("storage-group", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_ROLE = new OptionBuilder<>("storage-role", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_USER = new OptionBuilder<>("storage-user", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_DEPLOYMENT_STATE = new OptionBuilder<>("storage-deployment-state", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_AUTH_SESSION = new OptionBuilder<>("storage-auth-session", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_USER_SESSION = new OptionBuilder<>("storage-user-session", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_LOGIN_FAILURE = new OptionBuilder<>("storage-login-failure", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_AUTHORIZATION_PERSISTER = new OptionBuilder<>("storage-authorization-persister", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_USER_SESSION_PERSISTER = new OptionBuilder<>("storage-user-session-persister", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_ACTION_TOKEN = new OptionBuilder<>("storage-action-token", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_DBLOCK = new OptionBuilder<>("storage-dblock", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_CACHE_REALM_ENABLED = new OptionBuilder<>("cache-realm-enabled", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_CACHE_USER_ENABLED = new OptionBuilder<>("cache-user-enabled", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_SINGLE_USE_OBJECT = new OptionBuilder<>("storage-single-use-object", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final Option STORAGE_CACHE_AUTHORIZATION_ENABLED = new OptionBuilder<>("cache-authorization-enabled", String.class) + .category(OptionCategory.STORAGE) + .hidden() + .buildTime(true) + .build(); + + public static final List> ALL_OPTIONS = List.of(STORAGE); } diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CLusteringBuildSteps.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CLusteringBuildSteps.java index c6a18530df..7e333f1461 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CLusteringBuildSteps.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CLusteringBuildSteps.java @@ -27,9 +27,8 @@ import java.nio.file.Paths; import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; import org.infinispan.commons.util.FileLookupFactory; -import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.KeycloakRecorder; -import org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory; +import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.deployment.annotations.BuildProducer; diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/IsDefaultPersistenceUnitEnabled.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/IsLegacyStoreEnabled.java similarity index 85% rename from quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/IsDefaultPersistenceUnitEnabled.java rename to quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/IsLegacyStoreEnabled.java index ce90e39b80..9a4cca20fe 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/IsDefaultPersistenceUnitEnabled.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/IsLegacyStoreEnabled.java @@ -22,11 +22,11 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.getOption import java.util.function.BooleanSupplier; import org.keycloak.quarkus.runtime.Environment; -public class IsDefaultPersistenceUnitEnabled implements BooleanSupplier { +public class IsLegacyStoreEnabled implements BooleanSupplier { @Override public boolean getAsBoolean() { - return getOptionalBooleanValue("kc.storage-default-persistence-unit-enabled").get(); + return getOptionalBooleanValue("kc.storage-legacy-enabled").get(); } } diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 669a58f927..8f417d1ec6 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -21,7 +21,7 @@ import static org.keycloak.quarkus.runtime.Providers.getProviderManager; import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames; import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS; import static org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource.QUARKUS_PROPERTY_ENABLED; -import static org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory.QUERY_PROPERTY_PREFIX; +import static org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.QUERY_PROPERTY_PREFIX; import static org.keycloak.connections.jpa.util.JpaUtils.loadSpecificNamedQueries; import static org.keycloak.representations.provider.ScriptProviderDescriptor.AUTHENTICATORS; import static org.keycloak.representations.provider.ScriptProviderDescriptor.MAPPERS; @@ -202,7 +202,7 @@ class KeycloakProcessor { * @param config * @param descriptors */ - @BuildStep + @BuildStep(onlyIf = IsLegacyStoreEnabled.class) @Record(ExecutionTime.RUNTIME_INIT) void configurePersistenceUnits(HibernateOrmConfig config, List descriptors, @@ -228,7 +228,7 @@ class KeycloakProcessor { } } - @BuildStep(onlyIf = IsDefaultPersistenceUnitEnabled.class) + @BuildStep(onlyIf = IsLegacyStoreEnabled.class) void produceDefaultPersistenceUnit(BuildProducer producer) { ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit( Thread.currentThread().getContextClassLoader().getResource("default-persistence.xml")); @@ -522,7 +522,19 @@ class KeycloakProcessor { for (Spi spi : pm.loadSpis()) { Map, Map> providers = new HashMap<>(); - List loadedFactories = new ArrayList<>(pm.load(spi)); + String provider = Config.getProvider(spi.getName()); + List loadedFactories = new ArrayList<>(); + + if (provider == null) { + loadedFactories.addAll(pm.load(spi)); + } else { + ProviderFactory factory = pm.load(spi, provider); + + if (factory != null) { + loadedFactories.add(factory); + } + } + Map deployedScriptProviders = loadDeployedScriptProviders(classLoader, spi); loadedFactories.addAll(deployedScriptProviders.values()); diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index d42161a8ee..5f31c93196 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -295,6 +295,16 @@ + + org.keycloak + keycloak-model-map + + + * + * + + + diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java index e286431ccd..48ddc355cb 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java @@ -20,6 +20,7 @@ package org.keycloak.quarkus.runtime; import static org.keycloak.quarkus.runtime.Environment.getKeycloakModeFromProfile; import static org.keycloak.quarkus.runtime.Environment.isDevProfile; import static org.keycloak.quarkus.runtime.Environment.getProfileOrDefault; +import static org.keycloak.quarkus.runtime.Environment.isImportExportMode; import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode; import static org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun; import static org.keycloak.quarkus.runtime.cli.command.Start.isDevProfileNotAllowed; @@ -35,10 +36,17 @@ import io.quarkus.runtime.ApplicationLifecycleManager; import io.quarkus.runtime.Quarkus; import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.KeycloakTransactionManager; import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler; import org.keycloak.quarkus.runtime.cli.Picocli; import org.keycloak.common.Version; import org.keycloak.quarkus.runtime.cli.command.Start; +import org.keycloak.services.ServicesLogger; +import org.keycloak.services.managers.ApplianceBootstrap; +import org.keycloak.services.resources.KeycloakApplication; import io.quarkus.runtime.QuarkusApplication; import io.quarkus.runtime.annotations.QuarkusMain; @@ -50,6 +58,9 @@ import io.quarkus.runtime.annotations.QuarkusMain; @ApplicationScoped public class KeycloakMain implements QuarkusApplication { + private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN"; + private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; + public static void main(String[] args) { System.setProperty("kc.version", Version.VERSION_KEYCLOAK); List cliArgs = Picocli.parseArgs(args); @@ -110,13 +121,17 @@ public class KeycloakMain implements QuarkusApplication { */ @Override public int run(String... args) throws Exception { + if (!isImportExportMode()) { + createAdminUser(); + } + if (isDevProfile()) { Logger.getLogger(KeycloakMain.class).warnf("Running the server in development mode. DO NOT use this configuration in production."); } int exitCode = ApplicationLifecycleManager.getExitCode(); - if (isTestLaunchMode()) { + if (isTestLaunchMode() || isImportExportMode()) { // in test mode we exit immediately // we should be managing this behavior more dynamically depending on the tests requirements (short/long lived) Quarkus.asyncExit(exitCode); @@ -126,4 +141,35 @@ public class KeycloakMain implements QuarkusApplication { return exitCode; } + + private void createAdminUser() { + String adminUserName = System.getenv(KEYCLOAK_ADMIN_ENV_VAR); + String adminPassword = System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR); + + if ((adminUserName == null || adminUserName.trim().length() == 0) + || (adminPassword == null || adminPassword.trim().length() == 0)) { + return; + } + + KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); + KeycloakSession session = sessionFactory.create(); + KeycloakTransactionManager transaction = session.getTransactionManager(); + + try { + transaction.begin(); + + new ApplianceBootstrap(session).createMasterRealmUser(adminUserName, adminPassword); + ServicesLogger.LOGGER.addUserSuccess(adminUserName, Config.getAdminRealm()); + + transaction.commit(); + } catch (IllegalStateException e) { + session.getTransactionManager().rollback(); + ServicesLogger.LOGGER.addUserFailedUserExists(adminUserName, Config.getAdminRealm()); + } catch (Throwable t) { + session.getTransactionManager().rollback(); + ServicesLogger.LOGGER.addUserFailed(t, adminUserName, Config.getAdminRealm()); + } finally { + session.close(); + } + } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java index ca3cdac4f4..6c59d8dafd 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java @@ -41,7 +41,7 @@ import org.keycloak.quarkus.runtime.storage.database.liquibase.FastServiceLocato import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.Spi; -import org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory; +import org.keycloak.quarkus.runtime.storage.legacy.infinispan.CacheManagerFactory; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java index 8325dd3016..b94d2ba8c4 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/MicroProfileConfigProvider.java @@ -131,7 +131,7 @@ public class MicroProfileConfigProvider implements Config.ConfigProvider { } private T getValue(String key, Class clazz, T defaultValue) { - return config.getOptionalValue(toDashCase(prefix.concat(OPTION_PART_SEPARATOR).concat(key)), clazz).orElse(defaultValue); + return config.getOptionalValue(toDashCase(prefix.concat(OPTION_PART_SEPARATOR).concat(key.replace('.', '-'))), clazz).orElse(defaultValue); } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java index acd8627451..65f522e0dc 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ClusteringPropertyMappers.java @@ -5,8 +5,11 @@ import org.keycloak.quarkus.runtime.Environment; import io.smallrye.config.ConfigSourceInterceptorContext; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; +import java.util.Optional; + final class ClusteringPropertyMappers { private ClusteringPropertyMappers() { @@ -30,11 +33,11 @@ final class ClusteringPropertyMappers { }; } - 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"; + private static Optional resolveConfigFile(Optional value, ConfigSourceInterceptorContext context) { + if ("local".equals(value.get())) { + return of("cache-local.xml"); + } else if ("ispn".equals(value.get())) { + return of("cache-ispn.xml"); } String pathPrefix; @@ -46,6 +49,6 @@ final class ClusteringPropertyMappers { pathPrefix = homeDir + "/conf/"; } - return pathPrefix + value; + return of(pathPrefix + value.get()); } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java index e280bfca06..70a5bd7515 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/DatabasePropertyMappers.java @@ -7,8 +7,8 @@ import org.keycloak.config.DatabaseOptions; import org.keycloak.config.database.Database; import java.util.Optional; -import java.util.function.BiFunction; +import static java.util.Optional.of; 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; @@ -27,7 +27,7 @@ final class DatabasePropertyMappers { fromOption(DatabaseOptions.DB_DRIVER) .mapFrom("db") .to("quarkus.datasource.jdbc.driver") - .transformer(DatabasePropertyMappers.getXaOrNonXaDriver()) + .transformer(DatabasePropertyMappers::getXaOrNonXaDriver) .build(), fromOption(DatabaseOptions.DB) .to("quarkus.datasource.db-kind") @@ -37,7 +37,7 @@ final class DatabasePropertyMappers { fromOption(DatabaseOptions.DB_URL) .to("quarkus.datasource.jdbc.url") .mapFrom("db") - .transformer(DatabasePropertyMappers.getDatabaseUrl()) + .transformer(DatabasePropertyMappers::getDatabaseUrl) .paramLabel("jdbc-url") .build(), fromOption(DatabaseOptions.DB_URL_HOST) @@ -88,46 +88,56 @@ final class DatabasePropertyMappers { }; } - private static BiFunction getDatabaseUrl() { - return (s, c) -> Database.getDefaultUrl(s).orElse(s); + private static Optional getDatabaseUrl(Optional value, ConfigSourceInterceptorContext c) { + Optional url = Database.getDefaultUrl(value.get()); + + if (url.isPresent()) { + return url; + } + + return value; } - private static BiFunction getXaOrNonXaDriver() { - return (String db, ConfigSourceInterceptorContext context) -> { - ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled"); + private static Optional getXaOrNonXaDriver(Optional value, ConfigSourceInterceptorContext context) { + ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled"); - boolean isXaEnabled = xaEnabledConfigValue == null || Boolean.parseBoolean(xaEnabledConfigValue.getValue()); + boolean isXaEnabled = xaEnabledConfigValue == null || Boolean.parseBoolean(xaEnabledConfigValue.getValue()); - return Database.getDriver(db, isXaEnabled).orElse(db); - }; + Optional driver = Database.getDriver(value.get(), isXaEnabled); + + if (driver.isPresent()) { + return driver; + } + + return value; } - private static String toDatabaseKind(String db, ConfigSourceInterceptorContext context) { - Optional databaseKind = Database.getDatabaseKind(db); + private static Optional toDatabaseKind(Optional db, ConfigSourceInterceptorContext context) { + Optional databaseKind = Database.getDatabaseKind(db.get()); if (databaseKind.isPresent()) { - return databaseKind.get(); + return databaseKind; } - addInitializationException(invalidDatabaseVendor(db, Database.getAliases())); + addInitializationException(invalidDatabaseVendor(db.get(), Database.getAliases())); - return "h2"; + return of("h2"); } - private static String resolveUsername(String value, ConfigSourceInterceptorContext context) { + private static Optional resolveUsername(Optional value, ConfigSourceInterceptorContext context) { if (isDevModeDatabase(context)) { - return "sa"; + return of("sa"); } - return Database.getDatabaseKind(value).isEmpty() ? value : null; + return Database.getDatabaseKind(value.get()).isEmpty() ? value : null; } - private static String resolvePassword(String value, ConfigSourceInterceptorContext context) { + private static Optional resolvePassword(Optional value, ConfigSourceInterceptorContext context) { if (isDevModeDatabase(context)) { - return "password"; + return of("password"); } - return Database.getDatabaseKind(value).isEmpty() ? value : null; + return Database.getDatabaseKind(value.get()).isEmpty() ? value : null; } private static boolean isDevModeDatabase(ConfigSourceInterceptorContext context) { @@ -135,13 +145,19 @@ final class DatabasePropertyMappers { return Database.getDatabaseKind(db).get().equals(DatabaseKind.H2); } - private static String transformDialect(String db, ConfigSourceInterceptorContext context) { - Optional databaseKind = Database.getDatabaseKind(db); + private static Optional transformDialect(Optional db, ConfigSourceInterceptorContext context) { + Optional databaseKind = Database.getDatabaseKind(db.get()); if (databaseKind.isEmpty()) { return db; } - return Database.getDialect(db).orElse(Database.getDialect("dev-file").get()); + Optional dialect = Database.getDialect(db.get()); + + if (dialect.isPresent()) { + return dialect; + } + + return Database.getDialect("dev-file"); } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java index e184d20da8..d3a3f8e611 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java @@ -1,9 +1,19 @@ package org.keycloak.quarkus.runtime.configuration.mappers; +import org.keycloak.common.Profile; import org.keycloak.config.FeatureOptions; +import org.keycloak.quarkus.runtime.configuration.Configuration; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import io.smallrye.config.ConfigSourceInterceptorContext; + final class FeaturePropertyMappers { private FeaturePropertyMappers() { @@ -13,6 +23,7 @@ final class FeaturePropertyMappers { return new PropertyMapper[] { fromOption(FeatureOptions.FEATURES) .paramLabel("feature") + .transformer(FeaturePropertyMappers::transformFeatures) .build(), fromOption(FeatureOptions.FEATURES_DISABLED) .paramLabel("feature") @@ -20,4 +31,15 @@ final class FeaturePropertyMappers { }; } + private static Optional transformFeatures(Optional features, ConfigSourceInterceptorContext context) { + if (Boolean.parseBoolean(Configuration.getRawValue("kc.storage-legacy-enabled"))) { + return features; + } + + Set featureSet = new HashSet<>(List.of(features.orElse("").split(","))); + + featureSet.add(Profile.Feature.MAP_STORAGE.name().replace('_', '-')); + + return of(String.join(",", featureSet)); + } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java index 590c532a58..bfc167be8a 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java @@ -9,7 +9,9 @@ import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider; import java.io.File; import java.nio.file.Paths; +import java.util.Optional; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers.getMapper; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; @@ -91,8 +93,8 @@ final class HttpPropertyMappers { }; } - private static String getHttpEnabledTransformer(String value, ConfigSourceInterceptorContext context) { - boolean enabled = Boolean.parseBoolean(value); + private static Optional getHttpEnabledTransformer(Optional value, ConfigSourceInterceptorContext context) { + boolean enabled = Boolean.parseBoolean(value.get()); ConfigValue proxy = context.proceed(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + "proxy"); if (Environment.isDevMode() || Environment.isImportExportMode() @@ -112,7 +114,7 @@ final class HttpPropertyMappers { } } - return enabled ? "enabled" : "disabled"; + return of(enabled ? "enabled" : "disabled"); } private static String getDefaultKeystorePathValue() { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java index 367009fdd6..64b6e94ce4 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java @@ -1,5 +1,6 @@ package org.keycloak.quarkus.runtime.configuration.mappers; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; @@ -7,6 +8,7 @@ import java.io.File; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.function.BiFunction; import java.util.logging.Level; import java.util.stream.Collectors; @@ -29,12 +31,7 @@ public final class LoggingPropertyMappers { fromOption(LoggingOptions.LOG_CONSOLE_OUTPUT) .to("quarkus.log.console.json") .paramLabel("default|json") - .transformer((value, context) -> { - if(value.equals(LoggingOptions.DEFAULT_CONSOLE_OUTPUT.name().toLowerCase(Locale.ROOT))) { - return Boolean.FALSE.toString(); - } - return Boolean.TRUE.toString(); - }) + .transformer(LoggingPropertyMappers::resolveLogConsoleOutput) .build(), fromOption(LoggingOptions.LOG_CONSOLE_FORMAT) .to("quarkus.log.console.format") @@ -71,38 +68,40 @@ public final class LoggingPropertyMappers { }; } - private static BiFunction resolveLogHandler(String handler) { + private static BiFunction, ConfigSourceInterceptorContext, Optional> resolveLogHandler(String handler) { 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(LoggingOptions.DEFAULT_LOG_HANDLER.name()) ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + String handlers = parentValue.get(); - if(parentValue.isBlank()) { + if(handlers.isBlank()) { addInitializationException(Messages.emptyValueForKey("log")); - return consoleDependantErrorResult; + return of(consoleDependantErrorResult); } - String[] logHandlerValues = parentValue.split(","); + String[] logHandlerValues = handlers.split(","); List availableLogHandlers = Arrays.stream(LoggingOptions.Handler.values()).map(h -> h.name()).collect(Collectors.toList()); if (!availableLogHandlers.containsAll(List.of(logHandlerValues))) { - addInitializationException(Messages.notRecognizedValueInList("log", parentValue, String.join(",", availableLogHandlers))); - return consoleDependantErrorResult; + addInitializationException(Messages.notRecognizedValueInList("log", handlers, String.join(",", availableLogHandlers))); + return of(consoleDependantErrorResult); } for (String handlerInput : logHandlerValues) { if (handlerInput.equals(handler)) { - return Boolean.TRUE.toString(); + return of(Boolean.TRUE.toString()); } } - return Boolean.FALSE.toString(); + return of(Boolean.FALSE.toString()); }; } - private static String resolveFileLogLocation(String value, ConfigSourceInterceptorContext configSourceInterceptorContext) { - if (value.endsWith(File.separator)) { - return value + LoggingOptions.DEFAULT_LOG_FILENAME; + private static Optional resolveFileLogLocation(Optional value, ConfigSourceInterceptorContext configSourceInterceptorContext) { + String location = value.get(); + + if (location.endsWith(File.separator)) { + return of(location + LoggingOptions.DEFAULT_LOG_FILENAME); } return value; @@ -116,10 +115,10 @@ public final class LoggingPropertyMappers { LogContext.getLogContext().getLogger(category).setLevel(toLevel(level)); } - private static String resolveLogLevel(String value, ConfigSourceInterceptorContext configSourceInterceptorContext) { - String rootLevel = LoggingOptions.DEFAULT_LOG_LEVEL.name(); + private static Optional resolveLogLevel(Optional value, ConfigSourceInterceptorContext configSourceInterceptorContext) { + Optional rootLevel = of(LoggingOptions.DEFAULT_LOG_LEVEL.name()); - for (String level : value.split(",")) { + for (String level : value.get().split(",")) { String[] parts = level.split(":"); String category = null; String categoryLevel; @@ -144,7 +143,7 @@ public final class LoggingPropertyMappers { } if (category == null) { - rootLevel = levelType.getName(); + rootLevel = of(levelType.getName()); } else { setCategoryLevel(category, levelType.getName()); } @@ -152,4 +151,12 @@ public final class LoggingPropertyMappers { return rootLevel; } + + private static Optional resolveLogConsoleOutput(Optional value, ConfigSourceInterceptorContext context) { + if (value.get().equals(LoggingOptions.DEFAULT_CONSOLE_OUTPUT.name().toLowerCase(Locale.ROOT))) { + return of(Boolean.FALSE.toString()); + } + + return of(Boolean.TRUE.toString()); + } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java index 0f6e392cc2..2ea80feccd 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java @@ -16,6 +16,7 @@ */ package org.keycloak.quarkus.runtime.configuration.mappers; +import static java.util.Optional.ofNullable; import static org.keycloak.quarkus.runtime.Environment.isRebuild; import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR; import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR_CHAR; @@ -23,6 +24,7 @@ import static org.keycloak.quarkus.runtime.configuration.Configuration.toCliForm import static org.keycloak.quarkus.runtime.configuration.Configuration.toEnvVarFormat; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -52,14 +54,14 @@ public class PropertyMapper { private final Option option; private final String to; - private final BiFunction mapper; + private final BiFunction, ConfigSourceInterceptorContext, Optional> mapper; private final String mapFrom; private final boolean mask; private final String paramLabel; private final String envVarFormat; private String cliFormat; - PropertyMapper(Option option, String to, BiFunction mapper, + PropertyMapper(Option option, String to, BiFunction, ConfigSourceInterceptorContext, Optional> mapper, String mapFrom, String paramLabel, boolean mask) { this.option = option; this.to = to == null ? getFrom() : to; @@ -71,7 +73,7 @@ public class PropertyMapper { this.envVarFormat = toEnvVarFormat(getFrom()); } - private static String defaultTransformer(String value, ConfigSourceInterceptorContext context) { + private static Optional defaultTransformer(Optional value, ConfigSourceInterceptorContext context) { return value; } @@ -111,19 +113,21 @@ public class PropertyMapper { } if (parentValue != null) { - return transformValue(parentValue.getValue(), context); + return transformValue(ofNullable(parentValue.getValue()), context); } } - if (this.option.getDefaultValue().isPresent()) { - return transformValue(this.option.getDefaultValue().get().toString(), context); + ConfigValue defaultValue = transformValue(this.option.getDefaultValue().map(Objects::toString), context); + + if (defaultValue != null) { + return defaultValue; } // now tries any defaults from quarkus ConfigValue current = context.proceed(name); if (current != null) { - return transformValue(current.getValue(), context); + return transformValue(ofNullable(current.getValue()), context); } return current; @@ -133,7 +137,7 @@ public class PropertyMapper { return config; } - ConfigValue value = transformValue(config.getValue(), context); + ConfigValue value = transformValue(ofNullable(config.getValue()), context); // we always fallback to the current value from the property we are mapping if (value == null) { @@ -193,29 +197,29 @@ public class PropertyMapper { return mask; } - private ConfigValue transformValue(String value, ConfigSourceInterceptorContext context) { + private ConfigValue transformValue(Optional value, ConfigSourceInterceptorContext context) { if (value == null) { return null; } if (mapper == null) { - return ConfigValue.builder().withName(to).withValue(value).build(); + return ConfigValue.builder().withName(to).withValue(value.orElse(null)).build(); } - String mappedValue = mapper.apply(value, context); + Optional mappedValue = mapper.apply(value, context); - if (mappedValue != null) { - return ConfigValue.builder().withName(to).withValue(mappedValue).build(); + if (mappedValue == null || mappedValue.isEmpty()) { + return null; } - return null; + return ConfigValue.builder().withName(to).withValue(mappedValue.get()).build(); } public static class Builder { private final Option option; private String to; - private BiFunction mapper; + private BiFunction, ConfigSourceInterceptorContext, Optional> mapper; private String mapFrom = null; private boolean isMasked = false; private String paramLabel; @@ -229,7 +233,7 @@ public class PropertyMapper { return this; } - public Builder transformer(BiFunction mapper) { + public Builder transformer(BiFunction, ConfigSourceInterceptorContext, Optional> mapper) { this.mapper = mapper; return this; } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java index fc9aad9885..efc0f46bca 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/ProxyPropertyMappers.java @@ -2,8 +2,9 @@ package org.keycloak.quarkus.runtime.configuration.mappers; import io.smallrye.config.ConfigSourceInterceptorContext; -import java.util.function.BiFunction; +import java.util.Optional; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException; @@ -16,12 +17,12 @@ final class ProxyPropertyMappers { public static PropertyMapper[] getProxyPropertyMappers() { return new PropertyMapper[] { - fromOption(ProxyOptions.proxy) + fromOption(ProxyOptions.PROXY) .to("quarkus.http.proxy.proxy-address-forwarding") .transformer(ProxyPropertyMappers::getValidProxyModeValue) .paramLabel("mode") .build(), - fromOption(ProxyOptions.proxyForwardedHost) + fromOption(ProxyOptions.PROXY_FORWARDED_HOST) .to("quarkus.http.proxy.enable-forwarded-host") .mapFrom("proxy") .transformer(ProxyPropertyMappers::getResolveEnableForwardedHost) @@ -29,22 +30,24 @@ final class ProxyPropertyMappers { }; } - private static String getValidProxyModeValue(String mode, ConfigSourceInterceptorContext context) { + private static Optional getValidProxyModeValue(Optional value, ConfigSourceInterceptorContext context) { + String mode = value.get(); + switch (mode) { case "none": - return "false"; + return of(Boolean.FALSE.toString()); case "edge": case "reencrypt": case "passthrough": - return "true"; + return of(Boolean.TRUE.toString()); default: addInitializationException(Messages.invalidProxyMode(mode)); - return "false"; + return of(Boolean.FALSE.toString()); } } - private static String getResolveEnableForwardedHost(String proxy, ConfigSourceInterceptorContext context) { - return String.valueOf(!"none".equals(proxy)); + private static Optional getResolveEnableForwardedHost(Optional proxy, ConfigSourceInterceptorContext context) { + return of(String.valueOf(!ProxyOptions.Mode.none.name().equals(proxy))); } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java index 60f18c964b..eb3f24598b 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java @@ -17,10 +17,11 @@ package org.keycloak.quarkus.runtime.configuration.mappers; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; +import java.util.Optional; import org.keycloak.config.StorageOptions; -import org.keycloak.quarkus.runtime.configuration.Configuration; import io.smallrye.config.ConfigSourceInterceptorContext; @@ -30,10 +31,161 @@ final class StoragePropertyMappers { public static PropertyMapper[] getMappers() { return new PropertyMapper[] { - fromOption(StorageOptions.DEFAULT_PERSISTENCE_UNIT_ENABLED) - .to("kc.spi-connections-jpa-quarkus-enabled") + fromOption(StorageOptions.STORAGE_LEGACY_ENABLED) + .to("kc.spi-connections-jpa-legacy-enabled") + .mapFrom("storage") .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) - .build() + .transformer(StoragePropertyMappers::isDefaultPersistenceUnitEnabled) + .build(), + fromOption(StorageOptions.STORAGE) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_PROVIDER) + .mapFrom("storage") + .to("kc.spi-map-storage-provider") + .transformer(StoragePropertyMappers::resolveStorageProvider) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_REALM) + .to("kc.spi-realm-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_CLIENT) + .to("kc.spi-client-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_CLIENT_SCOPE) + .to("kc.spi-client-scope-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_GROUP) + .to("kc.spi-group-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_ROLE) + .to("kc.spi-role-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_USER) + .to("kc.spi-user-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_DEPLOYMENT_STATE) + .to("kc.spi-deployment-state-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_AUTH_SESSION) + .to("kc.spi-authentication-sessions-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getCacheStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_USER_SESSION) + .to("kc.spi-user-sessions-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getCacheStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_LOGIN_FAILURE) + .to("kc.spi-login-failure-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getCacheStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_USER_SESSION_PERSISTER) + .to("kc.spi-user-session-persister-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getUserSessionPersisterStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_AUTHORIZATION_PERSISTER) + .to("kc.spi-authorization-persister-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getAreaStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_ACTION_TOKEN) + .to("kc.spi-action-token-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getCacheStorage) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_DBLOCK) + .to("kc.spi-dblock-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getDbLockProvider) + .paramLabel("type") + .build(), + fromOption(StorageOptions.STORAGE_CACHE_REALM_ENABLED) + .to("kc.spi-realm-cache-default-enabled") + .mapFrom("storage") + .transformer(StoragePropertyMappers::isCacheAreaEnabledForStorage) + .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) + .build(), + fromOption(StorageOptions.STORAGE_CACHE_AUTHORIZATION_ENABLED) + .to("kc.spi-authorization-cache-default-enabled") + .mapFrom("storage") + .transformer(StoragePropertyMappers::isCacheAreaEnabledForStorage) + .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) + .build(), + fromOption(StorageOptions.STORAGE_CACHE_USER_ENABLED) + .to("kc.spi-user-cache-default-enabled") + .mapFrom("storage") + .transformer(StoragePropertyMappers::isCacheAreaEnabledForStorage) + .paramLabel(Boolean.TRUE + "|" + Boolean.FALSE) + .build(), + fromOption(StorageOptions.STORAGE_SINGLE_USE_OBJECT) + .to("kc.spi-single-use-object-provider") + .mapFrom("storage") + .transformer(StoragePropertyMappers::getCacheStorage) + .paramLabel("type") + .build(), }; } + + private static Optional getAreaStorage(Optional storage, ConfigSourceInterceptorContext context) { + return of("legacy".equals(storage.orElse(null)) ? "jpa" : "map"); + } + + private static Optional getCacheStorage(Optional storage, ConfigSourceInterceptorContext context) { + return of("legacy".equals(storage.orElse(null)) ? "infinispan" : "map"); + } + + private static Optional getDbLockProvider(Optional storage, ConfigSourceInterceptorContext context) { + return of("legacy".equals(storage.orElse(null)) ? "jpa" : "none"); + } + + private static Optional getUserSessionPersisterStorage(Optional storage, ConfigSourceInterceptorContext context) { + return of("legacy".equals(storage.orElse(null)) ? "jpa" : "disabled"); + } + + private static Optional isDefaultPersistenceUnitEnabled(Optional value, ConfigSourceInterceptorContext context) { + if (value.get().equals(StorageOptions.StorageType.legacy.name())) { + return of(Boolean.TRUE.toString()); + } + + return of(Boolean.valueOf(value.get()).toString()); + } + + private static Optional resolveStorageProvider(Optional value, ConfigSourceInterceptorContext context) { + return Optional.ofNullable("legacy".equals(value.orElse(null)) ? null : "concurrenthashmap"); + } + + private static Optional isCacheAreaEnabledForStorage(Optional storage, ConfigSourceInterceptorContext context) { + return of("legacy".equals(storage.orElse(null)) ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); + } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java index 1ce5def338..2164389596 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/TransactionPropertyMappers.java @@ -3,8 +3,11 @@ package org.keycloak.quarkus.runtime.configuration.mappers; import io.smallrye.config.ConfigSourceInterceptorContext; import org.keycloak.config.TransactionOptions; +import static java.util.Optional.of; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; +import java.util.Optional; + public class TransactionPropertyMappers { private TransactionPropertyMappers(){} @@ -19,14 +22,14 @@ public class TransactionPropertyMappers { }; } - private static String getQuarkusTransactionsValue(String txValue, ConfigSourceInterceptorContext context) { - boolean isXaEnabled = Boolean.parseBoolean(txValue); + private static Optional getQuarkusTransactionsValue(Optional txValue, ConfigSourceInterceptorContext context) { + boolean isXaEnabled = Boolean.parseBoolean(txValue.get()); if (isXaEnabled) { - return "xa"; + return of("xa"); } - return "enabled"; + return of("enabled"); } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java index dd8a1acb18..00eaac43c2 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java @@ -21,15 +21,9 @@ import java.util.Set; import java.util.stream.Collectors; import javax.ws.rs.ApplicationPath; - -import org.keycloak.Config; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.KeycloakTransactionManager; +import org.keycloak.exportimport.ExportImportManager; import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; -import org.keycloak.services.ServicesLogger; -import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.quarkus.runtime.services.resources.QuarkusWelcomeResource; import org.keycloak.services.resources.WelcomeResource; @@ -37,17 +31,22 @@ import org.keycloak.services.resources.WelcomeResource; @ApplicationPath("/") public class QuarkusKeycloakApplication extends KeycloakApplication { - private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN"; - private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; - private static boolean filterSingletons(Object o) { return !WelcomeResource.class.isInstance(o); } @Override protected void startup() { - initializeKeycloakSessionFactory(); - createAdminUser(); + QuarkusKeycloakSessionFactory instance = QuarkusKeycloakSessionFactory.getInstance(); + sessionFactory = instance; + instance.init(); + ExportImportManager exportImportManager = bootstrap(); + + if (exportImportManager.isRunExport()) { + exportImportManager.runExport(); + } + + sessionFactory.publish(new PostMigrationEvent(sessionFactory)); } @Override @@ -60,42 +59,4 @@ public class QuarkusKeycloakApplication extends KeycloakApplication { return singletons; } - - private void initializeKeycloakSessionFactory() { - QuarkusKeycloakSessionFactory instance = QuarkusKeycloakSessionFactory.getInstance(); - sessionFactory = instance; - instance.init(); - sessionFactory.publish(new PostMigrationEvent(sessionFactory)); - } - - private void createAdminUser() { - String adminUserName = System.getenv(KEYCLOAK_ADMIN_ENV_VAR); - String adminPassword = System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR); - - if ((adminUserName == null || adminUserName.trim().length() == 0) - || (adminPassword == null || adminPassword.trim().length() == 0)) { - return; - } - - KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); - KeycloakSession session = sessionFactory.create(); - KeycloakTransactionManager transaction = session.getTransactionManager(); - - try { - transaction.begin(); - - new ApplianceBootstrap(session).createMasterRealmUser(adminUserName, adminPassword); - ServicesLogger.LOGGER.addUserSuccess(adminUserName, Config.getAdminRealm()); - - transaction.commit(); - } catch (IllegalStateException e) { - session.getTransactionManager().rollback(); - ServicesLogger.LOGGER.addUserFailedUserExists(adminUserName, Config.getAdminRealm()); - } catch (Throwable t) { - session.getTransactionManager().rollback(); - ServicesLogger.LOGGER.addUserFailed(t, adminUserName, Config.getAdminRealm()); - } finally { - session.close(); - } - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java similarity index 55% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java index aa18698fc4..9397213337 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/database/LegacyJpaConnectionProviderFactory.java @@ -15,18 +15,13 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.database.jpa; +package org.keycloak.quarkus.runtime.storage.legacy.database; import static org.keycloak.connections.jpa.util.JpaUtils.configureNamedQuery; -import static org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusJpaUpdaterProvider.VERIFY_AND_RUN_MASTER_CHANGELOG; +import static org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusJpaUpdaterProvider.VERIFY_AND_RUN_MASTER_CHANGELOG; import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -35,60 +30,40 @@ import java.sql.Statement; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.StringTokenizer; import javax.enterprise.inject.Instance; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; -import javax.transaction.SystemException; -import javax.transaction.Transaction; - -import com.fasterxml.jackson.core.type.TypeReference; import io.quarkus.arc.Arc; -import io.quarkus.runtime.Quarkus; import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.ServerStartupError; import org.keycloak.common.Version; -import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.connections.jpa.DefaultJpaConnectionProvider; import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.connections.jpa.updater.JpaUpdaterProvider; import org.keycloak.connections.jpa.util.JpaUtils; -import org.keycloak.exportimport.ExportImportManager; import org.keycloak.migration.MigrationModelManager; import org.keycloak.migration.ModelVersion; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserProvider; import org.keycloak.models.dblock.DBLockManager; import org.keycloak.models.dblock.DBLockProvider; -import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigurationBuilder; import org.keycloak.provider.ServerInfoAwareProviderFactory; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.services.ServicesLogger; -import org.keycloak.services.managers.ApplianceBootstrap; -import org.keycloak.services.managers.RealmManager; -import org.keycloak.transaction.JtaTransactionManagerLookup; import org.keycloak.quarkus.runtime.Environment; -import org.keycloak.util.JsonSerialization; +import org.keycloak.quarkus.runtime.storage.database.jpa.AbstractJpaConnectionProviderFactory; /** * @author Stian Thorgersen */ -public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionProviderFactory implements ServerInfoAwareProviderFactory { +public class LegacyJpaConnectionProviderFactory extends AbstractJpaConnectionProviderFactory implements ServerInfoAwareProviderFactory { public static final String QUERY_PROPERTY_PREFIX = "kc.query."; - private static final Logger logger = Logger.getLogger(QuarkusJpaConnectionProviderFactory.class); + private static final Logger logger = Logger.getLogger(LegacyJpaConnectionProviderFactory.class); private static final String SQL_GET_LATEST_VERSION = "SELECT ID, VERSION FROM %sMIGRATION_MODEL ORDER BY UPDATE_TIME DESC"; enum MigrationStrategy { @@ -96,7 +71,6 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr } private Map operationalInfo; - private KeycloakSessionFactory factory; @Override public JpaConnectionProvider create(KeycloakSession session) { @@ -106,7 +80,7 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr @Override public String getId() { - return "quarkus"; + return "legacy"; } private void addSpecificNamedQueries(KeycloakSession session) { @@ -128,7 +102,6 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr @Override public void postInit(KeycloakSessionFactory factory) { super.postInit(factory); - this.factory = factory; KeycloakSession session = factory.create(); String id = null; @@ -160,8 +133,6 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr if (schemaChanged || Environment.isImportExportMode()) { runJobInTransaction(factory, this::initSchema); - } else if (System.getProperty("keycloak.import") != null) { - importRealms(); } else { Version.RESOURCES_VERSION = id; } @@ -227,8 +198,6 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr } private void initSchema(KeycloakSession session) { - ExportImportManager exportImportManager = new ExportImportManager(session); - /* * Migrate model is executed just in case following providers are "jpa". * In Map Storage, there is an assumption that migrateModel is not needed. @@ -240,75 +209,6 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr logger.debug("Calling migrateModel"); migrateModel(session); } - - DBLockManager dbLockManager = new DBLockManager(session); - dbLockManager.checkForcedUnlock(); - DBLockProvider dbLock = dbLockManager.getDBLock(); - dbLock.waitForLock(DBLockProvider.Namespace.KEYCLOAK_BOOT); - try { - createMasterRealm(exportImportManager); - } finally { - dbLock.releaseLock(); - } - if (exportImportManager.isRunExport()) { - exportImportManager.runExport(); - Quarkus.asyncExit(); - } - } - - private ExportImportManager createMasterRealm(ExportImportManager exportImportManager) { - logger.debug("bootstrap"); - KeycloakSession session = factory.create(); - - try { - session.getTransactionManager().begin(); - - if (xaEnabled) { - JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup) factory - .getProviderFactory(JtaTransactionManagerLookup.class); - - try { - Transaction transaction = lookup.getTransactionManager().getTransaction(); - logger.debugv("bootstrap current transaction? {0}", transaction != null); - if (transaction != null) { - logger.debugv("bootstrap current transaction status? {0}", transaction.getStatus()); - } - } catch (SystemException e) { - throw new RuntimeException(e); - } - } - - ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session); - boolean createMasterRealm = applianceBootstrap.isNewInstall(); - - if (exportImportManager.isRunImport() && exportImportManager.isImportMasterIncluded()) { - createMasterRealm = false; - } - - if (createMasterRealm) { - applianceBootstrap.createMasterRealm(); - } - - session.getTransactionManager().commit(); - } catch (RuntimeException re) { - if (session.getTransactionManager().isActive()) { - session.getTransactionManager().rollback(); - } - throw re; - } finally { - session.close(); - } - - if (exportImportManager.isRunImport()) { - exportImportManager.runImport(); - Quarkus.asyncExit(); - } else { - importRealms(); - } - - importAddUser(); - - return exportImportManager; } private void migrateModel(KeycloakSession session) { @@ -319,131 +219,6 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr } } - private void importRealms() { - String files = System.getProperty("keycloak.import"); - if (files != null) { - StringTokenizer tokenizer = new StringTokenizer(files, ","); - while (tokenizer.hasMoreTokens()) { - String file = tokenizer.nextToken().trim(); - RealmRepresentation rep; - try { - Path filePath = Paths.get(file); - - if (!(Files.exists(filePath) && Files.isRegularFile(filePath) && filePath.toString().endsWith(".json"))) { - logger.debugf("Ignoring import file because it is not a valid file: %s", file); - continue; - } - - rep = JsonSerialization.readValue(StringPropertyReplacer.replaceProperties( - Files.readString(filePath), new StringPropertyReplacer.PropertyResolver() { - @Override - public String resolve(String property) { - return Optional.ofNullable(System.getenv(property)).orElse(null); - } - }), RealmRepresentation.class); - } catch (Exception cause) { - throw new RuntimeException("Failed to parse realm configuration file: " + file, cause); - } - importRealm(rep, "file " + file); - } - } - } - - private void importRealm(RealmRepresentation rep, String from) { - KeycloakSession session = factory.create(); - boolean exists = false; - try { - session.getTransactionManager().begin(); - - try { - RealmManager manager = new RealmManager(session); - - if (rep.getId() != null && manager.getRealm(rep.getId()) != null) { - ServicesLogger.LOGGER.realmExists(rep.getRealm(), from); - exists = true; - } - - if (!exists && manager.getRealmByName(rep.getRealm()) != null) { - ServicesLogger.LOGGER.realmExists(rep.getRealm(), from); - exists = true; - } - if (!exists) { - RealmModel realm = manager.importRealm(rep); - ServicesLogger.LOGGER.importedRealm(realm.getName(), from); - } - session.getTransactionManager().commit(); - } catch (Throwable cause) { - session.getTransactionManager().rollback(); - if (!exists) { - throw new RuntimeException("Failed to import realm: " + rep.getRealm(), cause); - } - } - } finally { - session.close(); - } - } - - private void importAddUser() { - String configDir = System.getProperty("jboss.server.config.dir"); - if (configDir != null) { - File addUserFile = new File(configDir + File.separator + "keycloak-add-user.json"); - if (addUserFile.isFile()) { - ServicesLogger.LOGGER.imprtingUsersFrom(addUserFile); - - List realms; - try { - realms = JsonSerialization - .readValue(new FileInputStream(addUserFile), new TypeReference>() { - }); - } catch (IOException e) { - ServicesLogger.LOGGER.failedToLoadUsers(e); - return; - } - - for (RealmRepresentation realmRep : realms) { - for (UserRepresentation userRep : realmRep.getUsers()) { - KeycloakSession session = factory.create(); - - try { - session.getTransactionManager().begin(); - RealmModel realm = session.realms().getRealmByName(realmRep.getRealm()); - - if (realm == null) { - ServicesLogger.LOGGER.addUserFailedRealmNotFound(userRep.getUsername(), realmRep.getRealm()); - } - - UserProvider users = session.users(); - - if (users.getUserByUsername(realm, userRep.getUsername()) != null) { - ServicesLogger.LOGGER.notCreatingExistingUser(userRep.getUsername()); - } else { - UserModel user = users.addUser(realm, userRep.getUsername()); - user.setEnabled(userRep.isEnabled()); - RepresentationToModel.createCredentials(userRep, session, realm, user, false); - RepresentationToModel.createRoleMappings(userRep, user, realm); - ServicesLogger.LOGGER.addUserSuccess(userRep.getUsername(), realmRep.getRealm()); - } - - session.getTransactionManager().commit(); - } catch (ModelDuplicateException e) { - session.getTransactionManager().rollback(); - ServicesLogger.LOGGER.addUserFailedUserExists(userRep.getUsername(), realmRep.getRealm()); - } catch (Throwable t) { - session.getTransactionManager().rollback(); - ServicesLogger.LOGGER.addUserFailed(t, userRep.getUsername(), realmRep.getRealm()); - } finally { - session.close(); - } - } - } - - if (!addUserFile.delete()) { - ServicesLogger.LOGGER.failedToDeleteFile(addUserFile.getAbsolutePath()); - } - } - } - } - private String getSchema(String schema) { return schema == null ? "" : schema + "."; } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java similarity index 98% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java index 5f977b3f82..8d9a096983 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.infinispan; +package org.keycloak.quarkus.runtime.storage.legacy.infinispan; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/QuarkusCacheManagerProvider.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusCacheManagerProvider.java similarity index 94% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/QuarkusCacheManagerProvider.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusCacheManagerProvider.java index ae46deca05..a5f5a546e2 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/QuarkusCacheManagerProvider.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusCacheManagerProvider.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.infinispan; +package org.keycloak.quarkus.runtime.storage.legacy.infinispan; import org.keycloak.cluster.ManagedCacheManagerProvider; import org.keycloak.Config; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/QuarkusInfinispanConnectionFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java similarity index 51% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/QuarkusInfinispanConnectionFactory.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java index 7af81465da..a29da321e5 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/QuarkusInfinispanConnectionFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/QuarkusInfinispanConnectionFactory.java @@ -1,23 +1,21 @@ /* + * Copyright 2021 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. * - * * Copyright 2021 Red Hat, Inc. and/or its affiliates - * * and other contributors as indicated by the @author tags. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.infinispan; +package org.keycloak.quarkus.runtime.storage.legacy.infinispan; import org.infinispan.manager.EmbeddedCacheManager; import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusJpaUpdaterProvider.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusJpaUpdaterProvider.java similarity index 99% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusJpaUpdaterProvider.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusJpaUpdaterProvider.java index 2eb00d15d4..762a07455e 100755 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusJpaUpdaterProvider.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusJpaUpdaterProvider.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.database.liquibase; +package org.keycloak.quarkus.runtime.storage.legacy.liquibase; import java.io.File; import java.io.FileWriter; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusJpaUpdaterProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusJpaUpdaterProviderFactory.java similarity index 96% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusJpaUpdaterProviderFactory.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusJpaUpdaterProviderFactory.java index 284a8245c6..de44ffe41a 100755 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusJpaUpdaterProviderFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusJpaUpdaterProviderFactory.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.database.liquibase; +package org.keycloak.quarkus.runtime.storage.legacy.liquibase; import org.keycloak.Config; import org.keycloak.connections.jpa.updater.JpaUpdaterProvider; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusLiquibaseConnectionProvider.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusLiquibaseConnectionProvider.java similarity index 98% rename from quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusLiquibaseConnectionProvider.java rename to quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusLiquibaseConnectionProvider.java index 5ab0ea2a69..d0166eaaed 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/liquibase/QuarkusLiquibaseConnectionProvider.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/liquibase/QuarkusLiquibaseConnectionProvider.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.quarkus.runtime.storage.database.liquibase; +package org.keycloak.quarkus.runtime.storage.legacy.liquibase; import java.lang.reflect.Method; import java.sql.Connection; diff --git a/quarkus/runtime/src/main/resources/META-INF/keycloak.conf b/quarkus/runtime/src/main/resources/META-INF/keycloak.conf index e308abbe9d..1cda82b812 100644 --- a/quarkus/runtime/src/main/resources/META-INF/keycloak.conf +++ b/quarkus/runtime/src/main/resources/META-INF/keycloak.conf @@ -24,4 +24,10 @@ metrics-enabled=false #logging defaults log-console-output=default -log-file=${kc.home.dir:default}data/log/keycloak.log \ No newline at end of file +log-file=${kc.home.dir:default}data/log/keycloak.log + +# Storage defaults +spi-map-storage-concurrenthashmap-dir=${kc.home.dir:default}data/chm +spi-map-storage-concurrenthashmap-key-type-single-use-objects=string +spi-map-storage-concurrenthashmap-key-type-realms=string +spi-map-storage-concurrenthashmap-key-type-authz-resource-servers=string \ No newline at end of file diff --git a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.cluster.ManagedCacheManagerProvider b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.cluster.ManagedCacheManagerProvider index a0802aeb78..a5bd1cdbc8 100644 --- a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.cluster.ManagedCacheManagerProvider +++ b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.cluster.ManagedCacheManagerProvider @@ -1 +1 @@ -org.keycloak.quarkus.runtime.storage.infinispan.QuarkusCacheManagerProvider \ No newline at end of file +org.keycloak.quarkus.runtime.storage.legacy.infinispan.QuarkusCacheManagerProvider \ No newline at end of file diff --git a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory index fec4d5c4bd..b0480f6044 100644 --- a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory +++ b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory @@ -17,4 +17,4 @@ # */ # -org.keycloak.quarkus.runtime.storage.infinispan.QuarkusInfinispanConnectionFactory \ No newline at end of file +org.keycloak.quarkus.runtime.storage.legacy.infinispan.QuarkusInfinispanConnectionFactory \ No newline at end of file diff --git a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory index 71601689ff..26387b9b96 100644 --- a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory +++ b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.JpaConnectionProviderFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory +org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory diff --git a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory index 6e984000de..9881d369b8 100644 --- a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory +++ b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusJpaUpdaterProviderFactory \ No newline at end of file +org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusJpaUpdaterProviderFactory \ No newline at end of file diff --git a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory index d12cc0bfe9..3bd6e9d61a 100644 --- a/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory +++ b/quarkus/runtime/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusLiquibaseConnectionProvider \ No newline at end of file +org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusLiquibaseConnectionProvider \ No newline at end of file diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/ConfigurationTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/ConfigurationTest.java index 072db49bd7..50b24b642d 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/ConfigurationTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/ConfigurationTest.java @@ -38,6 +38,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.keycloak.Config; +import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory; import org.keycloak.quarkus.runtime.configuration.KeycloakConfigSourceProvider; import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider; @@ -128,6 +129,11 @@ public class ConfigurationTest { assertEquals("debug", createConfig().getRawValue("kc.log-level")); } + @Test + public void testSanitizeKey() { + assertEquals("string", initConfig("map-storage", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID).get("keyType.realms")); + } + @Test public void testEnvVarAvailableFromPropertyNames() { putEnvVar("KC_VAULT_DIR", "/foo/bar"); diff --git a/quarkus/tests/integration/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java b/quarkus/tests/integration/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java index ee75431a35..261535618d 100644 --- a/quarkus/tests/integration/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java +++ b/quarkus/tests/integration/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java @@ -88,14 +88,14 @@ public class CLITestExtension extends QuarkusMainTestExtension { if (distConfig != null) { onKeepServerAlive(context.getRequiredTestMethod().getAnnotation(KeepServerAlive.class)); + if (dist == null) { + dist = createDistribution(distConfig); + } + + onBeforeStartDistribution(context.getRequiredTestClass().getAnnotation(BeforeStartDistribution.class)); + onBeforeStartDistribution(context.getRequiredTestMethod().getAnnotation(BeforeStartDistribution.class)); + if (launch != null) { - if (dist == null) { - dist = createDistribution(distConfig); - } - - onBeforeStartDistribution(context.getRequiredTestClass().getAnnotation(BeforeStartDistribution.class)); - onBeforeStartDistribution(context.getRequiredTestMethod().getAnnotation(BeforeStartDistribution.class)); - result = dist.run(Arrays.asList(launch.value())); } } else { @@ -226,7 +226,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { Class type = parameterContext.getParameter().getType(); - return type == LaunchResult.class || type == RawDistRootPath.class || (dist != null && type == KeycloakDistribution.class); + return type == LaunchResult.class || type == RawDistRootPath.class || type == KeycloakDistribution.class; } private void configureProfile(ExtensionContext context) { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java index 0907856db3..6008e58df3 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java @@ -44,7 +44,7 @@ public class ImportAtStartupDistTest { @Test @BeforeStartDistribution(CreateRealmConfigurationFileAndDir.class) - @Launch({"start-dev", "--import-realm", "--log-level=org.keycloak.quarkus.runtime.storage.database.jpa:debug"}) + @Launch({"start-dev", "--import-realm", "--log-level=org.keycloak.services.resources.KeycloakApplication:debug"}) void testImportAndIgnoreDirectory(LaunchResult result) { CLIResult cliResult = (CLIResult) result; cliResult.assertMessage("Imported realm quickstart-realm from file"); @@ -53,7 +53,7 @@ public class ImportAtStartupDistTest { @Test @BeforeStartDistribution(CreateRealmConfigurationFileWithUnsupportedExtension.class) - @Launch({"start-dev", "--import-realm", "--log-level=org.keycloak.quarkus.runtime.storage.database.jpa:debug"}) + @Launch({"start-dev", "--import-realm", "--log-level=org.keycloak.services.resources.KeycloakApplication:debug"}) void testIgnoreFileWithUnsupportedExtension(LaunchResult result) { CLIResult cliResult = (CLIResult) result; cliResult.assertMessage("Ignoring import file because it is not a valid file"); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java index 8d1700ed9d..d7dbd9e368 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java @@ -49,7 +49,7 @@ public class LoggingDistTest { @Launch({ "start-dev", "--log-level=debug" }) void testSetRootLevel(LaunchResult result) { CLIResult cliResult = (CLIResult) result; - assertTrue(cliResult.getOutput().contains("DEBUG [org.hibernate")); + assertTrue(cliResult.getOutput().contains("DEBUG [io.quarkus.resteasy.runtime]")); cliResult.assertStartedDevMode(); } @@ -87,7 +87,7 @@ public class LoggingDistTest { @Launch({ "start-dev", "--log-level=off,org.keycloak:warn,debug" }) void testSetLastRootLevelIfMultipleSet(LaunchResult result) { CLIResult cliResult = (CLIResult) result; - assertTrue(cliResult.getOutput().contains("DEBUG [org.hibernate")); + assertTrue(cliResult.getOutput().contains("DEBUG [io.quarkus.resteasy.runtime]")); assertFalse(cliResult.getOutput().contains("INFO [org.keycloak")); cliResult.assertStartedDevMode(); } @@ -125,7 +125,7 @@ public class LoggingDistTest { void testLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) { CLIResult cliResult = (CLIResult) result; assertFalse(cliResult.getOutput().contains("\"loggerName\":\"io.quarkus\",\"level\":\"INFO\")")); - assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory\",\"level\":\"DEBUG\"")); + assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.services.resources.KeycloakApplication\",\"level\":\"DEBUG\"")); assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.infinispan.CONTAINER\",\"level\":\"INFO\"")); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/map/ChmStorageDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/map/ChmStorageDistTest.java new file mode 100644 index 0000000000..1ad8dc3a86 --- /dev/null +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/map/ChmStorageDistTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.it.storage.map; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.keycloak.it.junit5.extension.CLIResult; +import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.utils.RawDistRootPath; + +import io.quarkus.test.junit.main.Launch; +import io.quarkus.test.junit.main.LaunchResult; + +@RawDistOnly(reason = "Need to check dist path") +@DistributionTest(reInstall = DistributionTest.ReInstall.BEFORE_TEST) +public class ChmStorageDistTest { + + @Test + @Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false", "--storage=chm" }) + void testStartUsingChmsStorage(LaunchResult result, RawDistRootPath distPath) { + CLIResult cliResult = (CLIResult) result; + assertExpectedMessages(cliResult, distPath); + cliResult.assertStarted(); + } + + @Test + @Launch({ "start-dev", "--storage=chm" }) + void testStartDevUsingChmsStorage(LaunchResult result, RawDistRootPath distPath) { + CLIResult cliResult = (CLIResult) result; + assertExpectedMessages(cliResult, distPath); + cliResult.assertStartedDevMode(); + } + + private void assertExpectedMessages(CLIResult cliResult, RawDistRootPath distPath) { + cliResult.assertMessage("Experimental feature enabled: map_storage"); + cliResult.assertMessage("Hibernate ORM is disabled because no JPA entities were found"); + Assert.assertFalse(distPath.getDistRootPath().resolve("data").resolve("h2").toFile().exists()); + } +} diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt index 7036d546fa..f123322be7 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt @@ -35,6 +35,10 @@ Cluster: 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. +Storage: + +--storage Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy. + Database: --db The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql, diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.windows.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.windows.approved.txt index 8a00931c9e..5694aeb9b0 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.windows.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.windows.approved.txt @@ -35,6 +35,10 @@ Cluster: 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. +Storage: + +--storage Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy. + Database: --db The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql, diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt index 31fe756d6e..ade8869185 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt @@ -27,6 +27,10 @@ Cluster: 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. +Storage: + +--storage Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy. + Database: --db The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql, diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt index 01aa726e77..05a290a0ce 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt @@ -27,6 +27,10 @@ Cluster: 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. +Storage: + +--storage Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy. + Database: --db The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql, diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index c13e1eb52d..09c019a960 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -22,6 +22,7 @@ import org.keycloak.Config; import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.util.Resteasy; +import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.config.ConfigProviderFactory; import org.keycloak.exportimport.ExportImportManager; import org.keycloak.models.KeycloakSession; @@ -58,12 +59,14 @@ import javax.ws.rs.core.Application; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; import java.util.StringTokenizer; @@ -137,7 +140,7 @@ public class KeycloakApplication extends Application { } } }); - + if (exportImportManager[0].isRunExport()) { exportImportManager[0].runExport(); } @@ -252,9 +255,22 @@ public class KeycloakApplication extends Application { String file = tokenizer.nextToken().trim(); RealmRepresentation rep; try { - rep = loadJson(new FileInputStream(file), RealmRepresentation.class); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); + Path filePath = Paths.get(file); + + if (!(Files.exists(filePath) && Files.isRegularFile(filePath) && filePath.toString().endsWith(".json"))) { + logger.debugf("Ignoring import file because it is not a valid file: %s", file); + continue; + } + + rep = JsonSerialization.readValue(StringPropertyReplacer.replaceProperties( + new String(Files.readAllBytes(filePath), "UTF-8"), new StringPropertyReplacer.PropertyResolver() { + @Override + public String resolve(String property) { + return Optional.ofNullable(System.getenv(property)).orElse(null); + } + }), RealmRepresentation.class); + } catch (Exception cause) { + throw new RuntimeException("Failed to parse realm configuration file: " + file, cause); } importRealm(rep, "file " + file); } @@ -354,13 +370,4 @@ public class KeycloakApplication extends Application { } } } - - private static T loadJson(InputStream is, Class type) { - try { - return JsonSerialization.readValue(is, type); - } catch (IOException e) { - throw new RuntimeException("Failed to parse json", e); - } - } - }