parent
3170efd3ad
commit
605b51890e
45 changed files with 686 additions and 443 deletions
|
@ -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() {
|
||||
|
|
|
@ -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<List> 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();
|
||||
|
||||
|
|
|
@ -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<Output> 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.")
|
||||
|
|
|
@ -14,7 +14,7 @@ public class ProxyOptions {
|
|||
passthrough;
|
||||
}
|
||||
|
||||
public static final Option<Mode> proxy = new OptionBuilder<>("proxy", Mode.class)
|
||||
public static final Option<Mode> PROXY = new OptionBuilder<>("proxy", Mode.class)
|
||||
.category(OptionCategory.PROXY)
|
||||
.description("The proxy address forwarding mode if the server is behind a reverse proxy. " +
|
||||
"Possible values are: " + String.join(",", Arrays.stream(Mode.values()).skip(1).map(m -> m.name()).collect(Collectors.joining(","))))
|
||||
|
@ -22,7 +22,7 @@ public class ProxyOptions {
|
|||
.expectedValues(Mode.values())
|
||||
.build();
|
||||
|
||||
public static final Option<Boolean> proxyForwardedHost = new OptionBuilder<>("proxy-forwarded-host", Boolean.class)
|
||||
public static final Option<Boolean> 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<Option<?>> ALL_OPTIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ALL_OPTIONS.add(proxy);
|
||||
ALL_OPTIONS.add(proxyForwardedHost);
|
||||
ALL_OPTIONS.add(PROXY);
|
||||
ALL_OPTIONS.add(PROXY_FORWARDED_HOST);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Boolean> DEFAULT_PERSISTENCE_UNIT_ENABLED = new OptionBuilder<>("storage-default-persistence-unit-enabled", Boolean.class)
|
||||
public enum StorageType {
|
||||
legacy,
|
||||
chm
|
||||
}
|
||||
|
||||
public static final Option<Boolean> STORAGE_LEGACY_ENABLED = new OptionBuilder<>("storage-legacy-enabled", Boolean.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.defaultValue(true)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<StorageType> 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<StorageType> STORAGE_PROVIDER = new OptionBuilder<>("storage-provider", StorageType.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_REALM = new OptionBuilder<>("storage-realm", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_CLIENT = new OptionBuilder<>("storage-client", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_CLIENT_SCOPE = new OptionBuilder<>("storage-client-scope", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_GROUP = new OptionBuilder<>("storage-group", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_ROLE = new OptionBuilder<>("storage-role", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_USER = new OptionBuilder<>("storage-user", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_DEPLOYMENT_STATE = new OptionBuilder<>("storage-deployment-state", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_AUTH_SESSION = new OptionBuilder<>("storage-auth-session", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_USER_SESSION = new OptionBuilder<>("storage-user-session", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_LOGIN_FAILURE = new OptionBuilder<>("storage-login-failure", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_AUTHORIZATION_PERSISTER = new OptionBuilder<>("storage-authorization-persister", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_USER_SESSION_PERSISTER = new OptionBuilder<>("storage-user-session-persister", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_ACTION_TOKEN = new OptionBuilder<>("storage-action-token", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_DBLOCK = new OptionBuilder<>("storage-dblock", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_CACHE_REALM_ENABLED = new OptionBuilder<>("cache-realm-enabled", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_CACHE_USER_ENABLED = new OptionBuilder<>("cache-user-enabled", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_SINGLE_USE_OBJECT = new OptionBuilder<>("storage-single-use-object", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_CACHE_AUTHORIZATION_ENABLED = new OptionBuilder<>("cache-authorization-enabled", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.hidden()
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final List<Option<?>> ALL_OPTIONS = List.of(STORAGE);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<PersistenceXmlDescriptorBuildItem> descriptors,
|
||||
|
@ -228,7 +228,7 @@ class KeycloakProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
@BuildStep(onlyIf = IsDefaultPersistenceUnitEnabled.class)
|
||||
@BuildStep(onlyIf = IsLegacyStoreEnabled.class)
|
||||
void produceDefaultPersistenceUnit(BuildProducer<PersistenceXmlDescriptorBuildItem> producer) {
|
||||
ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit(
|
||||
Thread.currentThread().getContextClassLoader().getResource("default-persistence.xml"));
|
||||
|
@ -522,7 +522,19 @@ class KeycloakProcessor {
|
|||
|
||||
for (Spi spi : pm.loadSpis()) {
|
||||
Map<Class<? extends Provider>, Map<String, ProviderFactory>> providers = new HashMap<>();
|
||||
List<ProviderFactory> loadedFactories = new ArrayList<>(pm.load(spi));
|
||||
String provider = Config.getProvider(spi.getName());
|
||||
List<ProviderFactory> 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<String, ProviderFactory> deployedScriptProviders = loadDeployedScriptProviders(classLoader, spi);
|
||||
|
||||
loadedFactories.addAll(deployedScriptProviders.values());
|
||||
|
|
|
@ -295,6 +295,16 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-map</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Keycloak Dependencies-->
|
||||
<dependency>
|
||||
|
|
|
@ -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<String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -131,7 +131,7 @@ public class MicroProfileConfigProvider implements Config.ConfigProvider {
|
|||
}
|
||||
|
||||
private <T> T getValue(String key, Class<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> resolveConfigFile(Optional<String> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, ConfigSourceInterceptorContext, String> getDatabaseUrl() {
|
||||
return (s, c) -> Database.getDefaultUrl(s).orElse(s);
|
||||
private static Optional<String> getDatabaseUrl(Optional<String> value, ConfigSourceInterceptorContext c) {
|
||||
Optional<String> url = Database.getDefaultUrl(value.get());
|
||||
|
||||
if (url.isPresent()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static BiFunction<String, ConfigSourceInterceptorContext, String> getXaOrNonXaDriver() {
|
||||
return (String db, ConfigSourceInterceptorContext context) -> {
|
||||
ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled");
|
||||
private static Optional<String> getXaOrNonXaDriver(Optional<String> 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<String> driver = Database.getDriver(value.get(), isXaEnabled);
|
||||
|
||||
if (driver.isPresent()) {
|
||||
return driver;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String toDatabaseKind(String db, ConfigSourceInterceptorContext context) {
|
||||
Optional<String> databaseKind = Database.getDatabaseKind(db);
|
||||
private static Optional<String> toDatabaseKind(Optional<String> db, ConfigSourceInterceptorContext context) {
|
||||
Optional<String> 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<String> resolveUsername(Optional<String> 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<String> resolvePassword(Optional<String> 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<String> databaseKind = Database.getDatabaseKind(db);
|
||||
private static Optional<String> transformDialect(Optional<String> db, ConfigSourceInterceptorContext context) {
|
||||
Optional<String> databaseKind = Database.getDatabaseKind(db.get());
|
||||
|
||||
if (databaseKind.isEmpty()) {
|
||||
return db;
|
||||
}
|
||||
|
||||
return Database.getDialect(db).orElse(Database.getDialect("dev-file").get());
|
||||
Optional<String> dialect = Database.getDialect(db.get());
|
||||
|
||||
if (dialect.isPresent()) {
|
||||
return dialect;
|
||||
}
|
||||
|
||||
return Database.getDialect("dev-file");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> transformFeatures(Optional<String> features, ConfigSourceInterceptorContext context) {
|
||||
if (Boolean.parseBoolean(Configuration.getRawValue("kc.storage-legacy-enabled"))) {
|
||||
return features;
|
||||
}
|
||||
|
||||
Set<String> featureSet = new HashSet<>(List.of(features.orElse("").split(",")));
|
||||
|
||||
featureSet.add(Profile.Feature.MAP_STORAGE.name().replace('_', '-'));
|
||||
|
||||
return of(String.join(",", featureSet));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> getHttpEnabledTransformer(Optional<String> 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() {
|
||||
|
|
|
@ -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<String, ConfigSourceInterceptorContext, String> resolveLogHandler(String handler) {
|
||||
private static BiFunction<Optional<String>, ConfigSourceInterceptorContext, Optional<String>> 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<String> 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<String> resolveFileLogLocation(Optional<String> 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<String> resolveLogLevel(Optional<String> value, ConfigSourceInterceptorContext configSourceInterceptorContext) {
|
||||
Optional<String> 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<String> resolveLogConsoleOutput(Optional<String> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T> {
|
|||
|
||||
private final Option<T> option;
|
||||
private final String to;
|
||||
private final BiFunction<String, ConfigSourceInterceptorContext, String> mapper;
|
||||
private final BiFunction<Optional<String>, ConfigSourceInterceptorContext, Optional<String>> mapper;
|
||||
private final String mapFrom;
|
||||
private final boolean mask;
|
||||
private final String paramLabel;
|
||||
private final String envVarFormat;
|
||||
private String cliFormat;
|
||||
|
||||
PropertyMapper(Option<T> option, String to, BiFunction<String, ConfigSourceInterceptorContext, String> mapper,
|
||||
PropertyMapper(Option<T> option, String to, BiFunction<Optional<String>, ConfigSourceInterceptorContext, Optional<String>> mapper,
|
||||
String mapFrom, String paramLabel, boolean mask) {
|
||||
this.option = option;
|
||||
this.to = to == null ? getFrom() : to;
|
||||
|
@ -71,7 +73,7 @@ public class PropertyMapper<T> {
|
|||
this.envVarFormat = toEnvVarFormat(getFrom());
|
||||
}
|
||||
|
||||
private static String defaultTransformer(String value, ConfigSourceInterceptorContext context) {
|
||||
private static Optional<String> defaultTransformer(Optional<String> value, ConfigSourceInterceptorContext context) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -111,19 +113,21 @@ public class PropertyMapper<T> {
|
|||
}
|
||||
|
||||
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<T> {
|
|||
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<T> {
|
|||
return mask;
|
||||
}
|
||||
|
||||
private ConfigValue transformValue(String value, ConfigSourceInterceptorContext context) {
|
||||
private ConfigValue transformValue(Optional<String> 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<String> 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<T> {
|
||||
|
||||
private final Option<T> option;
|
||||
private String to;
|
||||
private BiFunction<String, ConfigSourceInterceptorContext, String> mapper;
|
||||
private BiFunction<Optional<String>, ConfigSourceInterceptorContext, Optional<String>> mapper;
|
||||
private String mapFrom = null;
|
||||
private boolean isMasked = false;
|
||||
private String paramLabel;
|
||||
|
@ -229,7 +233,7 @@ public class PropertyMapper<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> transformer(BiFunction<String, ConfigSourceInterceptorContext, String> mapper) {
|
||||
public Builder<T> transformer(BiFunction<Optional<String>, ConfigSourceInterceptorContext, Optional<String>> mapper) {
|
||||
this.mapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -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<String> getValidProxyModeValue(Optional<String> 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<String> getResolveEnableForwardedHost(Optional<String> proxy, ConfigSourceInterceptorContext context) {
|
||||
return of(String.valueOf(!ProxyOptions.Mode.none.name().equals(proxy)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<String> getAreaStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||
return of("legacy".equals(storage.orElse(null)) ? "jpa" : "map");
|
||||
}
|
||||
|
||||
private static Optional<String> getCacheStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||
return of("legacy".equals(storage.orElse(null)) ? "infinispan" : "map");
|
||||
}
|
||||
|
||||
private static Optional<String> getDbLockProvider(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||
return of("legacy".equals(storage.orElse(null)) ? "jpa" : "none");
|
||||
}
|
||||
|
||||
private static Optional<String> getUserSessionPersisterStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||
return of("legacy".equals(storage.orElse(null)) ? "jpa" : "disabled");
|
||||
}
|
||||
|
||||
private static Optional<String> isDefaultPersistenceUnitEnabled(Optional<String> 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<String> resolveStorageProvider(Optional<String> value, ConfigSourceInterceptorContext context) {
|
||||
return Optional.ofNullable("legacy".equals(value.orElse(null)) ? null : "concurrenthashmap");
|
||||
}
|
||||
|
||||
private static Optional<String> isCacheAreaEnabledForStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
|
||||
return of("legacy".equals(storage.orElse(null)) ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> getQuarkusTransactionsValue(Optional<String> txValue, ConfigSourceInterceptorContext context) {
|
||||
boolean isXaEnabled = Boolean.parseBoolean(txValue.get());
|
||||
|
||||
if (isXaEnabled) {
|
||||
return "xa";
|
||||
return of("xa");
|
||||
}
|
||||
|
||||
return "enabled";
|
||||
return of("enabled");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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<String, String> 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<RealmRepresentation> realms;
|
||||
try {
|
||||
realms = JsonSerialization
|
||||
.readValue(new FileInputStream(addUserFile), new TypeReference<List<RealmRepresentation>>() {
|
||||
});
|
||||
} 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 + ".";
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -24,4 +24,10 @@ metrics-enabled=false
|
|||
|
||||
#logging defaults
|
||||
log-console-output=default
|
||||
log-file=${kc.home.dir:default}data/log/keycloak.log
|
||||
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
|
|
@ -1 +1 @@
|
|||
org.keycloak.quarkus.runtime.storage.infinispan.QuarkusCacheManagerProvider
|
||||
org.keycloak.quarkus.runtime.storage.legacy.infinispan.QuarkusCacheManagerProvider
|
|
@ -17,4 +17,4 @@
|
|||
# */
|
||||
#
|
||||
|
||||
org.keycloak.quarkus.runtime.storage.infinispan.QuarkusInfinispanConnectionFactory
|
||||
org.keycloak.quarkus.runtime.storage.legacy.infinispan.QuarkusInfinispanConnectionFactory
|
|
@ -15,4 +15,4 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory
|
||||
org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusJpaUpdaterProviderFactory
|
||||
org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusJpaUpdaterProviderFactory
|
|
@ -15,4 +15,4 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusLiquibaseConnectionProvider
|
||||
org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusLiquibaseConnectionProvider
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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\""));
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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 <type> Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy.
|
||||
|
||||
Database:
|
||||
|
||||
--db <vendor> The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql,
|
||||
|
|
|
@ -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 <type> Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy.
|
||||
|
||||
Database:
|
||||
|
||||
--db <vendor> The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql,
|
||||
|
|
|
@ -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 <type> Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy.
|
||||
|
||||
Database:
|
||||
|
||||
--db <vendor> The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql,
|
||||
|
|
|
@ -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 <type> Sets a storage mechanism. Possible values are: legacy, chm. Default: legacy.
|
||||
|
||||
Database:
|
||||
|
||||
--db <vendor> The database vendor. Possible values are: dev-file, dev-mem, mariadb, mssql,
|
||||
|
|
|
@ -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> T loadJson(InputStream is, Class<T> type) {
|
||||
try {
|
||||
return JsonSerialization.readValue(is, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to parse json", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue