diff --git a/quarkus/runtime/src/main/java/org/keycloak/cli/MainCommand.java b/quarkus/runtime/src/main/java/org/keycloak/cli/MainCommand.java index adea5abc9e..fff586df5c 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/cli/MainCommand.java +++ b/quarkus/runtime/src/main/java/org/keycloak/cli/MainCommand.java @@ -19,11 +19,16 @@ package org.keycloak.cli; import static org.keycloak.cli.Picocli.error; import static org.keycloak.cli.Picocli.println; +import static org.keycloak.exportimport.ExportImportConfig.ACTION_EXPORT; +import static org.keycloak.exportimport.ExportImportConfig.ACTION_IMPORT; +import static org.keycloak.exportimport.Strategy.IGNORE_EXISTING; +import static org.keycloak.exportimport.Strategy.OVERWRITE_EXISTING; import io.quarkus.bootstrap.runner.RunnerClassLoader; import org.keycloak.configuration.KeycloakConfigSourceProvider; import io.quarkus.bootstrap.runner.QuarkusEntryPoint; + import org.keycloak.util.Environment; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -31,11 +36,7 @@ import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Option; import picocli.CommandLine.Spec; -import java.lang.reflect.Field; -import java.nio.file.Path; -import java.util.Map; - -@Command(name = "keycloak", +@Command(name = "keycloak", usageHelpWidth = 150, header = "Keycloak - Open Source Identity and Access Management\n\nFind more information at: https://www.keycloak.org/%n", description = "Use this command-line tool to manage your Keycloak cluster%n", footerHeading = "%nUse \"${COMMAND-NAME} --help\" for more information about a command.%nUse \"${COMMAND-NAME} options\" for a list of all command-line options.", @@ -109,8 +110,7 @@ public class MainCommand { optionListHeading = "%nOptions%n", parameterListHeading = "Available Commands%n") public void startDev(@Option(names = "--verbose", description = "Print out more details when running this command.", required = false) Boolean verbose) { - System.setProperty("kc.profile", "dev"); - System.setProperty("quarkus.profile", "dev"); + setProfile("dev"); KeycloakMain.start(spec.commandLine()); } @@ -126,28 +126,13 @@ public class MainCommand { @Option(names = "--users", arity = "1", description = "Set how users should be exported. Possible values are: skip, realm_file, same_file, different_files.", paramLabel = "", defaultValue = "different_files") String users, @Option(names = "--users-per-file", arity = "1", description = "Set the number of users per file. It’s used only if --users=different_files.", paramLabel = "", defaultValue = "50") Integer usersPerFile, @Option(names = "--verbose", description = "Print out more details when running this command.", required = false) Boolean verbose) { - System.setProperty("keycloak.migration.action", "export"); - - if (toDir != null) { - System.setProperty("keycloak.migration.provider", "dir"); - System.setProperty("keycloak.migration.dir", toDir); - } else if (toFile != null) { - System.setProperty("keycloak.migration.provider", "singleFile"); - System.setProperty("keycloak.migration.file", toFile); - } else { - error(spec.commandLine(), "Must specify either --dir or --file options."); - } - System.setProperty("keycloak.migration.usersExportStrategy", users.toUpperCase()); if (usersPerFile != null) { System.setProperty("keycloak.migration.usersPerFile", usersPerFile.toString()); } - - if (realm != null) { - System.setProperty("keycloak.migration.realmName", realm); - } - KeycloakMain.start(spec.commandLine()); + + runImportExport(ACTION_EXPORT, toDir, toFile, realm, verbose); } @Command(name = "import", @@ -161,24 +146,9 @@ public class MainCommand { @Option(names = "--realm", arity = "1", description = "Set the name of the realm to import", paramLabel = "") String realm, @Option(names = "--override", arity = "1", description = "Set if existing data should be skipped or overridden.", paramLabel = "false", defaultValue = "true") boolean override, @Option(names = "--verbose", description = "Print out more details when running this command.", required = false) Boolean verbose) { - System.setProperty("keycloak.migration.action", "import"); - if (toDir != null) { - System.setProperty("keycloak.migration.provider", "dir"); - System.setProperty("keycloak.migration.dir", toDir); - } else if (toFile != null) { - System.setProperty("keycloak.migration.provider", "singleFile"); - System.setProperty("keycloak.migration.file", toFile); - } else { - error(spec.commandLine(), "Must specify either --dir or --file options."); - } + System.setProperty("keycloak.migration.strategy", override ? OVERWRITE_EXISTING.name() : IGNORE_EXISTING.name()); - if (realm != null) { - System.setProperty("keycloak.migration.realmName", realm); - } - - System.setProperty("keycloak.migration.strategy", override ? "OVERWRITE_EXISTING" : "IGNORE_EXISTING"); - - KeycloakMain.start(spec.commandLine()); + runImportExport(ACTION_IMPORT, toDir, toFile, realm, verbose); } @Command(name = "start", @@ -212,4 +182,25 @@ public class MainCommand { System.setProperty("kc.show.config", filter); KeycloakMain.start(spec.commandLine()); } + + private void runImportExport(String action, String toDir, String toFile, String realm, Boolean verbose) { + System.setProperty("keycloak.migration.action", action); + + if (toDir != null) { + System.setProperty("keycloak.migration.provider", "dir"); + System.setProperty("keycloak.migration.dir", toDir); + } else if (toFile != null) { + System.setProperty("keycloak.migration.provider", "singleFile"); + System.setProperty("keycloak.migration.file", toFile); + } else { + error(spec.commandLine(), "Must specify either --dir or --file options."); + } + + if (realm != null) { + System.setProperty("keycloak.migration.realmName", realm); + } + + setProfile(Environment.IMPORT_EXPORT_MODE); + start(null, verbose); + } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java index 6c60cdd92d..64cd7942af 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java @@ -54,7 +54,8 @@ public final class PropertyMappers { Boolean enabled = Boolean.valueOf(value); ConfigValue proxy = context.proceed(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + "proxy"); - if (Environment.isDevMode() || (proxy != null && "edge".equalsIgnoreCase(proxy.getValue()))) { + if (Environment.isDevMode() || Environment.isImportExportMode() + || (proxy != null && "edge".equalsIgnoreCase(proxy.getValue()))) { enabled = true; } diff --git a/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/QuarkusJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/QuarkusJpaConnectionProviderFactory.java index 99abd2f310..3bd477a513 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/QuarkusJpaConnectionProviderFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/connections/jpa/QuarkusJpaConnectionProviderFactory.java @@ -74,6 +74,7 @@ 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.util.Environment; import org.keycloak.util.JsonSerialization; /** @@ -144,20 +145,20 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP emf = instance.get(); KeycloakSession session = factory.create(); - boolean initSchema; + boolean schemaChanged; try (Connection connection = getConnection()) { createOperationalInfo(connection); addSpecificNamedQueries(session, connection); - initSchema = createOrUpdateSchema(getSchema(), connection, session); + schemaChanged = createOrUpdateSchema(getSchema(), connection, session); } catch (SQLException cause) { throw new RuntimeException("Failed to update database.", cause); } finally { session.close(); } - if (initSchema || ExportImportConfig.ACTION_EXPORT.equals(ExportImportConfig.getAction())) { - runJobInTransaction(factory, this::initSchemaOrExport); + if (schemaChanged || Environment.isImportExportMode()) { + runJobInTransaction(factory, this::initSchema); } } @@ -201,7 +202,7 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP } } - private void initSchemaOrExport(KeycloakSession session) { + private void initSchema(KeycloakSession session) { ExportImportManager exportImportManager = new ExportImportManager(session); /* diff --git a/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java b/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java index 109a84dc67..af9ad216e0 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java +++ b/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java @@ -26,6 +26,8 @@ import org.keycloak.configuration.Configuration; public final class Environment { + public static final String IMPORT_EXPORT_MODE = "import_export"; + public static Boolean isRebuild() { return Boolean.valueOf(System.getProperty("quarkus.launch.rebuild")); } @@ -90,6 +92,10 @@ public final class Environment { return ProfileManager.getLaunchMode() == LaunchMode.DEVELOPMENT; } + public static boolean isImportExportMode() { + return IMPORT_EXPORT_MODE.equalsIgnoreCase(getProfile()); + } + public static boolean isWindows() { return SystemUtils.IS_OS_WINDOWS; } diff --git a/quarkus/server/src/main/resources/META-INF/keycloak.properties b/quarkus/server/src/main/resources/META-INF/keycloak.properties index 55585939d5..7198c96a7a 100644 --- a/quarkus/server/src/main/resources/META-INF/keycloak.properties +++ b/quarkus/server/src/main/resources/META-INF/keycloak.properties @@ -19,6 +19,10 @@ spi.theme.folder.dir=${kc.home.dir:}/themes %dev.spi.theme.cache-templates=false %dev.spi.theme.static-max-age=-1 +# The default configuration when running in import or export mode +%import_export.http.enabled=true +%import_export.cluster=local + # Logging configuration. INFO is the default level for most of the categories #quarkus.log.level = DEBUG quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level=WARN