From 24a492ffe0d7f5ae3ad92b8abea2ee227f4a7d68 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 8 Jul 2014 13:58:20 +0200 Subject: [PATCH 1/3] Export/import refactoring to use same representation as admin endpoints --- .../idm/CredentialRepresentation.java | 33 +- .../idm/OAuthClientRepresentation.java | 9 - .../idm/RealmRepresentation.java | 24 + export-import/export-import-api/pom.xml | 26 + .../exportimport/ExportImportConfig.java | 107 +++ .../exportimport/ExportImportManager.java | 63 ++ .../exportimport/ExportImportProvider.java | 12 - .../keycloak/exportimport/ExportProvider.java | 18 + .../exportimport/ExportProviderFactory.java | 9 + .../org/keycloak/exportimport/ExportSpi.java | 26 + .../keycloak/exportimport/ImportProvider.java | 16 + .../exportimport/ImportProviderFactory.java | 9 + .../org/keycloak/exportimport/ImportSpi.java | 26 + .../org/keycloak/exportimport/Strategy.java | 10 + .../exportimport/util/ExportImportJob.java | 13 + .../exportimport/util/ExportImportUtils.java | 45 ++ .../exportimport/util/ExportUtils.java | 365 ++++++++++ .../exportimport/util/ImportUtils.java | 170 +++++ .../util/MultipleStepsExportProvider.java | 115 +++ .../services/org.keycloak.provider.Spi | 2 + export-import/export-import-dir/pom.xml | 66 ++ .../exportimport/dir/DirExportProvider.java | 73 ++ .../dir/DirExportProviderFactory.java | 36 + .../exportimport/dir/DirImportProvider.java | 109 +++ .../dir/DirImportProviderFactory.java | 34 + ...eycloak.exportimport.ExportProviderFactory | 1 + ...eycloak.exportimport.ImportProviderFactory | 1 + .../exportimport/ExportImportConfig.java | 58 -- .../ExportImportProviderImpl.java | 3 +- .../exportimport/ExportImportUtils.java | 20 - .../exportimport/ExportImportTestBase.java | 115 --- .../JPAToMongoExportImportTest.java | 26 +- .../MongoToJPAExportImportTest.java | 56 +- .../export-import-single-file/pom.xml | 65 ++ .../singlefile/SingleFileExportProvider.java | 84 +++ .../SingleFileExportProviderFactory.java | 36 + .../singlefile/SingleFileImportProvider.java | 58 ++ .../SingleFileImportProviderFactory.java | 34 + ...eycloak.exportimport.ExportProviderFactory | 1 + ...eycloak.exportimport.ImportProviderFactory | 1 + export-import/export-import-zip/pom.xml | 70 ++ .../exportimport/zip/ZipExportProvider.java | 73 ++ .../zip/ZipExportProviderFactory.java | 44 ++ .../exportimport/zip/ZipImportProvider.java | 109 +++ .../zip/ZipImportProviderFactory.java | 41 ++ ...eycloak.exportimport.ExportProviderFactory | 1 + ...eycloak.exportimport.ImportProviderFactory | 1 + export-import/pom.xml | 3 + model/api/pom.xml | 5 + .../models/utils/KeycloakModelUtils.java | 23 + .../models/utils}/ModelToRepresentation.java | 54 +- .../models/utils/RepresentationToModel.java | 671 ++++++++++++++++++ .../org/keycloak/model/test/AdapterTest.java | 10 +- .../model/test/ApplicationModelTest.java | 6 +- .../org/keycloak/model/test/ModelTest.java | 5 +- .../idm/LDAPKeycloakCredentialHandler.java | 2 +- .../services/managers/ApplianceBootstrap.java | 3 +- .../services/managers/ApplicationManager.java | 186 +---- .../services/managers/ClaimManager.java | 66 -- .../services/managers/OAuthClientManager.java | 83 --- .../services/managers/RealmManager.java | 408 +---------- .../services/resources/AccountService.java | 2 +- .../resources/KeycloakApplication.java | 18 +- .../resources/admin/ApplicationResource.java | 11 +- .../resources/admin/ApplicationsResource.java | 8 +- .../resources/admin/ClaimResource.java | 6 +- .../resources/admin/OAuthClientResource.java | 10 +- .../resources/admin/OAuthClientsResource.java | 7 +- .../resources/admin/RealmAdminResource.java | 5 +- .../resources/admin/RealmsAdminResource.java | 2 +- .../admin/RoleContainerResource.java | 3 +- .../resources/admin/RoleResource.java | 2 +- .../resources/admin/ScopeMappedResource.java | 4 +- .../resources/admin/UsersResource.java | 5 +- testsuite/integration/pom.xml | 12 +- .../composites/CompositeRoleTest.java | 3 +- .../exportimport/ExportImportTest.java | 269 +++++++ .../testsuite/rule/AbstractKeycloakRule.java | 11 +- .../org/keycloak/test/tools/PerfTools.java | 6 +- 79 files changed, 3120 insertions(+), 1103 deletions(-) create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java delete mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java create mode 100644 export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java create mode 100644 export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi create mode 100644 export-import/export-import-dir/pom.xml create mode 100644 export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProvider.java create mode 100644 export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProviderFactory.java create mode 100644 export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java create mode 100644 export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java create mode 100644 export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory create mode 100644 export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportUtils.java delete mode 100644 export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java create mode 100644 export-import/export-import-single-file/pom.xml create mode 100644 export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java create mode 100644 export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java create mode 100644 export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java create mode 100644 export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java create mode 100644 export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory create mode 100644 export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory create mode 100644 export-import/export-import-zip/pom.xml create mode 100644 export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProvider.java create mode 100644 export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java create mode 100644 export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java create mode 100644 export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java create mode 100644 export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory create mode 100644 export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory rename {services/src/main/java/org/keycloak/services/managers => model/api/src/main/java/org/keycloak/models/utils}/ModelToRepresentation.java (79%) create mode 100644 model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java delete mode 100755 services/src/main/java/org/keycloak/services/managers/ClaimManager.java create mode 100644 testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java diff --git a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java index b0c37ac22a..99237ee23f 100755 --- a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java @@ -11,9 +11,16 @@ public class CredentialRepresentation { public static final String CLIENT_CERT = "cert"; protected String type; - protected String value; protected String device; + // Plain-text value of credential (used for example during import from manually created JSON file) + protected String value; + + // Value stored in DB (used for example during export/import) + protected String hashedSaltedValue; + protected String salt; + protected Integer hashIterations; + public String getType() { return type; } @@ -37,4 +44,28 @@ public class CredentialRepresentation { public void setDevice(String device) { this.device = device; } + + public String getHashedSaltedValue() { + return hashedSaltedValue; + } + + public void setHashedSaltedValue(String hashedSaltedValue) { + this.hashedSaltedValue = hashedSaltedValue; + } + + public String getSalt() { + return salt; + } + + public void setSalt(String salt) { + this.salt = salt; + } + + public Integer getHashIterations() { + return hashIterations; + } + + public void setHashIterations(Integer hashIterations) { + this.hashIterations = hashIterations; + } } diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java index 8751a38ca2..7511de6f20 100755 --- a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java @@ -9,7 +9,6 @@ import java.util.List; public class OAuthClientRepresentation { protected String id; protected String name; - protected String baseUrl; protected List redirectUris; protected List webOrigins; protected Boolean enabled; @@ -44,14 +43,6 @@ public class OAuthClientRepresentation { this.enabled = enabled; } - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - public List getRedirectUris() { return redirectUris; } diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java index 4b5380d8fc..6d85877a83 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -423,4 +423,28 @@ public class RealmRepresentation { public void setFailureFactor(Integer failureFactor) { this.failureFactor = failureFactor; } + + public boolean isAuditEnabled() { + return auditEnabled; + } + + public void setAuditEnabled(boolean auditEnabled) { + this.auditEnabled = auditEnabled; + } + + public long getAuditExpiration() { + return auditExpiration; + } + + public void setAuditExpiration(long auditExpiration) { + this.auditExpiration = auditExpiration; + } + + public List getAuditListeners() { + return auditListeners; + } + + public void setAuditListeners(List auditListeners) { + this.auditListeners = auditListeners; + } } diff --git a/export-import/export-import-api/pom.xml b/export-import/export-import-api/pom.xml index 19db6e5e9a..1b4dc0a4a2 100755 --- a/export-import/export-import-api/pom.xml +++ b/export-import/export-import-api/pom.xml @@ -26,6 +26,32 @@ ${project.version} provided + + org.keycloak + keycloak-invalidation-cache-model + ${project.version} + provided + + + org.jboss.logging + jboss-logging + provided + + + org.codehaus.jackson + jackson-core-asl + provided + + + org.codehaus.jackson + jackson-mapper-asl + provided + + + net.iharder + base64 + provided + diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java new file mode 100644 index 0000000000..8014c21f4e --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportConfig.java @@ -0,0 +1,107 @@ +package org.keycloak.exportimport; + +/** + * @author Stian Thorgersen + */ +public class ExportImportConfig { + + public static final String ACTION = "keycloak.migration.action"; + public static final String ACTION_EXPORT = "export"; + public static final String ACTION_IMPORT = "import"; + + public static final String PROVIDER = "keycloak.migration.provider"; + public static final String PROVIDER_DEFAULT = "zip"; + + // Name of the realm to export. If null, then full export will be triggered + public static final String REALM_NAME = "keycloak.migration.realmName"; + + // used for "dir" provider + public static final String DIR = "keycloak.migration.dir"; + // used for "zip" provider + public static final String ZIP_FILE = "keycloak.migration.zipFile"; + public static final String ZIP_PASSWORD = "keycloak.migration.zipPassword"; + // used for "singleFile" provider + public static final String FILE = "keycloak.migration.file"; + + // Number of users per file used in "dir" and "zip" providers. -1 means adding users to same file with realm. 0 means adding to separate file with unlimited page number + public static final String USERS_PER_FILE = "keycloak.migration.usersPerFile"; + public static final Integer DEFAULT_USERS_PER_FILE = 5000; + + // Strategy used during import data + public static final String STRATEGY = "keycloak.migration.strategy"; + public static final Strategy DEFAULT_STRATEGY = Strategy.OVERWRITE_EXISTING; + + public static String getAction() { + return System.getProperty(ACTION); + } + + public static void setAction(String exportImportAction) { + System.setProperty(ACTION, exportImportAction); + } + + public static String getProvider() { + return System.getProperty(PROVIDER, PROVIDER_DEFAULT); + } + + public static void setProvider(String exportImportProvider) { + System.setProperty(PROVIDER, exportImportProvider); + } + + public static String getRealmName() { + return System.getProperty(REALM_NAME); + } + + public static void setRealmName(String realmName) { + if (realmName != null) { + System.setProperty(REALM_NAME, realmName); + } else { + System.getProperties().remove(REALM_NAME); + } + } + + public static String getDir() { + return System.getProperty(DIR); + } + + public static String setDir(String dir) { + return System.setProperty(DIR, dir); + } + + public static String getZipFile() { + return System.getProperty(ZIP_FILE); + } + + public static void setZipFile(String exportImportZipFile) { + System.setProperty(ZIP_FILE, exportImportZipFile); + } + + public static String getZipPassword() { + return System.getProperty(ZIP_PASSWORD); + } + + public static void setZipPassword(String exportImportZipPassword) { + System.setProperty(ZIP_PASSWORD, exportImportZipPassword); + } + + public static String getFile() { + return System.getProperty(FILE); + } + + public static void setFile(String file) { + System.setProperty(FILE, file); + } + + public static Integer getUsersPerFile() { + String usersPerFile = System.getProperty(USERS_PER_FILE, String.valueOf(DEFAULT_USERS_PER_FILE)); + return Integer.parseInt(usersPerFile.trim()); + } + + public static void setUsersPerFile(Integer usersPerFile) { + System.setProperty(USERS_PER_FILE, String.valueOf(usersPerFile)); + } + + public static Strategy getStrategy() { + String strategy = System.getProperty(STRATEGY, DEFAULT_STRATEGY.toString()); + return Enum.valueOf(Strategy.class, strategy); + } +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java new file mode 100644 index 0000000000..a15a1ce38c --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java @@ -0,0 +1,63 @@ +package org.keycloak.exportimport; + + +import org.jboss.logging.Logger; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +/** + * @author Marek Posolda + */ +public class ExportImportManager { + + private static final Logger logger = Logger.getLogger(ExportImportManager.class); + + public void checkExportImport(KeycloakSessionFactory sessionFactory) { + String exportImportAction = ExportImportConfig.getAction(); + String realmName = ExportImportConfig.getRealmName(); + + boolean export = false; + boolean importt = false; + if (ExportImportConfig.ACTION_EXPORT.equals(exportImportAction)) { + export = true; + } else if (ExportImportConfig.ACTION_IMPORT.equals(exportImportAction)) { + importt = true; + } + + if (export || importt) { + String exportImportProviderId = ExportImportConfig.getProvider(); + logger.debug("Will use provider: " + exportImportProviderId); + KeycloakSession session = sessionFactory.create(); + + try { + if (export) { + ExportProvider exportProvider = session.getProvider(ExportProvider.class, exportImportProviderId); + + if (realmName == null) { + logger.info("Full model export requested"); + exportProvider.exportModel(session); + } else { + logger.infof("Export of realm '%s' requested", realmName); + exportProvider.exportRealm(session, realmName); + } + logger.info("Export finished successfully"); + } else { + ImportProvider importProvider = session.getProvider(ImportProvider.class, exportImportProviderId); + Strategy strategy = ExportImportConfig.getStrategy(); + if (realmName == null) { + logger.infof("Full model import requested. Strategy: %s", strategy.toString()); + importProvider.importModel(session, strategy); + } else { + logger.infof("Import of realm '%s' requested. Strategy: %s", realmName, strategy.toString()); + importProvider.importRealm(session, realmName, strategy); + } + logger.info("Import finished successfully"); + } + } catch (Throwable ioe) { + logger.error("Error during export/import", ioe); + } finally { + session.close(); + } + } + } +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java deleted file mode 100644 index d53f1fa8bc..0000000000 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.keycloak.exportimport; - -import org.keycloak.models.KeycloakSessionFactory; - -/** - * @author Marek Posolda - */ -public interface ExportImportProvider { - - void checkExportImport(KeycloakSessionFactory sessionFactory); - -} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java new file mode 100644 index 0000000000..4171b89664 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java @@ -0,0 +1,18 @@ +package org.keycloak.exportimport; + +import java.io.IOException; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.Provider; + +/** + * @author Marek Posolda + */ +public interface ExportProvider extends Provider { + + void exportModel(KeycloakSession session) throws IOException; + + void exportRealm(KeycloakSession session, String realmName) throws IOException; + +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java new file mode 100644 index 0000000000..735e1849e1 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java @@ -0,0 +1,9 @@ +package org.keycloak.exportimport; + +import org.keycloak.provider.ProviderFactory; + +/** + * @author Marek Posolda + */ +public interface ExportProviderFactory extends ProviderFactory { +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java new file mode 100644 index 0000000000..6ec29606d9 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportSpi.java @@ -0,0 +1,26 @@ +package org.keycloak.exportimport; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Marek Posolda + */ +public class ExportSpi implements Spi { + + @Override + public String getName() { + return "export"; + } + + @Override + public Class getProviderClass() { + return ExportProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return ExportProviderFactory.class; + } +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java new file mode 100644 index 0000000000..5283977db1 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java @@ -0,0 +1,16 @@ +package org.keycloak.exportimport; + +import java.io.IOException; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.provider.Provider; + +/** + * @author Marek Posolda + */ +public interface ImportProvider extends Provider { + + void importModel(KeycloakSession session, Strategy strategy) throws IOException; + + void importRealm(KeycloakSession session, String realmName, Strategy strategy) throws IOException; +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java new file mode 100644 index 0000000000..ecb1bd37fb --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java @@ -0,0 +1,9 @@ +package org.keycloak.exportimport; + +import org.keycloak.provider.ProviderFactory; + +/** + * @author Marek Posolda + */ +public interface ImportProviderFactory extends ProviderFactory { +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java new file mode 100644 index 0000000000..b562f3fcbe --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportSpi.java @@ -0,0 +1,26 @@ +package org.keycloak.exportimport; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Marek Posolda + */ +public class ImportSpi implements Spi { + + @Override + public String getName() { + return "import"; + } + + @Override + public Class getProviderClass() { + return ImportProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return ImportProviderFactory.class; + } +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java new file mode 100644 index 0000000000..6bbd70d923 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/Strategy.java @@ -0,0 +1,10 @@ +package org.keycloak.exportimport; + +/** + * @author Marek Posolda + */ +public enum Strategy { + + IGNORE_EXISTING, // Ignore existing user entries + OVERWRITE_EXISTING // Overwrite existing user entries +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java new file mode 100644 index 0000000000..12922b4a41 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java @@ -0,0 +1,13 @@ +package org.keycloak.exportimport.util; + +import java.io.IOException; + +/** + * Task to be executed inside transaction + * + * @author Marek Posolda + */ +public interface ExportImportJob { + + public void run() throws IOException; +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java new file mode 100644 index 0000000000..2c0ab44599 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java @@ -0,0 +1,45 @@ +package org.keycloak.exportimport.util; + +import java.io.IOException; + +import org.jboss.logging.Logger; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakTransaction; +import org.keycloak.models.RealmModel; + +/** + * @author Marek Posolda + */ +public class ExportImportUtils { + + /** + * Wrap given runnable job into KeycloakTransaction. Assumption is that session already exists and it doesn't need to be closed when finished + * + * @param session + * @param job + */ + public static void runJobInTransaction(KeycloakSession session, ExportImportJob job) throws IOException { + KeycloakTransaction tx = session.getTransaction(); + try { + tx.begin(); + + job.run(); + + if (tx.isActive()) { + if (tx.getRollbackOnly()) { + tx.rollback(); + } else { + tx.commit(); + } + } + } finally { + if (tx.isActive()) { + tx.rollback(); + } + } + } + + public static String getMasterRealmAdminApplicationName(RealmModel realm) { + return realm.getName() + "-realm"; + } +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java new file mode 100644 index 0000000000..4c96812073 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java @@ -0,0 +1,365 @@ +package org.keycloak.exportimport.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.iharder.Base64; +import org.codehaus.jackson.JsonEncoding; +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.keycloak.exportimport.Strategy; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.AuthenticationLinkModel; +import org.keycloak.models.ClientModel; +import org.keycloak.models.OAuthClientModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleContainerModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.SocialLinkModel; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.representations.idm.ApplicationRepresentation; +import org.keycloak.representations.idm.AuthenticationLinkRepresentation; +import org.keycloak.representations.idm.ClaimRepresentation; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.OAuthClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.RolesRepresentation; +import org.keycloak.representations.idm.ScopeMappingRepresentation; +import org.keycloak.representations.idm.SocialLinkRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +/** + * @author Marek Posolda + */ +public class ExportUtils { + + public static RealmRepresentation exportRealm(RealmModel realm, boolean includeUsers) { + RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm); + + // Audit + rep.setAuditEnabled(realm.isAuditEnabled()); + if (realm.getAuditExpiration() != 0) { + rep.setAuditExpiration(realm.getAuditExpiration()); + } + + if (realm.getAuditListeners() != null) { + rep.setAuditListeners(new LinkedList(realm.getAuditListeners())); + } + + // Applications + List applications = realm.getApplications(); + List appReps = new ArrayList(); + for (ApplicationModel app : applications) { + ApplicationRepresentation appRep = exportApplication(app); + appReps.add(appRep); + } + rep.setApplications(appReps); + + // OAuth clients + List oauthClients = realm.getOAuthClients(); + List oauthClientReps = new ArrayList(); + for (OAuthClientModel oauthClient : oauthClients) { + OAuthClientRepresentation clientRep = ModelToRepresentation.toRepresentation(oauthClient); + oauthClientReps.add(clientRep); + } + rep.setOauthClients(oauthClientReps); + + // Roles + List realmRoleReps = null; + Map> appRolesReps = new HashMap>(); + + Set realmRoles = realm.getRoles(); + if (realmRoles != null && realmRoles.size() > 0) { + realmRoleReps = exportRoles(realmRoles); + } + for (ApplicationModel app : applications) { + Set currentAppRoles = app.getRoles(); + List currentAppRoleReps = exportRoles(currentAppRoles); + appRolesReps.put(app.getName(), currentAppRoleReps); + } + + RolesRepresentation rolesRep = new RolesRepresentation(); + if (realmRoleReps != null) { + rolesRep.setRealm(realmRoleReps); + } + if (appRolesReps.size() > 0) { + rolesRep.setApplication(appRolesReps); + } + rep.setRoles(rolesRep); + + // Scopes + List allClients = new ArrayList(applications); + allClients.addAll(realm.getOAuthClients()); + Map> appScopeReps = new HashMap>(); + + for (ClientModel client : allClients) { + Set clientScopes = client.getScopeMappings(); + ScopeMappingRepresentation scopeMappingRep = null; + for (RoleModel scope : clientScopes) { + if (scope.getContainer() instanceof RealmModel) { + if (scopeMappingRep == null) { + scopeMappingRep = rep.scopeMapping(client.getClientId()); + } + scopeMappingRep.role(scope.getName()); + } else { + ApplicationModel app = (ApplicationModel)scope.getContainer(); + String appName = app.getName(); + List currentAppScopes = appScopeReps.get(appName); + if (currentAppScopes == null) { + currentAppScopes = new ArrayList(); + appScopeReps.put(appName, currentAppScopes); + } + + ScopeMappingRepresentation currentClientScope = null; + for (ScopeMappingRepresentation scopeMapping : currentAppScopes) { + if (scopeMapping.getClient().equals(client.getClientId())) { + currentClientScope = scopeMapping; + break; + } + } + if (currentClientScope == null) { + currentClientScope = new ScopeMappingRepresentation(); + currentClientScope.setClient(client.getClientId()); + currentAppScopes.add(currentClientScope); + } + currentClientScope.role(scope.getName()); + } + } + } + + if (appScopeReps.size() > 0) { + rep.setApplicationScopeMappings(appScopeReps); + } + + // Finally users if needed + if (includeUsers) { + List allUsers = realm.getUsers(); + List users = new ArrayList(); + for (UserModel user : allUsers) { + UserRepresentation userRep = exportUser(realm, user); + users.add(userRep); + } + + if (users.size() > 0) { + rep.setUsers(users); + } + } + + return rep; + } + + /** + * Full export of application including claims and secret + * @param app + * @return full ApplicationRepresentation + */ + public static ApplicationRepresentation exportApplication(ApplicationModel app) { + ApplicationRepresentation appRep = ModelToRepresentation.toRepresentation(app); + + appRep.setSecret(app.getSecret()); + ClaimRepresentation claimRep = ModelToRepresentation.toRepresentation((ClientModel)app); + appRep.setClaims(claimRep); + return appRep; + } + + public static List exportRoles(Collection roles) { + List roleReps = new ArrayList(); + + for (RoleModel role : roles) { + RoleRepresentation roleRep = exportRole(role); + roleReps.add(roleRep); + } + return roleReps; + } + + public static List getRoleNames(Collection roles) { + List roleNames = new ArrayList(); + for (RoleModel role : roles) { + roleNames.add(role.getName()); + } + return roleNames; + } + + /** + * Full export of role including composite roles + * @param role + * @return RoleRepresentation with all stuff filled (including composite roles) + */ + public static RoleRepresentation exportRole(RoleModel role) { + RoleRepresentation roleRep = ModelToRepresentation.toRepresentation(role); + + Set composites = role.getComposites(); + if (composites != null && composites.size() > 0) { + Set compositeRealmRoles = null; + Map> compositeAppRoles = null; + + for (RoleModel composite : composites) { + RoleContainerModel crContainer = composite.getContainer(); + if (crContainer instanceof RealmModel) { + + if (compositeRealmRoles == null) { + compositeRealmRoles = new HashSet(); + } + compositeRealmRoles.add(composite.getName()); + } else { + if (compositeAppRoles == null) { + compositeAppRoles = new HashMap>(); + } + + ApplicationModel app = (ApplicationModel)crContainer; + String appName = app.getName(); + List currentAppComposites = compositeAppRoles.get(appName); + if (currentAppComposites == null) { + currentAppComposites = new ArrayList(); + compositeAppRoles.put(appName, currentAppComposites); + } + currentAppComposites.add(composite.getName()); + } + } + + RoleRepresentation.Composites compRep = new RoleRepresentation.Composites(); + if (compositeRealmRoles != null) { + compRep.setRealm(compositeRealmRoles); + } + if (compositeAppRoles != null) { + compRep.setApplication(compositeAppRoles); + } + + roleRep.setComposites(compRep); + } + + return roleRep; + } + + /** + * Full export of user (including role mappings and credentials) + * + * @param user + * @return fully exported user representation + */ + public static UserRepresentation exportUser(RealmModel realm, UserModel user) { + UserRepresentation userRep = ModelToRepresentation.toRepresentation(user); + + // AuthenticationLink + AuthenticationLinkModel authLink = user.getAuthenticationLink(); + if (authLink != null) { + AuthenticationLinkRepresentation authLinkRepresentation = exportAuthLink(authLink); + userRep.setAuthenticationLink(authLinkRepresentation); + } + + // Social links + Set socialLinks = realm.getSocialLinks(user); + List socialLinkReps = new ArrayList(); + for (SocialLinkModel socialLink : socialLinks) { + SocialLinkRepresentation socialLinkRep = exportSocialLink(socialLink); + socialLinkReps.add(socialLinkRep); + } + if (socialLinkReps.size() > 0) { + userRep.setSocialLinks(socialLinkReps); + } + + // Role mappings + Set roles = user.getRoleMappings(); + List realmRoleNames = new ArrayList(); + Map> appRoleNames = new HashMap>(); + for (RoleModel role : roles) { + if (role.getContainer() instanceof RealmModel) { + realmRoleNames.add(role.getName()); + } else { + ApplicationModel app = (ApplicationModel)role.getContainer(); + String appName = app.getName(); + List currentAppRoles = appRoleNames.get(appName); + if (currentAppRoles == null) { + currentAppRoles = new ArrayList(); + appRoleNames.put(appName, currentAppRoles); + } + + currentAppRoles.add(role.getName()); + } + } + + if (realmRoleNames.size() > 0) { + userRep.setRealmRoles(realmRoleNames); + } + if (appRoleNames.size() > 0) { + userRep.setApplicationRoles(appRoleNames); + } + + // Credentials + List creds = user.getCredentialsDirectly(); + List credReps = new ArrayList(); + for (UserCredentialValueModel cred : creds) { + CredentialRepresentation credRep = exportCredential(cred); + credReps.add(credRep); + } + userRep.setCredentials(credReps); + + return userRep; + } + + public static AuthenticationLinkRepresentation exportAuthLink(AuthenticationLinkModel authLinkModel) { + AuthenticationLinkRepresentation authLinkRep = new AuthenticationLinkRepresentation(); + authLinkRep.setAuthProvider(authLinkModel.getAuthProvider()); + authLinkRep.setAuthUserId(authLinkModel.getAuthUserId()); + return authLinkRep; + } + + public static SocialLinkRepresentation exportSocialLink(SocialLinkModel socialLink) { + SocialLinkRepresentation socialLinkRep = new SocialLinkRepresentation(); + socialLinkRep.setSocialProvider(socialLink.getSocialProvider()); + socialLinkRep.setSocialUserId(socialLink.getSocialUserId()); + socialLinkRep.setSocialUsername(socialLink.getSocialUsername()); + return socialLinkRep; + } + + public static CredentialRepresentation exportCredential(UserCredentialValueModel userCred) { + CredentialRepresentation credRep = new CredentialRepresentation(); + credRep.setType(userCred.getType()); + credRep.setDevice(userCred.getDevice()); + credRep.setHashedSaltedValue(userCred.getValue()); + credRep.setSalt(Base64.encodeBytes(userCred.getSalt())); + credRep.setHashIterations(userCred.getHashIterations()); + return credRep; + } + + // Streaming API + + public static void exportUsersToStream(RealmModel realm, List usersToExport, ObjectMapper mapper, OutputStream os) throws IOException { + JsonFactory factory = mapper.getJsonFactory(); + JsonGenerator generator = factory.createJsonGenerator(os, JsonEncoding.UTF8); + try { + if (mapper.isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) { + generator.useDefaultPrettyPrinter(); + } + generator.writeStartObject(); + generator.writeStringField("realm", realm.getName()); + // generator.writeStringField("strategy", strategy.toString()); + generator.writeFieldName("users"); + generator.writeStartArray(); + + for (UserModel user : usersToExport) { + UserRepresentation userRep = ExportUtils.exportUser(realm, user); + generator.writeObject(userRep); + } + + generator.writeEndArray(); + generator.writeEndObject(); + } finally { + generator.close(); + } + } +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java new file mode 100644 index 0000000000..274798751b --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java @@ -0,0 +1,170 @@ +package org.keycloak.exportimport.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.codehaus.jackson.JsonEncoding; +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.JsonToken; +import org.codehaus.jackson.io.SerializedString; +import org.codehaus.jackson.map.ObjectMapper; +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.exportimport.Strategy; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelProvider; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +/** + * @author Marek Posolda + */ +public class ImportUtils { + + private static final Logger logger = Logger.getLogger(ImportUtils.class); + + /** + * Fully import realm from representation, save it to model and return model of newly created realm + * + * @param session + * @param rep + * @param strategy specifies whether to overwrite or ignore existing realm or user entries + * @return newly imported realm (or existing realm if ignoreExisting is true and realm of this name already exists) + */ + public static RealmModel importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy) { + String realmName = rep.getRealm(); + ModelProvider model = session.getModel(); + RealmModel realm = model.getRealmByName(realmName); + + if (realm != null) { + if (strategy == Strategy.IGNORE_EXISTING) { + logger.infof("Realm '%s' already exists. Import skipped", realmName); + return realm; + } else { + logger.infof("Realm '%s' already exists. Removing it before import", realmName); + model.removeRealm(realm.getId()); + } + } + + realm = rep.getId() != null ? model.createRealm(rep.getId(), realmName) : model.createRealm(realmName); + + RepresentationToModel.importRealm(rep, realm); + + refreshMasterAdminApps(model, realm); + + logger.infof("Realm '%s' imported", realmName); + return realm; + } + + private static void refreshMasterAdminApps(ModelProvider model, RealmModel realm) { + String adminRealmId = Config.getAdminRealm(); + if (adminRealmId.equals(realm.getId())) { + // We just imported master realm. All 'masterAdminApps' need to be refreshed + RealmModel adminRealm = realm; + for (RealmModel currentRealm : model.getRealms()) { + ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(currentRealm)); + currentRealm.setMasterAdminApp(masterApp); + } + } else { + // Need to refresh masterApp for current realm + RealmModel adminRealm = model.getRealm(adminRealmId); + ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(realm)); + realm.setMasterAdminApp(masterApp); + } + } + + + /** + * Fully import realm (or more realms from particular stream) + * + * @param session + * @param mapper + * @param is + * @param strategy + * @throws IOException + */ + public static void importFromStream(KeycloakSession session, ObjectMapper mapper, InputStream is, Strategy strategy) throws IOException { + JsonFactory factory = mapper.getJsonFactory(); + JsonParser parser = factory.createJsonParser(is); + try { + parser.nextToken(); + + if (parser.getCurrentToken() == JsonToken.START_ARRAY) { + // Case with more realms in stream + parser.nextToken(); + while (parser.getCurrentToken() == JsonToken.START_OBJECT) { + RealmRepresentation realmRep = parser.readValueAs(RealmRepresentation.class); + parser.nextToken(); + importRealm(session, realmRep, strategy); + } + } else if (parser.getCurrentToken() == JsonToken.START_OBJECT) { + // Case with single realm in stream + RealmRepresentation realmRep = parser.readValueAs(RealmRepresentation.class); + importRealm(session, realmRep, strategy); + } + } finally { + parser.close(); + } + } + + // Assuming that it's invoked inside transaction + public static void importUsersFromStream(KeycloakSession session, String realmName, ObjectMapper mapper, InputStream is) throws IOException { + ModelProvider model = session.getModel(); + JsonFactory factory = mapper.getJsonFactory(); + JsonParser parser = factory.createJsonParser(is); + try { + parser.nextToken(); + + while (parser.nextToken() == JsonToken.FIELD_NAME) { + if ("realm".equals(parser.getText())) { + parser.nextToken(); + String currRealmName = parser.getText(); + if (!currRealmName.equals(realmName)) { + throw new IllegalStateException("Trying to import users into invalid realm. Realm name: " + realmName + ", Expected realm name: " + realmName); + } + } else if ("users".equals(parser.getText())) { + parser.nextToken(); + + if (parser.getCurrentToken() == JsonToken.START_ARRAY) { + parser.nextToken(); + } + + // TODO: support for more transactions per single users file (if needed) + List userReps = new ArrayList(); + while (parser.getCurrentToken() == JsonToken.START_OBJECT) { + UserRepresentation user = parser.readValueAs(UserRepresentation.class); + userReps.add(user); + parser.nextToken(); + } + + importUsers(model, realmName, userReps); + + if (parser.getCurrentToken() == JsonToken.END_ARRAY) { + parser.nextToken(); + } + } + } + } finally { + parser.close(); + } + } + + private static void importUsers(ModelProvider model, String realmName, List userReps) { + RealmModel realm = model.getRealmByName(realmName); + Map apps = realm.getApplicationNameMap(); + for (UserRepresentation user : userReps) { + RepresentationToModel.createUser(realm, user, apps); + } + } + +} diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java new file mode 100644 index 0000000000..145ba52ae1 --- /dev/null +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java @@ -0,0 +1,115 @@ +package org.keycloak.exportimport.util; + +import java.io.IOException; +import java.util.List; + +import org.jboss.logging.Logger; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ExportProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.RealmRepresentation; + +/** + * @author Marek Posolda + */ +public abstract class MultipleStepsExportProvider implements ExportProvider { + + protected final Logger logger = Logger.getLogger(getClass()); + + @Override + public void exportModel(final KeycloakSession session) throws IOException { + final RealmsHolder holder = new RealmsHolder(); + + // Import users into same file with realm + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() { + List realms = session.getModel().getRealms(); + holder.realms = realms; + } + + }); + + for (RealmModel realm : holder.realms) { + exportRealm(session, realm.getName()); + } + } + + @Override + public void exportRealm(final KeycloakSession session, final String realmName) throws IOException { + final int usersPerFile = ExportImportConfig.getUsersPerFile(); + final UsersHolder usersHolder = new UsersHolder(); + final boolean exportUsersIntoSameFile = usersPerFile < 0; + + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + RealmModel realm = session.getModel().getRealmByName(realmName); + RealmRepresentation rep = ExportUtils.exportRealm(realm, exportUsersIntoSameFile); + writeRealm(realmName + "-realm.json", rep); + logger.info("Realm '" + realmName + "' - data exported"); + + // Count total number of users + if (!exportUsersIntoSameFile) { + // TODO: getUsersCount method on model + usersHolder.totalCount = realm.getUsers().size(); + } + } + + }); + + if (!exportUsersIntoSameFile) { + + usersHolder.currentPageStart = 0; + + // usersPerFile==0 means exporting all users into single file (but separate to realm) + final int countPerPage = usersPerFile == 0 ? usersHolder.totalCount : usersPerFile; + + while (usersHolder.currentPageStart < usersHolder.totalCount) { + if (usersHolder.currentPageStart + countPerPage < usersHolder.totalCount) { + usersHolder.currentPageEnd = usersHolder.currentPageStart + countPerPage; + } else { + usersHolder.currentPageEnd = usersHolder.totalCount; + } + + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + RealmModel realm = session.getModel().getRealmByName(realmName); + // TODO: pagination + List users = realm.getUsers(); + usersHolder.users = users.subList(usersHolder.currentPageStart, usersHolder.currentPageEnd); + + writeUsers(realmName + "-users-" + (usersHolder.currentPageStart / countPerPage) + ".json", realm, usersHolder.users); + + logger.info("Users " + usersHolder.currentPageStart + "-" + usersHolder.currentPageEnd + " exported"); + } + + }); + + usersHolder.currentPageStart = usersHolder.currentPageEnd; + } + } + } + + protected abstract void writeRealm(String fileName, RealmRepresentation rep) throws IOException; + + protected abstract void writeUsers(String fileName, RealmModel realm, List users) throws IOException; + + public static class RealmsHolder { + List realms; + + } + + public static class UsersHolder { + List users; + int totalCount; + int currentPageStart; + int currentPageEnd; + } +} diff --git a/export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..93029438b2 --- /dev/null +++ b/export-import/export-import-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1,2 @@ +org.keycloak.exportimport.ExportSpi +org.keycloak.exportimport.ImportSpi \ No newline at end of file diff --git a/export-import/export-import-dir/pom.xml b/export-import/export-import-dir/pom.xml new file mode 100644 index 0000000000..4cd8dc1172 --- /dev/null +++ b/export-import/export-import-dir/pom.xml @@ -0,0 +1,66 @@ + + + + keycloak-export-import-parent + org.keycloak + 1.0-beta-4-SNAPSHOT + ../pom.xml + + 4.0.0 + + keycloak-export-import-dir + Keycloak Export Import To Directory + + + + + org.keycloak + keycloak-core + ${project.version} + provided + + + org.keycloak + keycloak-model-api + ${project.version} + provided + + + org.keycloak + keycloak-export-import-api + ${project.version} + provided + + + org.codehaus.jackson + jackson-core-asl + provided + + + org.codehaus.jackson + jackson-mapper-asl + provided + + + org.jboss.logging + jboss-logging + provided + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProvider.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProvider.java new file mode 100644 index 0000000000..48dcf561dd --- /dev/null +++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProvider.java @@ -0,0 +1,73 @@ +package org.keycloak.exportimport.dir; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +import org.keycloak.exportimport.util.ExportUtils; +import org.keycloak.exportimport.util.MultipleStepsExportProvider; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class DirExportProvider extends MultipleStepsExportProvider { + + private final File rootDirectory; + + public DirExportProvider() { + // Determine system tmp directory + String tempDir = System.getProperty("java.io.tmpdir"); + + this.rootDirectory = new File(tempDir + "/keycloak-export"); + this.rootDirectory.mkdirs(); + + logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath()); + } + + public DirExportProvider(File rootDirectory) { + this.rootDirectory = rootDirectory; + this.rootDirectory.mkdirs(); + + logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath()); + } + + public static boolean recursiveDeleteDir(File dirPath) { + if (dirPath.exists()) { + File[] files = dirPath.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + recursiveDeleteDir(files[i]); + } else { + files[i].delete(); + } + } + } + if (dirPath.exists()) + return dirPath.delete(); + else + return true; + } + + @Override + public void writeRealm(String fileName, RealmRepresentation rep) throws IOException { + File file = new File(this.rootDirectory, fileName); + FileOutputStream stream = new FileOutputStream(file); + JsonSerialization.prettyMapper.writeValue(stream, rep); + } + + @Override + protected void writeUsers(String fileName, RealmModel realm, List users) throws IOException { + File file = new File(this.rootDirectory, fileName); + FileOutputStream os = new FileOutputStream(file); + ExportUtils.exportUsersToStream(realm, users, JsonSerialization.prettyMapper, os); + } + + @Override + public void close() { + } +} diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProviderFactory.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProviderFactory.java new file mode 100644 index 0000000000..74e7eb43c7 --- /dev/null +++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirExportProviderFactory.java @@ -0,0 +1,36 @@ +package org.keycloak.exportimport.dir; + +import java.io.File; + +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ExportProvider; +import org.keycloak.exportimport.ExportProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class DirExportProviderFactory implements ExportProviderFactory { + + public static final String PROVIDER_ID = "dir"; + + @Override + public ExportProvider create(KeycloakSession session) { + String dir = ExportImportConfig.getDir(); + return dir!=null ? new DirExportProvider(new File(dir)) : new DirExportProvider(); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java new file mode 100644 index 0000000000..3f6fc901df --- /dev/null +++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java @@ -0,0 +1,109 @@ +package org.keycloak.exportimport.dir; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.io.IOException; + +import org.codehaus.jackson.map.ObjectMapper; +import org.jboss.logging.Logger; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ImportProvider; +import org.keycloak.exportimport.Strategy; +import org.keycloak.exportimport.util.ExportImportJob; +import org.keycloak.exportimport.util.ExportImportUtils; +import org.keycloak.exportimport.util.ImportUtils; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class DirImportProvider implements ImportProvider { + + private static final Logger logger = Logger.getLogger(DirImportProvider.class); + + private final File rootDirectory; + + public DirImportProvider() { + // Determine system tmp directory + String tempDir = System.getProperty("java.io.tmpdir"); + + // Delete and recreate directory inside tmp + this.rootDirectory = new File(tempDir + "/keycloak-export"); + if (!this.rootDirectory .exists()) { + throw new IllegalStateException("Directory " + this.rootDirectory + " doesn't exists"); + } + + logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath()); + } + + public DirImportProvider(File rootDirectory) { + this.rootDirectory = rootDirectory; + + logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath()); + } + + @Override + public void importModel(KeycloakSession session, Strategy strategy) throws IOException { + File[] realmFiles = this.rootDirectory.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return (name.endsWith("-realm.json")); + } + }); + + for (File file : realmFiles) { + String fileName = file.getName(); + + // Parse "foo" from "foo-realm.json" + String realmName = fileName.substring(0, fileName.length() - 11); + importRealm(session, realmName, strategy); + } + } + + @Override + public void importRealm(final KeycloakSession session, final String realmName, final Strategy strategy) throws IOException { + File realmFile = new File(this.rootDirectory + File.separator + realmName + "-realm.json"); + File[] userFiles = this.rootDirectory.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return (name.startsWith(realmName)) && (name.endsWith(".json")) && (name.substring(realmName.length()).contains("-users-") ); + } + }); + + // Import realm first + FileInputStream is = new FileInputStream(realmFile); + final RealmRepresentation realmRep = JsonSerialization.readValue(is, RealmRepresentation.class); + + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + ImportUtils.importRealm(session, realmRep, strategy); + } + + }); + + // Import users + for (File userFile : userFiles) { + final FileInputStream fis = new FileInputStream(userFile); + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, fis); + } + }); + } + } + + @Override + public void close() { + + } +} diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java new file mode 100644 index 0000000000..3e39a77850 --- /dev/null +++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProviderFactory.java @@ -0,0 +1,34 @@ +package org.keycloak.exportimport.dir; + +import java.io.File; + +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ImportProvider; +import org.keycloak.exportimport.ImportProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class DirImportProviderFactory implements ImportProviderFactory { + + @Override + public ImportProvider create(KeycloakSession session) { + String dir = ExportImportConfig.getDir(); + return dir!=null ? new DirImportProvider(new File(dir)) : new DirImportProvider(); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return DirExportProviderFactory.PROVIDER_ID; + } +} diff --git a/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory new file mode 100644 index 0000000000..8a8fea4adb --- /dev/null +++ b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory @@ -0,0 +1 @@ +org.keycloak.exportimport.dir.DirExportProviderFactory \ No newline at end of file diff --git a/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory new file mode 100644 index 0000000000..ba9874ed89 --- /dev/null +++ b/export-import/export-import-dir/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory @@ -0,0 +1 @@ +org.keycloak.exportimport.dir.DirImportProviderFactory \ No newline at end of file diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java deleted file mode 100644 index f21d4bdb41..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.keycloak.exportimport; - -/** - * @author Stian Thorgersen - */ -public class ExportImportConfig { - - public static final String ACTION = "keycloak.migration.action"; - public static final String PROVIDER = "keycloak.migration.provider"; - public static final String PROVIDER_DEFAULT = "zip"; - - // used for "directory" provider - public static final String DIR = "keycloak.migration.dir"; - // used for "zip" provider - public static final String FILE = "keycloak.migration.zipFile"; - public static final String PASSWORD = "keycloak.migration.zipPassword"; - - public static String getAction() { - return System.getProperty(ACTION); - } - - public static void setAction(String exportImportAction) { - System.setProperty(ACTION, exportImportAction); - } - - public static String getProvider() { - return System.getProperty(PROVIDER, PROVIDER_DEFAULT); - } - - public static void setProvider(String exportImportProvider) { - System.setProperty(PROVIDER, exportImportProvider); - } - - public static String getDir() { - return System.getProperty(DIR); - } - - public static String setDir(String dir) { - return System.setProperty(DIR, dir); - } - - public static String getZipFile() { - return System.getProperty(FILE); - } - - public static void setZipFile(String exportImportZipFile) { - System.setProperty(FILE, exportImportZipFile); - } - - public static String getZipPassword() { - return System.getProperty(PASSWORD); - } - - public static void setZipPassword(String exportImportZipPassword) { - System.setProperty(PASSWORD, exportImportZipPassword); - } - -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java index 03c9dda2ab..c43d3c5233 100644 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java +++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java @@ -12,14 +12,13 @@ import org.keycloak.util.ProviderLoader; /** * @author Marek Posolda */ -public class ExportImportProviderImpl implements ExportImportProvider { +public class ExportImportProviderImpl { private static final Logger logger = Logger.getLogger(ExportImportProviderImpl.class); public static final String ACTION_EXPORT = "export"; public static final String ACTION_IMPORT = "import"; - @Override public void checkExportImport(KeycloakSessionFactory sessionFactory) { String exportImportAction = ExportImportConfig.getAction(); diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportUtils.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportUtils.java deleted file mode 100644 index cbdf36de15..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportUtils.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.keycloak.exportimport; - -import org.keycloak.models.RealmModel; -import org.keycloak.representations.idm.RealmRepresentation; - -/** - * @author Marek Posolda - */ -public class ExportImportUtils { - - public RealmRepresentation exportRealm(RealmModel realm, boolean includeUsers) { - - return null; - } - - public RealmRepresentation exportUsers(RealmModel realm, int start, int count) { - - return null; - } -} diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java deleted file mode 100644 index 1f0c55e357..0000000000 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.keycloak.exportimport; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Test; -import org.keycloak.model.test.AbstractModelTest; -import org.keycloak.model.test.ImportTest; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.services.managers.ApplianceBootstrap; -import org.keycloak.services.managers.RealmManager; -import org.keycloak.services.resources.KeycloakApplication; -import org.keycloak.util.ProviderLoader; - -import java.util.Iterator; - -/** - * @author Marek Posolda - */ -public abstract class ExportImportTestBase { - - protected KeycloakSessionFactory factory; - - protected KeycloakSession session; - protected RealmManager realmManager; - - @After - public void after() { - System.getProperties().remove("keycloak.model.provider"); - } - - @Test - public void testExportImport() throws Exception { - // Init JPA model - System.setProperty("keycloak.model.provider", getExportModelProvider()); - factory = KeycloakApplication.createSessionFactory(); - - // Bootstrap admin realm - beginTransaction(); - new ApplianceBootstrap().bootstrap(session, "/auth"); - commitTransaction(); - - // Classic import of realm to JPA model - beginTransaction(); - RealmRepresentation rep = AbstractModelTest.loadJson("testrealm.json"); - realmManager = new RealmManager(session); - RealmModel realm = realmManager.createRealm("demo", rep.getRealm()); - realmManager.importRealm(rep, realm); - - commitTransaction(); - - // Full export of realm - exportModel(factory); - - beginTransaction(); - realm = session.model().getRealm("demo"); - String wburkeId = realm.getUser("wburke").getId(); - String appId = realm.getApplicationByName("Application").getId(); - - // Commit transaction and close JPA now - commitTransaction(); - factory.close(); - - // Bootstrap mongo session and factory - System.setProperty("keycloak.model.provider", getImportModelProvider()); - factory = KeycloakApplication.createSessionFactory(); - - // Full import of previous export into mongo - importModel(factory); - - // Verify it's imported in mongo (reusing ImportTest) - beginTransaction(); - RealmModel importedRealm = session.model().getRealm("demo"); - System.out.println("Exported realm: " + realm + ", Imported realm: " + importedRealm); - - Assert.assertEquals(wburkeId, importedRealm.getUser("wburke").getId()); - Assert.assertEquals(appId, importedRealm.getApplicationByName("Application").getId()); - ImportTest.assertDataImportedInRealm(importedRealm); - - // Commit and close Mongo - commitTransaction(); - factory.close(); - } - - protected abstract String getExportModelProvider(); - - protected abstract String getImportModelProvider(); - - protected abstract void exportModel(KeycloakSessionFactory factory); - - protected abstract void importModel(KeycloakSessionFactory factory); - - protected void beginTransaction() { - session = factory.create(); - session.getTransaction().begin(); - realmManager = new RealmManager(session); - } - - protected void commitTransaction() { - session.getTransaction().commit(); - session.close(); - } - - protected ExportImportProvider getExportImportProvider() { - Iterator providers = ProviderLoader.load(ExportImportProvider.class).iterator(); - - if (providers.hasNext()) { - return providers.next(); - } else { - throw new IllegalStateException("ExportImportProvider not found"); - } - } -} diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java index 9705ea6030..32be243a71 100644 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java +++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java @@ -10,29 +10,5 @@ import org.keycloak.models.KeycloakSessionFactory; * @author Marek Posolda */ @Ignore -public class JPAToMongoExportImportTest extends ExportImportTestBase { - - @Override - protected String getExportModelProvider() { - return "jpa"; - } - - @Override - protected String getImportModelProvider() { - return "mongo"; - } - - @Override - protected void exportModel(KeycloakSessionFactory factory) { - ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT); - ExportImportConfig.setProvider(TmpDirExportImportIOProvider.PROVIDER_ID); - getExportImportProvider().checkExportImport(factory); - } - - @Override - protected void importModel(KeycloakSessionFactory factory) { - ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT); - ExportImportConfig.setProvider(TmpDirExportImportIOProvider.PROVIDER_ID); - getExportImportProvider().checkExportImport(factory); - } +public class JPAToMongoExportImportTest { } diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java index 978872f262..686722a29c 100644 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java +++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java @@ -13,59 +13,5 @@ import java.io.File; * @author Marek Posolda */ @Ignore -public class MongoToJPAExportImportTest extends ExportImportTestBase { - - private static final String zipFile = "keycloak-export.zip"; - - @Override - protected String getExportModelProvider() { - return "mongo"; - } - - @Override - protected String getImportModelProvider() { - return "jpa"; - } - - @Override - protected void exportModel(KeycloakSessionFactory factory) { - ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT); - ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID); - File zipFile = getZipFile(); - ExportImportConfig.setZipFile(zipFile.getAbsolutePath()); - ExportImportConfig.setZipPassword("password123"); - - if (zipFile.exists()) { - zipFile.delete(); - } - - new ExportImportProviderImpl().checkExportImport(factory); - } - - @Override - protected void importModel(KeycloakSessionFactory factory) { - ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT); - ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID); - File zipFile = getZipFile(); - ExportImportConfig.setZipFile(zipFile.getAbsolutePath()); - ExportImportConfig.setZipPassword("password-invalid"); - - // Try invalid password - try { - new ExportImportProviderImpl().checkExportImport(factory); - Assert.fail("Not expected to be here. Exception should be thrown"); - } catch (Exception e) {}; - - ExportImportConfig.setZipPassword("password123"); - new ExportImportProviderImpl().checkExportImport(factory); - - if (zipFile.exists()) { - zipFile.delete(); - } - } - - private File getZipFile() { - String tempDir = System.getProperty("java.io.tmpdir"); - return new File(tempDir + File.separator + "keycloak-export.zip"); - } +public class MongoToJPAExportImportTest { } diff --git a/export-import/export-import-single-file/pom.xml b/export-import/export-import-single-file/pom.xml new file mode 100644 index 0000000000..25d65c0e43 --- /dev/null +++ b/export-import/export-import-single-file/pom.xml @@ -0,0 +1,65 @@ + + + + keycloak-export-import-parent + org.keycloak + 1.0-beta-4-SNAPSHOT + ../pom.xml + + 4.0.0 + + keycloak-export-import-single-file + Keycloak Export Import To Single File + + + + + org.keycloak + keycloak-core + ${project.version} + provided + + + org.keycloak + keycloak-model-api + ${project.version} + provided + + + org.keycloak + keycloak-export-import-api + ${project.version} + provided + + + org.codehaus.jackson + jackson-core-asl + provided + + + org.codehaus.jackson + jackson-mapper-asl + provided + + + org.jboss.logging + jboss-logging + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java new file mode 100644 index 0000000000..4a63ca0699 --- /dev/null +++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java @@ -0,0 +1,84 @@ +package org.keycloak.exportimport.singlefile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.codehaus.jackson.map.ObjectMapper; +import org.jboss.logging.Logger; +import org.keycloak.exportimport.ExportProvider; +import org.keycloak.exportimport.util.ExportImportJob; +import org.keycloak.exportimport.util.ExportImportUtils; +import org.keycloak.exportimport.util.ExportUtils; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class SingleFileExportProvider implements ExportProvider { + + private static final Logger logger = Logger.getLogger(SingleFileExportProvider.class); + + private File file; + + public SingleFileExportProvider(File file) { + this.file = file; + } + + public void setFile(File file) { + this.file = file; + } + + @Override + public void exportModel(final KeycloakSession session) throws IOException { + logger.infof("Exporting model into file %s", this.file.getAbsolutePath()); + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + List realms = session.getModel().getRealms(); + List reps = new ArrayList(); + for (RealmModel realm : realms) { + reps.add(ExportUtils.exportRealm(realm, true)); + } + + writeToFile(reps); + } + + }); + + } + + @Override + public void exportRealm(final KeycloakSession session, final String realmName) throws IOException { + logger.infof("Exporting realm '%s' into file %s", realmName, this.file.getAbsolutePath()); + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + RealmModel realm = session.getModel().getRealmByName(realmName); + RealmRepresentation realmRep = ExportUtils.exportRealm(realm, true); + writeToFile(realmRep); + } + + }); + } + + @Override + public void close() { + } + + private ObjectMapper getObjectMapper() { + return JsonSerialization.prettyMapper; + } + + private void writeToFile(Object reps) throws IOException { + FileOutputStream stream = new FileOutputStream(this.file); + getObjectMapper().writeValue(stream, reps); + } +} diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java new file mode 100644 index 0000000000..614c3bacab --- /dev/null +++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProviderFactory.java @@ -0,0 +1,36 @@ +package org.keycloak.exportimport.singlefile; + +import java.io.File; + +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ExportProvider; +import org.keycloak.exportimport.ExportProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class SingleFileExportProviderFactory implements ExportProviderFactory { + + public static final String PROVIDER_ID = "singleFile"; + + @Override + public ExportProvider create(KeycloakSession session) { + String fileName = ExportImportConfig.getFile(); + return new SingleFileExportProvider(new File(fileName)); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java new file mode 100644 index 0000000000..5ac38f400a --- /dev/null +++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java @@ -0,0 +1,58 @@ +package org.keycloak.exportimport.singlefile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jboss.logging.Logger; +import org.keycloak.exportimport.ImportProvider; +import org.keycloak.exportimport.Strategy; +import org.keycloak.exportimport.util.ExportImportJob; +import org.keycloak.exportimport.util.ExportImportUtils; +import org.keycloak.exportimport.util.ExportUtils; +import org.keycloak.exportimport.util.ImportUtils; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class SingleFileImportProvider implements ImportProvider { + + private static final Logger logger = Logger.getLogger(SingleFileImportProvider.class); + + private File file; + + public SingleFileImportProvider(File file) { + this.file = file; + } + + @Override + public void importModel(final KeycloakSession session, final Strategy strategy) throws IOException { + logger.infof("Full importing from file %s", this.file.getAbsolutePath()); + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + FileInputStream is = new FileInputStream(file); + ImportUtils.importFromStream(session, JsonSerialization.mapper, is, strategy); + } + + }); + } + + @Override + public void importRealm(KeycloakSession session, String realmName, Strategy strategy) throws IOException { + // TODO: just that single realm in case that file contains many realms? + importModel(session, strategy); + } + + @Override + public void close() { + + } +} diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java new file mode 100644 index 0000000000..6e3e73261d --- /dev/null +++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java @@ -0,0 +1,34 @@ +package org.keycloak.exportimport.singlefile; + +import java.io.File; + +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ImportProvider; +import org.keycloak.exportimport.ImportProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class SingleFileImportProviderFactory implements ImportProviderFactory { + + @Override + public ImportProvider create(KeycloakSession session) { + String fileName = ExportImportConfig.getFile(); + return new SingleFileImportProvider(new File(fileName)); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return SingleFileExportProviderFactory.PROVIDER_ID; + } +} diff --git a/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory new file mode 100644 index 0000000000..82a1fafc06 --- /dev/null +++ b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory @@ -0,0 +1 @@ +org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory \ No newline at end of file diff --git a/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory new file mode 100644 index 0000000000..d2192b6e84 --- /dev/null +++ b/export-import/export-import-single-file/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory @@ -0,0 +1 @@ +org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory \ No newline at end of file diff --git a/export-import/export-import-zip/pom.xml b/export-import/export-import-zip/pom.xml new file mode 100644 index 0000000000..7738f85b7b --- /dev/null +++ b/export-import/export-import-zip/pom.xml @@ -0,0 +1,70 @@ + + + + keycloak-export-import-parent + org.keycloak + 1.0-beta-4-SNAPSHOT + ../pom.xml + + 4.0.0 + + keycloak-export-import-zip + Keycloak Export Import To Encrypted ZIP + + + + + org.keycloak + keycloak-core + ${project.version} + provided + + + org.keycloak + keycloak-model-api + ${project.version} + provided + + + org.keycloak + keycloak-export-import-api + ${project.version} + provided + + + org.codehaus.jackson + jackson-core-asl + provided + + + org.codehaus.jackson + jackson-mapper-asl + provided + + + org.jboss.logging + jboss-logging + provided + + + de.idyl + winzipaes + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProvider.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProvider.java new file mode 100644 index 0000000000..ac0e0dfe7c --- /dev/null +++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProvider.java @@ -0,0 +1,73 @@ +package org.keycloak.exportimport.zip; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.List; + +import de.idyl.winzipaes.AesZipFileEncrypter; +import de.idyl.winzipaes.impl.AESEncrypter; +import de.idyl.winzipaes.impl.AESEncrypterBC; +import org.jboss.logging.Logger; +import org.keycloak.exportimport.util.ExportUtils; +import org.keycloak.exportimport.util.MultipleStepsExportProvider; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class ZipExportProvider extends MultipleStepsExportProvider { + + private static final Logger logger = Logger.getLogger(ZipExportProvider.class); + + private final AesZipFileEncrypter encrypter; + private final String password; + + public ZipExportProvider(File zipFile, String password) { + if (zipFile.exists()) { + throw new IllegalStateException("File " + zipFile.getAbsolutePath() + " already exists"); + } + this.password = password; + + try { + AESEncrypter encrypter = new AESEncrypterBC(); + this.encrypter = new AesZipFileEncrypter(zipFile, encrypter); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + logger.infof("Exporting into zip file %s", zipFile.getAbsolutePath()); + } + + @Override + protected void writeRealm(String fileName, RealmRepresentation rep) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + JsonSerialization.mapper.writeValue(stream, rep); + + byte[] byteArray = stream.toByteArray(); + ByteArrayInputStream bis = new ByteArrayInputStream(byteArray); + this.encrypter.add(fileName, bis, this.password); + } + + @Override + protected void writeUsers(String fileName, RealmModel realm, List users) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + ExportUtils.exportUsersToStream(realm, users, JsonSerialization.mapper, stream); + + byte[] byteArray = stream.toByteArray(); + ByteArrayInputStream bis = new ByteArrayInputStream(byteArray); + this.encrypter.add(fileName, bis, this.password); + } + + @Override + public void close() { + try { + this.encrypter.close(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } +} diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java new file mode 100644 index 0000000000..03f014760e --- /dev/null +++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipExportProviderFactory.java @@ -0,0 +1,44 @@ +package org.keycloak.exportimport.zip; + +import java.io.File; + +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ExportProvider; +import org.keycloak.exportimport.ExportProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class ZipExportProviderFactory implements ExportProviderFactory { + + + public static final String PROVIDER_ID = "zip"; + + @Override + public ExportProvider create(KeycloakSession session) { + String fileName = ExportImportConfig.getZipFile(); + String password = ExportImportConfig.getZipPassword(); + if (fileName == null) { + throw new IllegalArgumentException("ZIP file for export not provided"); + } + if (password == null) { + throw new IllegalArgumentException("Password for encrypting ZIP not provided"); + } + return new ZipExportProvider(new File(fileName), password); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return PROVIDER_ID; + } +} diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java new file mode 100644 index 0000000000..6ff1f5a244 --- /dev/null +++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java @@ -0,0 +1,109 @@ +package org.keycloak.exportimport.zip; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.zip.DataFormatException; + +import de.idyl.winzipaes.AesZipFileDecrypter; +import de.idyl.winzipaes.impl.AESDecrypter; +import de.idyl.winzipaes.impl.AESDecrypterBC; +import de.idyl.winzipaes.impl.ExtZipEntry; +import org.jboss.logging.Logger; +import org.keycloak.exportimport.ImportProvider; +import org.keycloak.exportimport.Strategy; +import org.keycloak.exportimport.util.ExportImportJob; +import org.keycloak.exportimport.util.ExportImportUtils; +import org.keycloak.exportimport.util.ImportUtils; +import org.keycloak.models.KeycloakSession; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class ZipImportProvider implements ImportProvider { + + private static final Logger logger = Logger.getLogger(ZipImportProvider.class); + + private final AesZipFileDecrypter decrypter; + private final String password; + + public ZipImportProvider(File zipFile, String password) { + try { + if (!zipFile.exists()) { + throw new IllegalStateException("File " + zipFile.getAbsolutePath() + " doesn't exists"); + } + + AESDecrypter decrypter = new AESDecrypterBC(); + this.decrypter = new AesZipFileDecrypter(zipFile, decrypter); + this.password = password; + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + logger.infof("Importing from ZIP file %s", zipFile.getAbsolutePath()); + } + + @Override + public void importModel(KeycloakSession session, Strategy strategy) throws IOException { + for (ExtZipEntry entry : this.decrypter.getEntryList()) { + String entryName = entry.getName(); + if (entryName.endsWith("-realm.json")) { + // Parse "foo" from "foo-realm.json" + String realmName = entryName.substring(0, entryName.length() - 11); + importRealm(session, realmName, strategy); + } + } + } + + @Override + public void importRealm(final KeycloakSession session, final String realmName, final Strategy strategy) throws IOException { + try { + // Import realm first + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + this.decrypter.extractEntry(this.decrypter.getEntry(realmName + "-realm.json"), bos, this.password); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + final RealmRepresentation realmRep = JsonSerialization.mapper.readValue(bis, RealmRepresentation.class); + + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + ImportUtils.importRealm(session, realmRep, strategy); + } + + }); + + + // Import users + for (ExtZipEntry entry : this.decrypter.getEntryList()) { + String name = entry.getName(); + if ( (name.startsWith(realmName)) && (name.endsWith(".json")) && (name.substring(realmName.length()).contains("-users-")) ) { + bos = new ByteArrayOutputStream(); + this.decrypter.extractEntry(entry, bos, this.password); + final ByteArrayInputStream bis2 = new ByteArrayInputStream(bos.toByteArray()); + + ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + + @Override + public void run() throws IOException { + ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, bis2); + } + }); + } + } + } catch (DataFormatException dfe) { + throw new RuntimeException(dfe); + } + } + + @Override + public void close() { + try { + this.decrypter.close(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } +} diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java new file mode 100644 index 0000000000..9127a8bcb9 --- /dev/null +++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProviderFactory.java @@ -0,0 +1,41 @@ +package org.keycloak.exportimport.zip; + +import java.io.File; + +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ImportProvider; +import org.keycloak.exportimport.ImportProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class ZipImportProviderFactory implements ImportProviderFactory { + + @Override + public ImportProvider create(KeycloakSession session) { + String fileName = ExportImportConfig.getZipFile(); + String password = ExportImportConfig.getZipPassword(); + if (fileName == null) { + throw new IllegalArgumentException("ZIP file for import not provided"); + } + if (password == null) { + throw new IllegalArgumentException("Password for decrypting ZIP not provided"); + } + return new ZipImportProvider(new File(fileName), password); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return ZipExportProviderFactory.PROVIDER_ID; + } +} diff --git a/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory new file mode 100644 index 0000000000..dce02f18db --- /dev/null +++ b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportProviderFactory @@ -0,0 +1 @@ +org.keycloak.exportimport.zip.ZipExportProviderFactory \ No newline at end of file diff --git a/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory new file mode 100644 index 0000000000..b88f372286 --- /dev/null +++ b/export-import/export-import-zip/src/main/resources/META-INF/services/org.keycloak.exportimport.ImportProviderFactory @@ -0,0 +1 @@ +org.keycloak.exportimport.zip.ZipImportProviderFactory \ No newline at end of file diff --git a/export-import/pom.xml b/export-import/pom.xml index b9a5a4d2e5..15d39f6a36 100755 --- a/export-import/pom.xml +++ b/export-import/pom.xml @@ -17,6 +17,9 @@ export-import-api export-import-impl + export-import-dir + export-import-single-file + export-import-zip diff --git a/model/api/pom.xml b/model/api/pom.xml index f72d856fea..d0af4dc25a 100755 --- a/model/api/pom.xml +++ b/model/api/pom.xml @@ -30,6 +30,11 @@ ${project.version} provided + + org.jboss.logging + jboss-logging + provided + junit junit diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index 939eebaddc..68eda42528 100644 --- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -3,6 +3,9 @@ package org.keycloak.models.utils; import java.io.IOException; import java.io.StringWriter; import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Set; @@ -10,8 +13,11 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; import org.bouncycastle.openssl.PEMWriter; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; +import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.util.PemUtils; @@ -65,6 +71,23 @@ public final class KeycloakModelUtils { return PemUtils.removeBeginEnd(s); } + public static void generateRealmKeys(RealmModel realm) { + KeyPair keyPair = null; + try { + keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + realm.setPrivateKey(keyPair.getPrivate()); + realm.setPublicKey(keyPair.getPublic()); + } + + public static UserCredentialModel generateSecret(ClientModel app) { + UserCredentialModel secret = UserCredentialModel.generateSecret(); + app.setSecret(secret.getValue()); + return secret; + } + /** * Deep search if given role is descendant of composite role * diff --git a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java similarity index 79% rename from services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java rename to model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 4a27c2d6c1..c7f3e36e71 100755 --- a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -1,4 +1,4 @@ -package org.keycloak.services.managers; +package org.keycloak.models.utils; import org.keycloak.models.ApplicationModel; import org.keycloak.models.AuthenticationProviderModel; @@ -13,9 +13,11 @@ import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.representations.idm.ApplicationRepresentation; import org.keycloak.representations.idm.AuthenticationProviderRepresentation; import org.keycloak.representations.idm.ClaimRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.OAuthClientRepresentation; import org.keycloak.representations.idm.RealmAuditRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; @@ -29,6 +31,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; /** * @author Bill Burke @@ -201,4 +204,53 @@ public class ModelToRepresentation { } return rep; } + + public static ApplicationRepresentation toRepresentation(ApplicationModel applicationModel) { + ApplicationRepresentation rep = new ApplicationRepresentation(); + rep.setId(applicationModel.getId()); + rep.setName(applicationModel.getName()); + rep.setEnabled(applicationModel.isEnabled()); + rep.setAdminUrl(applicationModel.getManagementUrl()); + rep.setPublicClient(applicationModel.isPublicClient()); + rep.setBearerOnly(applicationModel.isBearerOnly()); + rep.setSurrogateAuthRequired(applicationModel.isSurrogateAuthRequired()); + rep.setBaseUrl(applicationModel.getBaseUrl()); + rep.setNotBefore(applicationModel.getNotBefore()); + + Set redirectUris = applicationModel.getRedirectUris(); + if (redirectUris != null) { + rep.setRedirectUris(new LinkedList(redirectUris)); + } + + Set webOrigins = applicationModel.getWebOrigins(); + if (webOrigins != null) { + rep.setWebOrigins(new LinkedList(webOrigins)); + } + + if (!applicationModel.getDefaultRoles().isEmpty()) { + rep.setDefaultRoles(applicationModel.getDefaultRoles().toArray(new String[0])); + } + + return rep; + } + + public static OAuthClientRepresentation toRepresentation(OAuthClientModel model) { + OAuthClientRepresentation rep = new OAuthClientRepresentation(); + rep.setId(model.getId()); + rep.setName(model.getClientId()); + rep.setEnabled(model.isEnabled()); + rep.setPublicClient(model.isPublicClient()); + rep.setDirectGrantsOnly(model.isDirectGrantsOnly()); + Set redirectUris = model.getRedirectUris(); + if (redirectUris != null) { + rep.setRedirectUris(new LinkedList(redirectUris)); + } + + Set webOrigins = model.getWebOrigins(); + if (webOrigins != null) { + rep.setWebOrigins(new LinkedList(webOrigins)); + } + rep.setNotBefore(model.getNotBefore()); + return rep; + } } diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java new file mode 100644 index 0000000000..b031eac75e --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -0,0 +1,671 @@ +package org.keycloak.models.utils; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.iharder.Base64; +import org.jboss.logging.Logger; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.AuthenticationLinkModel; +import org.keycloak.models.AuthenticationProviderModel; +import org.keycloak.models.ClaimMask; +import org.keycloak.models.ClientModel; +import org.keycloak.models.OAuthClientModel; +import org.keycloak.models.PasswordPolicy; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.SocialLinkModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.ApplicationRepresentation; +import org.keycloak.representations.idm.AuthenticationLinkRepresentation; +import org.keycloak.representations.idm.AuthenticationProviderRepresentation; +import org.keycloak.representations.idm.ClaimRepresentation; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.OAuthClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.ScopeMappingRepresentation; +import org.keycloak.representations.idm.SocialLinkRepresentation; +import org.keycloak.representations.idm.UserRepresentation; + +public class RepresentationToModel { + + private static Logger logger = Logger.getLogger(RepresentationToModel.class); + + public static void importRealm(RealmRepresentation rep, RealmModel newRealm) { + newRealm.setName(rep.getRealm()); + if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled()); + if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial()); + if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected()); + if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds()); + if (rep.getMinimumQuickLoginWaitSeconds() != null) newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds()); + if (rep.getWaitIncrementSeconds() != null) newRealm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds()); + if (rep.getQuickLoginCheckMilliSeconds() != null) newRealm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds()); + if (rep.getMaxDeltaTimeSeconds() != null) newRealm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds()); + if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor()); + + if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore()); + + if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); + else newRealm.setAccessTokenLifespan(300); + + if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); + else newRealm.setSsoSessionIdleTimeout(600); + if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); + else newRealm.setSsoSessionMaxLifespan(36000); + + if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); + else newRealm.setAccessCodeLifespan(60); + + if (rep.getAccessCodeLifespanUserAction() != null) + newRealm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); + else newRealm.setAccessCodeLifespanUserAction(300); + + if (rep.isSslNotRequired() != null) newRealm.setSslNotRequired(rep.isSslNotRequired()); + if (rep.isPasswordCredentialGrantAllowed() != null) newRealm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed()); + if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); + if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe()); + if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail()); + if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); + if (rep.isUpdateProfileOnInitialSocialLogin() != null) + newRealm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin()); + if (rep.getPrivateKey() == null || rep.getPublicKey() == null) { + KeycloakModelUtils.generateRealmKeys(newRealm); + } else { + newRealm.setPrivateKeyPem(rep.getPrivateKey()); + newRealm.setPublicKeyPem(rep.getPublicKey()); + } + if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme()); + if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme()); + if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme()); + if (rep.getEmailTheme() != null) newRealm.setEmailTheme(rep.getEmailTheme()); + + if (rep.getRequiredCredentials() != null) { + for (String requiredCred : rep.getRequiredCredentials()) { + addRequiredCredential(newRealm, requiredCred); + } + } else { + addRequiredCredential(newRealm, CredentialRepresentation.PASSWORD); + } + + if (rep.getPasswordPolicy() != null) newRealm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy())); + + if (rep.getApplications() != null) { + Map appMap = createApplications(rep, newRealm); + } + + if (rep.getRoles() != null) { + if (rep.getRoles().getRealm() != null) { // realm roles + for (RoleRepresentation roleRep : rep.getRoles().getRealm()) { + createRole(newRealm, roleRep); + } + } + if (rep.getRoles().getApplication() != null) { + for (Map.Entry> entry : rep.getRoles().getApplication().entrySet()) { + ApplicationModel app = newRealm.getApplicationByName(entry.getKey()); + if (app == null) { + throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey()); + } + for (RoleRepresentation roleRep : entry.getValue()) { + // Application role may already exists (for example if it is defaultRole) + RoleModel role = app.getRole(roleRep.getName()); + if (role == null) { + role = app.addRole(roleRep.getName()); + } + role.setDescription(roleRep.getDescription()); + } + } + } + // now that all roles are created, re-iterate and set up composites + if (rep.getRoles().getRealm() != null) { // realm roles + for (RoleRepresentation roleRep : rep.getRoles().getRealm()) { + RoleModel role = newRealm.getRole(roleRep.getName()); + addComposites(role, roleRep, newRealm); + } + } + if (rep.getRoles().getApplication() != null) { + for (Map.Entry> entry : rep.getRoles().getApplication().entrySet()) { + ApplicationModel app = newRealm.getApplicationByName(entry.getKey()); + if (app == null) { + throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey()); + } + for (RoleRepresentation roleRep : entry.getValue()) { + RoleModel role = app.getRole(roleRep.getName()); + addComposites(role, roleRep, newRealm); + } + } + } + } + + + if (rep.getDefaultRoles() != null) { + for (String roleString : rep.getDefaultRoles()) { + newRealm.addDefaultRole(roleString.trim()); + } + } + + if (rep.getOauthClients() != null) { + createOAuthClients(rep, newRealm); + } + + + // Now that all possible roles and applications are created, create scope mappings + + Map appMap = newRealm.getApplicationNameMap(); + + if (rep.getApplicationScopeMappings() != null) { + + for (Map.Entry> entry : rep.getApplicationScopeMappings().entrySet()) { + ApplicationModel app = appMap.get(entry.getKey()); + if (app == null) { + throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey()); + } + createApplicationScopeMappings(newRealm, app, entry.getValue()); + } + } + + if (rep.getScopeMappings() != null) { + for (ScopeMappingRepresentation scope : rep.getScopeMappings()) { + ClientModel client = newRealm.findClient(scope.getClient()); + for (String roleString : scope.getRoles()) { + RoleModel role = newRealm.getRole(roleString.trim()); + if (role == null) { + role = newRealm.addRole(roleString.trim()); + } + client.addScopeMapping(role); + } + + } + } + + if (rep.getSmtpServer() != null) { + newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer())); + } + + if (rep.getSocialProviders() != null) { + newRealm.setSocialConfig(new HashMap(rep.getSocialProviders())); + } + if (rep.getLdapServer() != null) { + newRealm.setLdapServerConfig(new HashMap(rep.getLdapServer())); + } + + if (rep.getAuthenticationProviders() != null) { + List authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders()); + newRealm.setAuthenticationProviders(authProviderModels); + } else { + List authProviderModels = Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER); + newRealm.setAuthenticationProviders(authProviderModels); + } + + // create users and their role mappings and social mappings + + if (rep.getUsers() != null) { + for (UserRepresentation userRep : rep.getUsers()) { + UserModel user = createUser(newRealm, userRep, appMap); + } + } + } + + public static void updateRealm(RealmRepresentation rep, RealmModel realm) { + if (rep.getRealm() != null) { + realm.setName(rep.getRealm()); + } + if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); + if (rep.isSocial() != null) realm.setSocial(rep.isSocial()); + if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected()); + if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds()); + if (rep.getMinimumQuickLoginWaitSeconds() != null) realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds()); + if (rep.getWaitIncrementSeconds() != null) realm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds()); + if (rep.getQuickLoginCheckMilliSeconds() != null) realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds()); + if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds()); + if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor()); + if (rep.isPasswordCredentialGrantAllowed() != null) realm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed()); + if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); + if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe()); + if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); + if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); + if (rep.isUpdateProfileOnInitialSocialLogin() != null) + realm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin()); + if (rep.isSslNotRequired() != null) realm.setSslNotRequired((rep.isSslNotRequired())); + if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); + if (rep.getAccessCodeLifespanUserAction() != null) + realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); + if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore()); + if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); + if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); + if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); + if (rep.getRequiredCredentials() != null) { + realm.updateRequiredCredentials(rep.getRequiredCredentials()); + } + if (rep.getLoginTheme() != null) realm.setLoginTheme(rep.getLoginTheme()); + if (rep.getAccountTheme() != null) realm.setAccountTheme(rep.getAccountTheme()); + if (rep.getAdminTheme() != null) realm.setAdminTheme(rep.getAdminTheme()); + if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme()); + + if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy())); + + if (rep.getDefaultRoles() != null) { + realm.updateDefaultRoles(rep.getDefaultRoles().toArray(new String[rep.getDefaultRoles().size()])); + } + + if (rep.getSmtpServer() != null) { + realm.setSmtpConfig(new HashMap(rep.getSmtpServer())); + } + + if (rep.getSocialProviders() != null) { + realm.setSocialConfig(new HashMap(rep.getSocialProviders())); + } + + if (rep.getLdapServer() != null) { + realm.setLdapServerConfig(new HashMap(rep.getLdapServer())); + } + if (rep.getAuthenticationProviders() != null) { + List authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders()); + realm.setAuthenticationProviders(authProviderModels); + } + + if ("GENERATE".equals(rep.getPublicKey())) { + KeycloakModelUtils.generateRealmKeys(realm); + } + } + + // Basic realm stuff + + public static void addRequiredCredential(RealmModel newRealm, String requiredCred) { + newRealm.addRequiredCredential(requiredCred); + } + + + private static List convertAuthenticationProviders(List authenticationProviders) { + List result = new ArrayList(); + + for (AuthenticationProviderRepresentation representation : authenticationProviders) { + AuthenticationProviderModel model = new AuthenticationProviderModel(representation.getProviderName(), + representation.isPasswordUpdateSupported(), representation.getConfig()); + result.add(model); + } + return result; + } + + // Roles + + public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) { + RoleModel role = roleRep.getId()!=null ? newRealm.addRole(roleRep.getId(), roleRep.getName()) : newRealm.addRole(roleRep.getName()); + if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription()); + } + + private static void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) { + if (roleRep.getComposites() == null) return; + if (roleRep.getComposites().getRealm() != null) { + for (String roleStr : roleRep.getComposites().getRealm()) { + RoleModel realmRole = realm.getRole(roleStr); + if (realmRole == null) throw new RuntimeException("Unable to find composite realm role: " + roleStr); + role.addCompositeRole(realmRole); + } + } + if (roleRep.getComposites().getApplication() != null) { + for (Map.Entry> entry : roleRep.getComposites().getApplication().entrySet()) { + ApplicationModel app = realm.getApplicationByName(entry.getKey()); + if (app == null) { + throw new RuntimeException("App doesn't exist in role definitions: " + roleRep.getName()); + } + for (String roleStr : entry.getValue()) { + RoleModel appRole = app.getRole(roleStr); + if (appRole == null) throw new RuntimeException("Unable to find composite app role: " + roleStr); + role.addCompositeRole(appRole); + } + + } + + } + + } + + // APPLICATIONS + + private static Map createApplications(RealmRepresentation rep, RealmModel realm) { + Map appMap = new HashMap(); + for (ApplicationRepresentation resourceRep : rep.getApplications()) { + ApplicationModel app = createApplication(realm, resourceRep); + appMap.put(app.getName(), app); + } + return appMap; + } + + /** + * Does not create scope or role mappings! + * + * @param realm + * @param resourceRep + * @return + */ + public static ApplicationModel createApplication(RealmModel realm, ApplicationRepresentation resourceRep) { + logger.debug("************ CREATE APPLICATION: {0}" + resourceRep.getName()); + ApplicationModel applicationModel = resourceRep.getId()!=null ? realm.addApplication(resourceRep.getId(), resourceRep.getName()) : realm.addApplication(resourceRep.getName()); + if (resourceRep.isEnabled() != null) applicationModel.setEnabled(resourceRep.isEnabled()); + applicationModel.setManagementUrl(resourceRep.getAdminUrl()); + if (resourceRep.isSurrogateAuthRequired() != null) + applicationModel.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired()); + applicationModel.setBaseUrl(resourceRep.getBaseUrl()); + if (resourceRep.isBearerOnly() != null) applicationModel.setBearerOnly(resourceRep.isBearerOnly()); + if (resourceRep.isPublicClient() != null) applicationModel.setPublicClient(resourceRep.isPublicClient()); + applicationModel.updateApplication(); + + if (resourceRep.getNotBefore() != null) { + applicationModel.setNotBefore(resourceRep.getNotBefore()); + } + + applicationModel.setSecret(resourceRep.getSecret()); + if (applicationModel.getSecret() == null) { + KeycloakModelUtils.generateSecret(applicationModel); + } + + + if (resourceRep.getRedirectUris() != null) { + for (String redirectUri : resourceRep.getRedirectUris()) { + applicationModel.addRedirectUri(redirectUri); + } + } + if (resourceRep.getWebOrigins() != null) { + for (String webOrigin : resourceRep.getWebOrigins()) { + logger.debugv("Application: {0} webOrigin: {1}", resourceRep.getName(), webOrigin); + applicationModel.addWebOrigin(webOrigin); + } + } else { + // add origins from redirect uris + if (resourceRep.getRedirectUris() != null) { + Set origins = new HashSet(); + for (String redirectUri : resourceRep.getRedirectUris()) { + logger.info("add redirectUri to origin: " + redirectUri); + if (redirectUri.startsWith("http:")) { + URI uri = URI.create(redirectUri); + String origin = uri.getScheme() + "://" + uri.getHost(); + if (uri.getPort() != -1) { + origin += ":" + uri.getPort(); + } + logger.debugv("adding default application origin: {0}" , origin); + origins.add(origin); + } + } + if (origins.size() > 0) { + applicationModel.setWebOrigins(origins); + } + } + } + + if (resourceRep.getDefaultRoles() != null) { + applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles()); + } + + if (resourceRep.getClaims() != null) { + setClaims(applicationModel, resourceRep.getClaims()); + } else { + applicationModel.setAllowedClaimsMask(ClaimMask.USERNAME); + } + + return applicationModel; + } + + public static void updateApplication(ApplicationRepresentation rep, ApplicationModel resource) { + if (rep.getName() != null) resource.setName(rep.getName()); + if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled()); + if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly()); + if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); + if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl()); + if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl()); + if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired()); + resource.updateApplication(); + + if (rep.getNotBefore() != null) { + resource.setNotBefore(rep.getNotBefore()); + } + if (rep.getDefaultRoles() != null) { + resource.updateDefaultRoles(rep.getDefaultRoles()); + } + + List redirectUris = rep.getRedirectUris(); + if (redirectUris != null) { + resource.setRedirectUris(new HashSet(redirectUris)); + } + + List webOrigins = rep.getWebOrigins(); + if (webOrigins != null) { + resource.setWebOrigins(new HashSet(webOrigins)); + } + + if (rep.getClaims() != null) { + setClaims(resource, rep.getClaims()); + } + } + + public static void setClaims(ClientModel model, ClaimRepresentation rep) { + long mask = model.getAllowedClaimsMask(); + if (rep.getAddress()) { + mask |= ClaimMask.ADDRESS; + } else { + mask &= ~ClaimMask.ADDRESS; + } + if (rep.getEmail()) { + mask |= ClaimMask.EMAIL; + } else { + mask &= ~ClaimMask.EMAIL; + } + if (rep.getGender()) { + mask |= ClaimMask.GENDER; + } else { + mask &= ~ClaimMask.GENDER; + } + if (rep.getLocale()) { + mask |= ClaimMask.LOCALE; + } else { + mask &= ~ClaimMask.LOCALE; + } + if (rep.getName()) { + mask |= ClaimMask.NAME; + } else { + mask &= ~ClaimMask.NAME; + } + if (rep.getPhone()) { + mask |= ClaimMask.PHONE; + } else { + mask &= ~ClaimMask.PHONE; + } + if (rep.getPicture()) { + mask |= ClaimMask.PICTURE; + } else { + mask &= ~ClaimMask.PICTURE; + } + if (rep.getProfile()) { + mask |= ClaimMask.PROFILE; + } else { + mask &= ~ClaimMask.PROFILE; + } + if (rep.getUsername()) { + mask |= ClaimMask.USERNAME; + } else { + mask &= ~ClaimMask.USERNAME; + } + if (rep.getWebsite()) { + mask |= ClaimMask.WEBSITE; + } else { + mask &= ~ClaimMask.WEBSITE; + } + model.setAllowedClaimsMask(mask); + } + + // OAuth clients + + private static void createOAuthClients(RealmRepresentation realmRep, RealmModel realm) { + for (OAuthClientRepresentation rep : realmRep.getOauthClients()) { + createOAuthClient(rep, realm); + } + } + + public static OAuthClientModel createOAuthClient(String id, String name, RealmModel realm) { + OAuthClientModel model = id!=null ? realm.addOAuthClient(id, name) : realm.addOAuthClient(name); + KeycloakModelUtils.generateSecret(model); + return model; + } + + public static OAuthClientModel createOAuthClient(OAuthClientRepresentation rep, RealmModel realm) { + OAuthClientModel model = createOAuthClient(rep.getId(), rep.getName(), realm); + updateOAuthClient(rep, model); + return model; + } + + public static void updateOAuthClient(OAuthClientRepresentation rep, OAuthClientModel model) { + if (rep.getName() != null) model.setClientId(rep.getName()); + if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled()); + if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient()); + if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly()); + if (rep.getClaims() != null) { + setClaims(model, rep.getClaims()); + } + if (rep.getNotBefore() != null) { + model.setNotBefore(rep.getNotBefore()); + } + if (rep.getSecret() != null) model.setSecret(rep.getSecret()); + List redirectUris = rep.getRedirectUris(); + if (redirectUris != null) { + model.setRedirectUris(new HashSet(redirectUris)); + } + + List webOrigins = rep.getWebOrigins(); + if (webOrigins != null) { + model.setWebOrigins(new HashSet(webOrigins)); + } + + if (rep.getClaims() != null) { + setClaims(model, rep.getClaims()); + } + + if (rep.getNotBefore() != null) { + model.setNotBefore(rep.getNotBefore()); + } + + } + + // Scope mappings + + public static void createApplicationScopeMappings(RealmModel realm, ApplicationModel applicationModel, List mappings) { + for (ScopeMappingRepresentation mapping : mappings) { + ClientModel client = realm.findClient(mapping.getClient()); + for (String roleString : mapping.getRoles()) { + RoleModel role = applicationModel.getRole(roleString.trim()); + if (role == null) { + role = applicationModel.addRole(roleString.trim()); + } + client.addScopeMapping(role); + } + } + } + + // Users + + public static UserModel createUser(RealmModel newRealm, UserRepresentation userRep, Map appMap) { + UserModel user = newRealm.addUser(userRep.getId(), userRep.getUsername(), false); + user.setEnabled(userRep.isEnabled()); + user.setEmail(userRep.getEmail()); + user.setFirstName(userRep.getFirstName()); + user.setLastName(userRep.getLastName()); + if (userRep.getAttributes() != null) { + for (Map.Entry entry : userRep.getAttributes().entrySet()) { + user.setAttribute(entry.getKey(), entry.getValue()); + } + } + if (userRep.getRequiredActions() != null) { + for (String requiredAction : userRep.getRequiredActions()) { + user.addRequiredAction(UserModel.RequiredAction.valueOf(requiredAction)); + } + } + if (userRep.getCredentials() != null) { + for (CredentialRepresentation cred : userRep.getCredentials()) { + updateCredential(user, cred); + } + } + if (userRep.getAuthenticationLink() != null) { + AuthenticationLinkRepresentation link = userRep.getAuthenticationLink(); + AuthenticationLinkModel authLink = new AuthenticationLinkModel(link.getAuthProvider(), link.getAuthUserId()); + user.setAuthenticationLink(authLink); + } + if (userRep.getSocialLinks() != null) { + for (SocialLinkRepresentation socialLink : userRep.getSocialLinks()) { + SocialLinkModel mappingModel = new SocialLinkModel(socialLink.getSocialProvider(), socialLink.getSocialUserId(), socialLink.getSocialUsername()); + newRealm.addSocialLink(user, mappingModel); + } + } + if (userRep.getRealmRoles() != null) { + for (String roleString : userRep.getRealmRoles()) { + RoleModel role = newRealm.getRole(roleString.trim()); + if (role == null) { + role = newRealm.addRole(roleString.trim()); + } + user.grantRole(role); + } + } + if (userRep.getApplicationRoles() != null) { + for (Map.Entry> entry : userRep.getApplicationRoles().entrySet()) { + ApplicationModel app = appMap.get(entry.getKey()); + if (app == null) { + throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey()); + } + createApplicationRoleMappings(app, user, entry.getValue()); + } + } + return user; + } + + // Detect if it is "plain-text" or "hashed" representation and update model according to it + private static void updateCredential(UserModel user, CredentialRepresentation cred) { + if (cred.getValue() != null) { + UserCredentialModel plainTextCred = convertCredential(cred); + user.updateCredential(plainTextCred); + } else { + UserCredentialValueModel hashedCred = new UserCredentialValueModel(); + hashedCred.setType(cred.getType()); + hashedCred.setDevice(cred.getDevice()); + hashedCred.setHashIterations(cred.getHashIterations()); + try { + hashedCred.setSalt(Base64.decode(cred.getSalt())); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + hashedCred.setValue(cred.getHashedSaltedValue()); + user.updateCredentialDirectly(hashedCred); + } + } + + public static UserCredentialModel convertCredential(CredentialRepresentation cred) { + UserCredentialModel credential = new UserCredentialModel(); + credential.setType(cred.getType()); + credential.setValue(cred.getValue()); + return credential; + } + + // Role mappings + + public static void createApplicationRoleMappings(ApplicationModel applicationModel, UserModel user, List roleNames) { + if (user == null) { + throw new RuntimeException("User not found"); + } + + for (String roleName : roleNames) { + RoleModel role = applicationModel.getRole(roleName.trim()); + if (role == null) { + role = applicationModel.addRole(roleName.trim()); + } + user.grantRole(role); + + } + } + +} diff --git a/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java b/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java index 7593ae5320..412ef28d73 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java @@ -5,7 +5,6 @@ import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import org.keycloak.models.ApplicationModel; -import org.keycloak.models.Constants; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.PasswordPolicy; @@ -16,10 +15,8 @@ import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; -import org.keycloak.models.UserSessionModel; -import org.keycloak.models.UserSessionProvider; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.services.managers.OAuthClientManager; import org.keycloak.services.managers.RealmManager; import java.util.ArrayList; @@ -151,8 +148,9 @@ public class AdapterTest extends AbstractModelTest { public void testOAuthClient() throws Exception { test1CreateRealm(); - OAuthClientModel oauth = new OAuthClientManager(realmModel).create("oauth-client"); - oauth = realmModel.getOAuthClient("oauth-client"); + RepresentationToModel.createOAuthClient(null, "oauth-client", realmModel); + OAuthClientModel oauth = realmModel.getOAuthClient("oauth-client"); + Assert.assertNotNull(oauth); } @Test diff --git a/model/tests/src/test/java/org/keycloak/model/test/ApplicationModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/ApplicationModelTest.java index f8869bcde1..58c0444b12 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/ApplicationModelTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/ApplicationModelTest.java @@ -7,6 +7,8 @@ import org.keycloak.models.ApplicationModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ApplicationRepresentation; import org.keycloak.services.managers.ApplicationManager; @@ -57,10 +59,10 @@ public class ApplicationModelTest extends AbstractModelTest { @Test public void json() { - ApplicationRepresentation representation = appManager.toRepresentation(application); + ApplicationRepresentation representation = ModelToRepresentation.toRepresentation(application); RealmModel realm = realmManager.createRealm("copy"); - ApplicationModel copy = appManager.createApplication(realm, representation); + ApplicationModel copy = RepresentationToModel.createApplication(realm, representation); assertEquals(application, copy); } diff --git a/model/tests/src/test/java/org/keycloak/model/test/ModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/ModelTest.java index 05fee92813..9d53df165c 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/ModelTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/ModelTest.java @@ -4,13 +4,10 @@ import org.junit.Assert; import org.junit.Test; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import java.util.HashMap; -import java.util.Iterator; -import java.util.List; public class ModelTest extends AbstractModelTest { diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java index 6f13dac86b..cf4d70c81b 100644 --- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java +++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java @@ -37,7 +37,7 @@ public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredenti @Override public void setup(LDAPIdentityStore store) { - // TODO: Don't setup it here once PLIDM-508 is fixed + // TODO: Don't setup it here once PLINK-508 is fixed if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) { String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate"); this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512"; diff --git a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java index 56cee64ac2..ebaf3d797c 100755 --- a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java +++ b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java @@ -12,6 +12,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.idm.CredentialRepresentation; import java.util.Arrays; @@ -58,7 +59,7 @@ public class ApplianceBootstrap { realm.setAccessCodeLifespanUserAction(300); realm.setSslNotRequired(true); realm.setRegistrationAllowed(false); - manager.generateRealmKeys(realm); + KeycloakModelUtils.generateRealmKeys(realm); realm.setAuthenticationProviders(Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER)); realm.setAuditListeners(Collections.singleton("jboss-logging")); diff --git a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java index 1f186b1612..3f9daca73e 100755 --- a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java @@ -4,26 +4,15 @@ import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.jboss.logging.Logger; import org.keycloak.models.ApplicationModel; -import org.keycloak.models.ClaimMask; -import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionProvider; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.adapters.config.BaseRealmConfig; -import org.keycloak.representations.idm.ApplicationRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.representations.idm.ScopeMappingRepresentation; -import org.keycloak.representations.idm.UserRoleMappingRepresentation; import java.net.URI; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.Set; /** * @author Bill Burke @@ -41,112 +30,9 @@ public class ApplicationManager { public ApplicationManager() { } - - /** - * Does not create scope or role mappings! - * - * @param realm - * @param resourceRep - * @return - */ - public ApplicationModel createApplication(RealmModel realm, ApplicationRepresentation resourceRep) { - logger.debug("************ CREATE APPLICATION: {0}" + resourceRep.getName()); - ApplicationModel applicationModel = realm.addApplication(resourceRep.getName()); - if (resourceRep.isEnabled() != null) applicationModel.setEnabled(resourceRep.isEnabled()); - applicationModel.setManagementUrl(resourceRep.getAdminUrl()); - if (resourceRep.isSurrogateAuthRequired() != null) - applicationModel.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired()); - applicationModel.setBaseUrl(resourceRep.getBaseUrl()); - if (resourceRep.isBearerOnly() != null) applicationModel.setBearerOnly(resourceRep.isBearerOnly()); - if (resourceRep.isPublicClient() != null) applicationModel.setPublicClient(resourceRep.isPublicClient()); - applicationModel.updateApplication(); - - if (resourceRep.getNotBefore() != null) { - applicationModel.setNotBefore(resourceRep.getNotBefore()); - } - - applicationModel.setSecret(resourceRep.getSecret()); - if (applicationModel.getSecret() == null) { - generateSecret(applicationModel); - } - - - if (resourceRep.getRedirectUris() != null) { - for (String redirectUri : resourceRep.getRedirectUris()) { - applicationModel.addRedirectUri(redirectUri); - } - } - if (resourceRep.getWebOrigins() != null) { - for (String webOrigin : resourceRep.getWebOrigins()) { - logger.debugv("Application: {0} webOrigin: {1}", resourceRep.getName(), webOrigin); - applicationModel.addWebOrigin(webOrigin); - } - } else { - // add origins from redirect uris - if (resourceRep.getRedirectUris() != null) { - Set origins = new HashSet(); - for (String redirectUri : resourceRep.getRedirectUris()) { - logger.info("add redirectUri to origin: " + redirectUri); - if (redirectUri.startsWith("http:")) { - URI uri = URI.create(redirectUri); - String origin = uri.getScheme() + "://" + uri.getHost(); - if (uri.getPort() != -1) { - origin += ":" + uri.getPort(); - } - logger.debugv("adding default application origin: {0}" , origin); - origins.add(origin); - } - } - if (origins.size() > 0) { - applicationModel.setWebOrigins(origins); - } - } - } - - if (resourceRep.getDefaultRoles() != null) { - applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles()); - } - - if (resourceRep.getClaims() != null) { - ClaimManager.setClaims(applicationModel, resourceRep.getClaims()); - } else { - applicationModel.setAllowedClaimsMask(ClaimMask.USERNAME); - } - - return applicationModel; - } - - public void createRoleMappings(ApplicationModel applicationModel, UserModel user, List roleNames) { - for (String roleName : roleNames) { - if (user == null) { - throw new RuntimeException("User not found"); - } - - RoleModel role = applicationModel.getRole(roleName.trim()); - if (role == null) { - role = applicationModel.addRole(roleName.trim()); - } - user.grantRole(role); - - } - } - - public void createScopeMappings(RealmModel realm, ApplicationModel applicationModel, List mappings) { - for (ScopeMappingRepresentation mapping : mappings) { - for (String roleString : mapping.getRoles()) { - RoleModel role = applicationModel.getRole(roleString.trim()); - if (role == null) { - role = applicationModel.addRole(roleString.trim()); - } - ClientModel client = realm.findClient(mapping.getClient()); - client.addScopeMapping(role); - } - } - } - public ApplicationModel createApplication(RealmModel realm, String name) { ApplicationModel app = realm.addApplication(name); - generateSecret(app); + KeycloakModelUtils.generateSecret(app); return app; } @@ -163,74 +49,6 @@ public class ApplicationManager { } } - public UserCredentialModel generateSecret(ApplicationModel app) { - UserCredentialModel secret = UserCredentialModel.generateSecret(); - app.setSecret(secret.getValue()); - return secret; - } - - public void updateApplication(ApplicationRepresentation rep, ApplicationModel resource) { - if (rep.getName() != null) resource.setName(rep.getName()); - if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled()); - if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly()); - if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); - if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl()); - if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl()); - if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired()); - resource.updateApplication(); - - if (rep.getNotBefore() != null) { - resource.setNotBefore(rep.getNotBefore()); - } - if (rep.getDefaultRoles() != null) { - resource.updateDefaultRoles(rep.getDefaultRoles()); - } - - List redirectUris = rep.getRedirectUris(); - if (redirectUris != null) { - resource.setRedirectUris(new HashSet(redirectUris)); - } - - List webOrigins = rep.getWebOrigins(); - if (webOrigins != null) { - resource.setWebOrigins(new HashSet(webOrigins)); - } - - if (rep.getClaims() != null) { - ClaimManager.setClaims(resource, rep.getClaims()); - } - } - - public ApplicationRepresentation toRepresentation(ApplicationModel applicationModel) { - ApplicationRepresentation rep = new ApplicationRepresentation(); - rep.setId(applicationModel.getId()); - rep.setName(applicationModel.getName()); - rep.setEnabled(applicationModel.isEnabled()); - rep.setAdminUrl(applicationModel.getManagementUrl()); - rep.setPublicClient(applicationModel.isPublicClient()); - rep.setBearerOnly(applicationModel.isBearerOnly()); - rep.setSurrogateAuthRequired(applicationModel.isSurrogateAuthRequired()); - rep.setBaseUrl(applicationModel.getBaseUrl()); - rep.setNotBefore(applicationModel.getNotBefore()); - - Set redirectUris = applicationModel.getRedirectUris(); - if (redirectUris != null) { - rep.setRedirectUris(new LinkedList(redirectUris)); - } - - Set webOrigins = applicationModel.getWebOrigins(); - if (webOrigins != null) { - rep.setWebOrigins(new LinkedList(webOrigins)); - } - - if (!applicationModel.getDefaultRoles().isEmpty()) { - rep.setDefaultRoles(applicationModel.getDefaultRoles().toArray(new String[0])); - } - - return rep; - - } - @JsonPropertyOrder({"realm", "realm-public-key", "bearer-only", "auth-server-url", "ssl-not-required", "resource", "public-client", "credentials", "use-resource-role-mappings"}) diff --git a/services/src/main/java/org/keycloak/services/managers/ClaimManager.java b/services/src/main/java/org/keycloak/services/managers/ClaimManager.java deleted file mode 100755 index af1ab62d41..0000000000 --- a/services/src/main/java/org/keycloak/services/managers/ClaimManager.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.keycloak.services.managers; - -import org.keycloak.models.ClaimMask; -import org.keycloak.models.ClientModel; -import org.keycloak.representations.idm.ClaimRepresentation; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class ClaimManager { - public static void setClaims(ClientModel model, ClaimRepresentation rep) { - long mask = model.getAllowedClaimsMask(); - if (rep.getAddress()) { - mask |= ClaimMask.ADDRESS; - } else { - mask &= ~ClaimMask.ADDRESS; - } - if (rep.getEmail()) { - mask |= ClaimMask.EMAIL; - } else { - mask &= ~ClaimMask.EMAIL; - } - if (rep.getGender()) { - mask |= ClaimMask.GENDER; - } else { - mask &= ~ClaimMask.GENDER; - } - if (rep.getLocale()) { - mask |= ClaimMask.LOCALE; - } else { - mask &= ~ClaimMask.LOCALE; - } - if (rep.getName()) { - mask |= ClaimMask.NAME; - } else { - mask &= ~ClaimMask.NAME; - } - if (rep.getPhone()) { - mask |= ClaimMask.PHONE; - } else { - mask &= ~ClaimMask.PHONE; - } - if (rep.getPicture()) { - mask |= ClaimMask.PICTURE; - } else { - mask &= ~ClaimMask.PICTURE; - } - if (rep.getProfile()) { - mask |= ClaimMask.PROFILE; - } else { - mask &= ~ClaimMask.PROFILE; - } - if (rep.getUsername()) { - mask |= ClaimMask.USERNAME; - } else { - mask &= ~ClaimMask.USERNAME; - } - if (rep.getWebsite()) { - mask |= ClaimMask.WEBSITE; - } else { - mask &= ~ClaimMask.WEBSITE; - } - model.setAllowedClaimsMask(mask); - } -} diff --git a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java index df13eb4679..b8c1c886f1 100755 --- a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java +++ b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java @@ -2,22 +2,15 @@ package org.keycloak.services.managers; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; -import org.keycloak.models.ApplicationModel; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserSessionProvider; import org.keycloak.representations.adapters.config.BaseRealmConfig; import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.representations.idm.OAuthClientRepresentation; import java.net.URI; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.Set; /** * @author Bill Burke @@ -26,35 +19,11 @@ import java.util.Set; public class OAuthClientManager { private RealmManager realmManager; - protected RealmModel realm; - - public OAuthClientManager(RealmModel realm) { - this.realm = realm; - } public OAuthClientManager(RealmManager realmManager) { this.realmManager = realmManager; } - public UserCredentialModel generateSecret(OAuthClientModel app) { - UserCredentialModel secret = UserCredentialModel.generateSecret(); - app.setSecret(secret.getValue()); - return secret; - } - - - public OAuthClientModel create(String name) { - OAuthClientModel model = realm.addOAuthClient(name); - generateSecret(model); - return model; - } - - public OAuthClientModel create(OAuthClientRepresentation rep) { - OAuthClientModel model = create(rep.getName()); - update(rep, model); - return model; - } - public boolean removeClient(RealmModel realm, OAuthClientModel client) { if (realm.removeOAuthClient(client.getId())) { UserSessionProvider sessions = realmManager.getSession().sessions(); @@ -67,58 +36,6 @@ public class OAuthClientManager { } } - public void update(OAuthClientRepresentation rep, OAuthClientModel model) { - if (rep.getName() != null) model.setClientId(rep.getName()); - if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled()); - if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient()); - if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly()); - if (rep.getClaims() != null) { - ClaimManager.setClaims(model, rep.getClaims()); - } - if (rep.getNotBefore() != null) { - model.setNotBefore(rep.getNotBefore()); - } - if (rep.getSecret() != null) model.setSecret(rep.getSecret()); - List redirectUris = rep.getRedirectUris(); - if (redirectUris != null) { - model.setRedirectUris(new HashSet(redirectUris)); - } - - List webOrigins = rep.getWebOrigins(); - if (webOrigins != null) { - model.setWebOrigins(new HashSet(webOrigins)); - } - - if (rep.getClaims() != null) { - ClaimManager.setClaims(model, rep.getClaims()); - } - - if (rep.getNotBefore() != null) { - model.setNotBefore(rep.getNotBefore()); - } - - } - - public static OAuthClientRepresentation toRepresentation(OAuthClientModel model) { - OAuthClientRepresentation rep = new OAuthClientRepresentation(); - rep.setId(model.getId()); - rep.setName(model.getClientId()); - rep.setEnabled(model.isEnabled()); - rep.setPublicClient(model.isPublicClient()); - rep.setDirectGrantsOnly(model.isDirectGrantsOnly()); - Set redirectUris = model.getRedirectUris(); - if (redirectUris != null) { - rep.setRedirectUris(new LinkedList(redirectUris)); - } - - Set webOrigins = model.getWebOrigins(); - if (webOrigins != null) { - rep.setWebOrigins(new LinkedList(webOrigins)); - } - rep.setNotBefore(model.getNotBefore()); - return rep; - } - @JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required", "resource", "public-client", "credentials"}) public static class InstallationAdapterConfig extends BaseRealmConfig { diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index 29cc8b2487..dcdc27695e 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -1,48 +1,25 @@ package org.keycloak.services.managers; import org.jboss.logging.Logger; +import org.keycloak.exportimport.util.ExportImportUtils; import org.keycloak.models.AccountRoles; import org.keycloak.models.AdminRoles; import org.keycloak.models.ApplicationModel; -import org.keycloak.models.AuthenticationLinkModel; -import org.keycloak.models.AuthenticationProviderModel; -import org.keycloak.models.ClientModel; import org.keycloak.Config; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelProvider; -import org.keycloak.models.OAuthClientModel; -import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.SocialLinkModel; -import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; -import org.keycloak.models.UserModel.RequiredAction; import org.keycloak.models.UserSessionProvider; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.representations.idm.ApplicationRepresentation; -import org.keycloak.representations.idm.AuthenticationLinkRepresentation; -import org.keycloak.representations.idm.AuthenticationProviderRepresentation; -import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.representations.idm.OAuthClientRepresentation; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.RealmAuditRepresentation; import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.representations.idm.ScopeMappingRepresentation; -import org.keycloak.representations.idm.SocialLinkRepresentation; -import org.keycloak.representations.idm.UserRepresentation; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; /** * Per request object @@ -128,10 +105,6 @@ public class RealmManager { adminConsole.addScopeMapping(adminRole); } - public String getMasterRealmAdminApplicationName(RealmModel realm) { - return realm.getName() + "-realm"; - } - public String getRealmAdminApplicationName(RealmModel realm) { return "realm-management"; } @@ -162,80 +135,6 @@ public class RealmManager { return removed; } - public void generateRealmKeys(RealmModel realm) { - KeyPair keyPair = null; - try { - keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - realm.setPrivateKey(keyPair.getPrivate()); - realm.setPublicKey(keyPair.getPublic()); - } - - public void updateRealm(RealmRepresentation rep, RealmModel realm) { - if (rep.getRealm() != null) { - realm.setName(rep.getRealm()); - } - if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); - if (rep.isSocial() != null) realm.setSocial(rep.isSocial()); - if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected()); - if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds()); - if (rep.getMinimumQuickLoginWaitSeconds() != null) realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds()); - if (rep.getWaitIncrementSeconds() != null) realm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds()); - if (rep.getQuickLoginCheckMilliSeconds() != null) realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds()); - if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds()); - if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor()); - if (rep.isPasswordCredentialGrantAllowed() != null) realm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed()); - if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); - if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe()); - if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); - if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); - if (rep.isUpdateProfileOnInitialSocialLogin() != null) - realm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin()); - if (rep.isSslNotRequired() != null) realm.setSslNotRequired((rep.isSslNotRequired())); - if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); - if (rep.getAccessCodeLifespanUserAction() != null) - realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); - if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore()); - if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); - if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); - if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); - if (rep.getRequiredCredentials() != null) { - realm.updateRequiredCredentials(rep.getRequiredCredentials()); - } - if (rep.getLoginTheme() != null) realm.setLoginTheme(rep.getLoginTheme()); - if (rep.getAccountTheme() != null) realm.setAccountTheme(rep.getAccountTheme()); - if (rep.getAdminTheme() != null) realm.setAdminTheme(rep.getAdminTheme()); - if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme()); - - if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy())); - - if (rep.getDefaultRoles() != null) { - realm.updateDefaultRoles(rep.getDefaultRoles().toArray(new String[rep.getDefaultRoles().size()])); - } - - if (rep.getSmtpServer() != null) { - realm.setSmtpConfig(new HashMap(rep.getSmtpServer())); - } - - if (rep.getSocialProviders() != null) { - realm.setSocialConfig(new HashMap(rep.getSocialProviders())); - } - - if (rep.getLdapServer() != null) { - realm.setLdapServerConfig(new HashMap(rep.getLdapServer())); - } - if (rep.getAuthenticationProviders() != null) { - List authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders()); - realm.setAuthenticationProviders(authProviderModels); - } - - if ("GENERATE".equals(rep.getPublicKey())) { - generateRealmKeys(realm); - } - } - public void updateRealmAudit(RealmAuditRepresentation rep, RealmModel realm) { realm.setAuditEnabled(rep.isAuditEnabled()); realm.setAuditExpiration(rep.getAuditExpiration() != null ? rep.getAuditExpiration() : 0); @@ -262,7 +161,7 @@ public class RealmManager { ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session)); - ApplicationModel realmAdminApp = applicationManager.createApplication(adminRealm, getMasterRealmAdminApplicationName(realm)); + ApplicationModel realmAdminApp = applicationManager.createApplication(adminRealm, ExportImportUtils.getMasterRealmAdminApplicationName(realm)); realmAdminApp.setBearerOnly(true); realm.setMasterAdminApp(realmAdminApp); @@ -319,273 +218,7 @@ public class RealmManager { } public void importRealm(RealmRepresentation rep, RealmModel newRealm) { - newRealm.setName(rep.getRealm()); - if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled()); - if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial()); - if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected()); - if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds()); - if (rep.getMinimumQuickLoginWaitSeconds() != null) newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds()); - if (rep.getWaitIncrementSeconds() != null) newRealm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds()); - if (rep.getQuickLoginCheckMilliSeconds() != null) newRealm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds()); - if (rep.getMaxDeltaTimeSeconds() != null) newRealm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds()); - if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor()); - - if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore()); - - if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); - else newRealm.setAccessTokenLifespan(300); - - if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); - else newRealm.setSsoSessionIdleTimeout(600); - if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); - else newRealm.setSsoSessionMaxLifespan(36000); - - if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); - else newRealm.setAccessCodeLifespan(60); - - if (rep.getAccessCodeLifespanUserAction() != null) - newRealm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); - else newRealm.setAccessCodeLifespanUserAction(300); - - if (rep.isSslNotRequired() != null) newRealm.setSslNotRequired(rep.isSslNotRequired()); - if (rep.isPasswordCredentialGrantAllowed() != null) newRealm.setPasswordCredentialGrantAllowed(rep.isPasswordCredentialGrantAllowed()); - if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); - if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe()); - if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail()); - if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); - if (rep.isUpdateProfileOnInitialSocialLogin() != null) - newRealm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin()); - if (rep.getPrivateKey() == null || rep.getPublicKey() == null) { - generateRealmKeys(newRealm); - } else { - newRealm.setPrivateKeyPem(rep.getPrivateKey()); - newRealm.setPublicKeyPem(rep.getPublicKey()); - } - if (rep.getLoginTheme() != null) newRealm.setLoginTheme(rep.getLoginTheme()); - if (rep.getAccountTheme() != null) newRealm.setAccountTheme(rep.getAccountTheme()); - if (rep.getAdminTheme() != null) newRealm.setAdminTheme(rep.getAdminTheme()); - if (rep.getEmailTheme() != null) newRealm.setEmailTheme(rep.getEmailTheme()); - - if (rep.getRequiredCredentials() != null) { - for (String requiredCred : rep.getRequiredCredentials()) { - addRequiredCredential(newRealm, requiredCred); - } - } else { - addRequiredCredential(newRealm, CredentialRepresentation.PASSWORD); - } - - if (rep.getPasswordPolicy() != null) newRealm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy())); - - if (rep.getApplications() != null) { - Map appMap = createApplications(rep, newRealm); - } - - if (rep.getRoles() != null) { - if (rep.getRoles().getRealm() != null) { // realm roles - for (RoleRepresentation roleRep : rep.getRoles().getRealm()) { - createRole(newRealm, roleRep); - } - } - if (rep.getRoles().getApplication() != null) { - for (Map.Entry> entry : rep.getRoles().getApplication().entrySet()) { - ApplicationModel app = newRealm.getApplicationByName(entry.getKey()); - if (app == null) { - throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey()); - } - for (RoleRepresentation roleRep : entry.getValue()) { - RoleModel role = app.addRole(roleRep.getName()); - role.setDescription(roleRep.getDescription()); - } - } - } - // now that all roles are created, re-iterate and set up composites - if (rep.getRoles().getRealm() != null) { // realm roles - for (RoleRepresentation roleRep : rep.getRoles().getRealm()) { - RoleModel role = newRealm.getRole(roleRep.getName()); - addComposites(role, roleRep, newRealm); - } - } - if (rep.getRoles().getApplication() != null) { - for (Map.Entry> entry : rep.getRoles().getApplication().entrySet()) { - ApplicationModel app = newRealm.getApplicationByName(entry.getKey()); - if (app == null) { - throw new RuntimeException("App doesn't exist in role definitions: " + entry.getKey()); - } - for (RoleRepresentation roleRep : entry.getValue()) { - RoleModel role = app.getRole(roleRep.getName()); - addComposites(role, roleRep, newRealm); - } - } - } - } - - - if (rep.getDefaultRoles() != null) { - for (String roleString : rep.getDefaultRoles()) { - newRealm.addDefaultRole(roleString.trim()); - } - } - - if (rep.getOauthClients() != null) { - createOAuthClients(rep, newRealm); - } - - - // Now that all possible roles and applications are created, create scope mappings - - Map appMap = newRealm.getApplicationNameMap(); - - if (rep.getApplicationScopeMappings() != null) { - ApplicationManager manager = new ApplicationManager(this); - for (Map.Entry> entry : rep.getApplicationScopeMappings().entrySet()) { - ApplicationModel app = appMap.get(entry.getKey()); - if (app == null) { - throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey()); - } - manager.createScopeMappings(newRealm, app, entry.getValue()); - } - } - - if (rep.getScopeMappings() != null) { - for (ScopeMappingRepresentation scope : rep.getScopeMappings()) { - for (String roleString : scope.getRoles()) { - RoleModel role = newRealm.getRole(roleString.trim()); - if (role == null) { - role = newRealm.addRole(roleString.trim()); - } - ClientModel client = newRealm.findClient(scope.getClient()); - client.addScopeMapping(role); - } - - } - } - - if (rep.getSmtpServer() != null) { - newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer())); - } - - if (rep.getSocialProviders() != null) { - newRealm.setSocialConfig(new HashMap(rep.getSocialProviders())); - } - if (rep.getLdapServer() != null) { - newRealm.setLdapServerConfig(new HashMap(rep.getLdapServer())); - } - - if (rep.getAuthenticationProviders() != null) { - List authProviderModels = convertAuthenticationProviders(rep.getAuthenticationProviders()); - newRealm.setAuthenticationProviders(authProviderModels); - } else { - List authProviderModels = Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER); - newRealm.setAuthenticationProviders(authProviderModels); - } - - // create users and their role mappings and social mappings - - if (rep.getUsers() != null) { - for (UserRepresentation userRep : rep.getUsers()) { - UserModel user = createUser(newRealm, userRep, appMap); - } - } - } - - public void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) { - if (roleRep.getComposites() == null) return; - if (roleRep.getComposites().getRealm() != null) { - for (String roleStr : roleRep.getComposites().getRealm()) { - RoleModel realmRole = realm.getRole(roleStr); - if (realmRole == null) throw new RuntimeException("Unable to find composite realm role: " + roleStr); - role.addCompositeRole(realmRole); - } - } - if (roleRep.getComposites().getApplication() != null) { - for (Map.Entry> entry : roleRep.getComposites().getApplication().entrySet()) { - ApplicationModel app = realm.getApplicationByName(entry.getKey()); - if (app == null) { - throw new RuntimeException("App doesn't exist in role definitions: " + roleRep.getName()); - } - for (String roleStr : entry.getValue()) { - RoleModel appRole = app.getRole(roleStr); - if (appRole == null) throw new RuntimeException("Unable to find composite app role: " + roleStr); - role.addCompositeRole(appRole); - } - - } - - } - - } - - public void createRole(RealmModel newRealm, RoleRepresentation roleRep) { - RoleModel role = newRealm.addRole(roleRep.getName()); - if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription()); - } - - public void createRole(RealmModel newRealm, ApplicationModel app, RoleRepresentation roleRep) { - RoleModel role = app.addRole(roleRep.getName()); - if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription()); - } - - - public UserModel createUser(RealmModel newRealm, UserRepresentation userRep, Map appMap) { - UserModel user = newRealm.addUser(userRep.getId(), userRep.getUsername(), false); - user.setEnabled(userRep.isEnabled()); - user.setEmail(userRep.getEmail()); - user.setFirstName(userRep.getFirstName()); - user.setLastName(userRep.getLastName()); - if (userRep.getAttributes() != null) { - for (Map.Entry entry : userRep.getAttributes().entrySet()) { - user.setAttribute(entry.getKey(), entry.getValue()); - } - } - if (userRep.getRequiredActions() != null) { - for (String requiredAction : userRep.getRequiredActions()) { - user.addRequiredAction(RequiredAction.valueOf(requiredAction)); - } - } - if (userRep.getCredentials() != null) { - for (CredentialRepresentation cred : userRep.getCredentials()) { - UserCredentialModel credential = fromRepresentation(cred); - user.updateCredential(credential); - } - } - if (userRep.getAuthenticationLink() != null) { - AuthenticationLinkRepresentation link = userRep.getAuthenticationLink(); - AuthenticationLinkModel authLink = new AuthenticationLinkModel(link.getAuthProvider(), link.getAuthUserId()); - user.setAuthenticationLink(authLink); - } - if (userRep.getSocialLinks() != null) { - for (SocialLinkRepresentation socialLink : userRep.getSocialLinks()) { - SocialLinkModel mappingModel = new SocialLinkModel(socialLink.getSocialProvider(), socialLink.getSocialUserId(), socialLink.getSocialUsername()); - newRealm.addSocialLink(user, mappingModel); - } - } - if (userRep.getRealmRoles() != null) { - for (String roleString : userRep.getRealmRoles()) { - RoleModel role = newRealm.getRole(roleString.trim()); - if (role == null) { - role = newRealm.addRole(roleString.trim()); - } - user.grantRole(role); - } - } - if (userRep.getApplicationRoles() != null) { - ApplicationManager manager = new ApplicationManager(this); - for (Map.Entry> entry : userRep.getApplicationRoles().entrySet()) { - ApplicationModel app = appMap.get(entry.getKey()); - if (app == null) { - throw new RuntimeException("Unable to find application role mappings for app: " + entry.getKey()); - } - manager.createRoleMappings(app, user, entry.getValue()); - } - } - return user; - } - - public static UserCredentialModel fromRepresentation(CredentialRepresentation cred) { - UserCredentialModel credential = new UserCredentialModel(); - credential.setType(cred.getType()); - credential.setValue(cred.getValue()); - return credential; + RepresentationToModel.importRealm(rep, newRealm); } /** @@ -606,37 +239,4 @@ public class RealmManager { return realmModel.searchForUser(searchString.trim()); } - public void addRequiredCredential(RealmModel newRealm, String requiredCred) { - newRealm.addRequiredCredential(requiredCred); - } - - protected Map createApplications(RealmRepresentation rep, RealmModel realm) { - Map appMap = new HashMap(); - ApplicationManager manager = new ApplicationManager(this); - for (ApplicationRepresentation resourceRep : rep.getApplications()) { - ApplicationModel app = manager.createApplication(realm, resourceRep); - appMap.put(app.getName(), app); - } - return appMap; - } - - protected void createOAuthClients(RealmRepresentation realmRep, RealmModel realm) { - OAuthClientManager manager = new OAuthClientManager(realm); - for (OAuthClientRepresentation rep : realmRep.getOauthClients()) { - OAuthClientModel app = manager.create(rep); - } - } - - protected List convertAuthenticationProviders(List authenticationProviders) { - List result = new ArrayList(); - - for (AuthenticationProviderRepresentation representation : authenticationProviders) { - AuthenticationProviderModel model = new AuthenticationProviderModel(representation.getProviderName(), - representation.isPasswordUpdateSupported(), representation.getConfig()); - result.add(model); - } - return result; - } - - } diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index 68fe12fa68..19c7c58e47 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -53,7 +53,7 @@ import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.OAuthRedirect; diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index c868e3f336..f4094af6c3 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -7,7 +7,7 @@ import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.Config; import org.keycloak.SkeletonKeyContextResolver; -import org.keycloak.exportimport.ExportImportProvider; +import org.keycloak.exportimport.ExportImportManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; @@ -24,7 +24,6 @@ import org.keycloak.services.scheduled.ScheduledTaskRunner; import org.keycloak.services.util.JsonConfigProvider; import org.keycloak.timer.TimerProvider; import org.keycloak.util.JsonSerialization; -import org.keycloak.util.ProviderLoader; import javax.servlet.ServletContext; import javax.ws.rs.core.Application; @@ -38,7 +37,6 @@ import java.io.InputStream; import java.net.URI; import java.net.URL; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import java.util.StringTokenizer; @@ -81,7 +79,7 @@ public class KeycloakApplication extends Application { classes.add(JsResource.class); classes.add(WelcomeResource.class); - checkExportImportProvider(); + new ExportImportManager().checkExportImport(this.sessionFactory); setupDefaultRealm(context.getContextPath()); @@ -235,16 +233,4 @@ public class KeycloakApplication extends Application { } } - protected void checkExportImportProvider() { - Iterator providers = ProviderLoader.load(ExportImportProvider.class).iterator(); - - if (providers.hasNext()) { - ExportImportProvider exportImport = providers.next(); - exportImport.checkExportImport(sessionFactory); - } else { - log.warn("No ExportImportProvider found!"); - } - } - - } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java index 919fcce413..530811211a 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java @@ -10,13 +10,15 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.adapters.action.SessionStats; import org.keycloak.representations.adapters.action.UserStats; import org.keycloak.representations.idm.ApplicationRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.services.managers.ApplicationManager; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.resources.KeycloakApplication; @@ -97,7 +99,7 @@ public class ApplicationResource { ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session)); try { - applicationManager.updateApplication(rep, application); + RepresentationToModel.updateApplication(rep, application); return Response.noContent().build(); } catch (ModelDuplicateException e) { return Flows.errors().exists("Application " + rep.getName() + " already exists"); @@ -116,8 +118,7 @@ public class ApplicationResource { public ApplicationRepresentation getApplication() { auth.requireView(); - ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session)); - return applicationManager.toRepresentation(application); + return ModelToRepresentation.toRepresentation(application); } @@ -184,7 +185,7 @@ public class ApplicationResource { auth.requireManage(); logger.debug("regenerateSecret"); - UserCredentialModel cred = new ApplicationManager().generateSecret(application); + UserCredentialModel cred = KeycloakModelUtils.generateSecret(application); CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred); return rep; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java index f83bfc79f3..c3038473e9 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationsResource.java @@ -8,6 +8,8 @@ import org.keycloak.models.ApplicationModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ApplicationRepresentation; import org.keycloak.services.managers.ApplicationManager; import org.keycloak.services.managers.RealmManager; @@ -60,12 +62,11 @@ public class ApplicationsResource { List rep = new ArrayList(); List applicationModels = realm.getApplications(); - ApplicationManager resourceManager = new ApplicationManager(new RealmManager(session)); boolean view = auth.hasView(); for (ApplicationModel applicationModel : applicationModels) { if (view) { - rep.add(resourceManager.toRepresentation(applicationModel)); + rep.add(ModelToRepresentation.toRepresentation(applicationModel)); } else { ApplicationRepresentation app = new ApplicationRepresentation(); app.setName(applicationModel.getName()); @@ -87,9 +88,8 @@ public class ApplicationsResource { public Response createApplication(final @Context UriInfo uriInfo, final ApplicationRepresentation rep) { auth.requireManage(); - ApplicationManager resourceManager = new ApplicationManager(new RealmManager(session)); try { - ApplicationModel applicationModel = resourceManager.createApplication(realm, rep); + ApplicationModel applicationModel = RepresentationToModel.createApplication(realm, rep); return Response.created(uriInfo.getAbsolutePathBuilder().path(applicationModel.getName()).build()).build(); } catch (ModelDuplicateException e) { return Flows.errors().exists("Application " + rep.getName() + " already exists"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java index 9a9ebaa389..0353bad3b7 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClaimResource.java @@ -1,9 +1,9 @@ package org.keycloak.services.resources.admin; import org.keycloak.models.ClientModel; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClaimRepresentation; -import org.keycloak.services.managers.ClaimManager; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -47,6 +47,6 @@ public class ClaimResource { @Consumes(MediaType.APPLICATION_JSON) public void updateClaims(ClaimRepresentation rep) { auth.requireManage(); - ClaimManager.setClaims(model, rep); + RepresentationToModel.setClaims(model, rep); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java index d8b6d652e3..e7722d24d0 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java @@ -8,9 +8,10 @@ import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.OAuthClientRepresentation; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.managers.OAuthClientManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; @@ -82,9 +83,8 @@ public class OAuthClientResource { public Response update(final OAuthClientRepresentation rep) { auth.requireManage(); - OAuthClientManager manager = new OAuthClientManager(realm); try { - manager.update(rep, oauthClient); + RepresentationToModel.updateOAuthClient(rep, oauthClient); return Response.noContent().build(); } catch (ModelDuplicateException e) { return Flows.errors().exists("Client " + rep.getName() + " already exists"); @@ -102,7 +102,7 @@ public class OAuthClientResource { public OAuthClientRepresentation getOAuthClient() { auth.requireView(); - return OAuthClientManager.toRepresentation(oauthClient); + return ModelToRepresentation.toRepresentation(oauthClient); } /** @@ -118,7 +118,7 @@ public class OAuthClientResource { public String getInstallation() throws IOException { auth.requireView(); - OAuthClientManager manager = new OAuthClientManager(realm); + OAuthClientManager manager = new OAuthClientManager(); Object rep = manager.toInstallationRepresentation(realm, oauthClient, getApplication().getBaseUri(uriInfo)); // TODO Temporary solution to pretty-print diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java index f80a8c95a4..dd081f03bf 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java @@ -8,6 +8,8 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.OAuthClientRepresentation; import org.keycloak.services.managers.OAuthClientManager; import org.keycloak.services.resources.flows.Flows; @@ -65,7 +67,7 @@ public class OAuthClientsResource { boolean view = auth.hasView(); for (OAuthClientModel oauth : oauthModels) { if (view) { - rep.add(OAuthClientManager.toRepresentation(oauth)); + rep.add(ModelToRepresentation.toRepresentation(oauth)); } else { OAuthClientRepresentation client = new OAuthClientRepresentation(); client.setName(oauth.getClientId()); @@ -87,9 +89,8 @@ public class OAuthClientsResource { public Response createOAuthClient(final @Context UriInfo uriInfo, final OAuthClientRepresentation rep) { auth.requireManage(); - OAuthClientManager resourceManager = new OAuthClientManager(realm); try { - OAuthClientModel oauth = resourceManager.create(rep); + OAuthClientModel oauth = RepresentationToModel.createOAuthClient(rep, realm); return Response.created(uriInfo.getAbsolutePathBuilder().path(oauth.getId()).build()).build(); } catch (ModelDuplicateException e) { return Flows.errors().exists("Client " + rep.getName() + " already exists"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index b9243491e8..79a0bcd1b4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -13,11 +13,12 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.adapters.action.SessionStats; import org.keycloak.representations.idm.RealmAuditRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.managers.LDAPConnectionTestManager; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.TokenManager; @@ -138,7 +139,7 @@ public class RealmAdminResource { logger.debug("updating realm: " + realm.getName()); try { - new RealmManager(session).updateRealm(rep, realm); + RepresentationToModel.updateRealm(rep, realm); return Response.noContent().build(); } catch (ModelDuplicateException e) { return Flows.errors().exists("Realm " + rep.getRealm() + " already exists"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java index 35f817604d..de7f157802 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java @@ -14,7 +14,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.TokenManager; import org.keycloak.services.resources.KeycloakApplication; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index d1bb14980c..cf433067b1 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -2,13 +2,12 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; -import org.keycloak.models.Constants; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.resources.flows.Flows; import javax.ws.rs.Consumes; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java index 8ef17d8e27..18599de0e0 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java @@ -5,7 +5,7 @@ import org.keycloak.models.ApplicationModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import java.util.Collections; import java.util.HashSet; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java index 92b129c780..96a225b0d7 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java @@ -7,12 +7,10 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.UserModel; import org.keycloak.representations.idm.ApplicationMappingsRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.services.managers.ModelToRepresentation; -import org.keycloak.services.managers.RealmManager; +import org.keycloak.models.utils.ModelToRepresentation; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index e86781d6e7..32ccf7f854 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -17,6 +17,7 @@ import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.adapters.action.UserStats; import org.keycloak.representations.idm.ApplicationMappingsRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; @@ -26,7 +27,7 @@ import org.keycloak.representations.idm.SocialLinkRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.services.managers.AccessCodeEntry; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.TokenManager; @@ -760,7 +761,7 @@ public class UsersResource { throw new BadRequestException("No password provided"); } - UserCredentialModel cred = RealmManager.fromRepresentation(pass); + UserCredentialModel cred = RepresentationToModel.convertCredential(pass); user.updateCredential(cred); user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); } diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index acb145b71e..6507a5e130 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -289,7 +289,17 @@ org.keycloak - keycloak-export-import-impl + keycloak-export-import-dir + ${project.version} + + + org.keycloak + keycloak-export-import-single-file + ${project.version} + + + org.keycloak + keycloak-export-import-zip ${project.version} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java index 5c7bc52279..f33a43e063 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java @@ -33,6 +33,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.AccessToken; import org.keycloak.services.managers.ApplicationManager; import org.keycloak.services.managers.RealmManager; @@ -59,7 +60,7 @@ public class CompositeRoleTest { @Override protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { RealmModel realm = manager.createRealm("Test"); - manager.generateRealmKeys(realm); + KeycloakModelUtils.generateRealmKeys(realm); realmPublicKey = realm.getPublicKey(); realm.setSsoSessionIdleTimeout(3000); realm.setAccessTokenLifespan(10000); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java new file mode 100644 index 0000000000..688b54ce33 --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java @@ -0,0 +1,269 @@ +package org.keycloak.testsuite.exportimport; + +import java.io.File; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.Config; +import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.dir.DirExportProvider; +import org.keycloak.exportimport.dir.DirExportProviderFactory; +import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory; +import org.keycloak.exportimport.zip.ZipExportProviderFactory; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelProvider; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.rule.KeycloakRule; +import org.keycloak.testutils.KeycloakServer; + +/** + * @author Marek Posolda + */ +public class ExportImportTest { + + @ClassRule + public static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() { + + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + addUser(appRealm, "user1", "password"); + addUser(appRealm, "user2", "password"); + addUser(appRealm, "user3", "password"); + addUser(adminstrationRealm, "admin2", "admin2"); + } + + }); + + @Test + public void testDirFullExportImport() throws Throwable { + ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID); + String targetDirPath = getExportImportTestDirectory() + File.separator + "dirExport"; + DirExportProvider.recursiveDeleteDir(new File(targetDirPath)); + ExportImportConfig.setDir(targetDirPath); + ExportImportConfig.setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE); + + testFullExportImport(); + + // There should be 4 files in target directory (2 realm, 2 user) + Assert.assertEquals(4, new File(targetDirPath).listFiles().length); + } + + @Test + public void testDirRealmExportImport() throws Throwable { + ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID); + String targetDirPath = getExportImportTestDirectory() + File.separator + "dirRealmExport"; + DirExportProvider.recursiveDeleteDir(new File(targetDirPath)); + ExportImportConfig.setDir(targetDirPath); + ExportImportConfig.setUsersPerFile(3); + + testRealmExportImport(); + + // There should be 3 files in target directory (1 realm, 2 user) + Assert.assertEquals(3, new File(targetDirPath).listFiles().length); + } + + @Test + public void testSingleFileFullExportImport() throws Throwable { + ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); + String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-full.json"; + ExportImportConfig.setFile(targetFilePath); + + testFullExportImport(); + } + + @Test + public void testSingleFileRealmExportImport() throws Throwable { + ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); + String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json"; + ExportImportConfig.setFile(targetFilePath); + + testRealmExportImport(); + } + + @Test + public void testZipFullExportImport() throws Throwable { + ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID); + String zipFilePath = getExportImportTestDirectory() + File.separator + "export-full.zip"; + new File(zipFilePath).delete(); + ExportImportConfig.setZipFile(zipFilePath); + ExportImportConfig.setZipPassword("encPassword"); + ExportImportConfig.setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE); + + testFullExportImport(); + } + + @Test + public void testZipRealmExportImport() throws Throwable { + ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID); + String zipFilePath = getExportImportTestDirectory() + File.separator + "export-realm.zip"; + new File(zipFilePath).delete(); + ExportImportConfig.setZipFile(zipFilePath); + ExportImportConfig.setZipPassword("encPassword"); + ExportImportConfig.setUsersPerFile(3); + + testRealmExportImport(); + } + + private void testFullExportImport() { + ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT); + ExportImportConfig.setRealmName(null); + + // Restart server, which triggers export + keycloakRule.restartServer(); + + // Delete some realm (and some data in admin realm) + KeycloakSession session = keycloakRule.startSession(); + try { + ModelProvider model = session.getModel(); + model.removeRealm(model.getRealmByName("test").getId()); + Assert.assertEquals(1, model.getRealms().size()); + + RealmModel master = model.getRealmByName(Config.getAdminRealm()); + master.removeUser("admin2"); + + assertNotAuthenticated(model, Config.getAdminRealm(), "admin2", "admin2"); + assertNotAuthenticated(model, "test", "test-user@localhost", "password"); + assertNotAuthenticated(model, "test", "user1", "password"); + assertNotAuthenticated(model, "test", "user2", "password"); + assertNotAuthenticated(model, "test", "user3", "password"); + } finally { + keycloakRule.stopSession(session, true); + } + + // Configure import + ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT); + + // Restart server, which triggers import + keycloakRule.restartServer(); + + // Ensure data are imported back + session = keycloakRule.startSession(); + try { + ModelProvider model = session.getModel(); + Assert.assertEquals(2, model.getRealms().size()); + + assertAuthenticated(model, Config.getAdminRealm(), "admin2", "admin2"); + assertAuthenticated(model, "test", "test-user@localhost", "password"); + assertAuthenticated(model, "test", "user1", "password"); + assertAuthenticated(model, "test", "user2", "password"); + assertAuthenticated(model, "test", "user3", "password"); + + } finally { + keycloakRule.stopSession(session, true); + } + } + + private void testRealmExportImport() { + ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT); + ExportImportConfig.setRealmName("test"); + + // Restart server, which triggers export + keycloakRule.restartServer(); + + // Delete some realm (and some data in admin realm) + KeycloakSession session = keycloakRule.startSession(); + try { + ModelProvider model = session.getModel(); + model.removeRealm(model.getRealmByName("test").getId()); + Assert.assertEquals(1, model.getRealms().size()); + + RealmModel master = model.getRealmByName(Config.getAdminRealm()); + master.removeUser("admin2"); + + assertNotAuthenticated(model, Config.getAdminRealm(), "admin2", "admin2"); + assertNotAuthenticated(model, "test", "test-user@localhost", "password"); + assertNotAuthenticated(model, "test", "user1", "password"); + assertNotAuthenticated(model, "test", "user2", "password"); + assertNotAuthenticated(model, "test", "user3", "password"); + } finally { + keycloakRule.stopSession(session, true); + } + + // Configure import + ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT); + + // Restart server, which triggers import + keycloakRule.restartServer(); + + // Ensure data are imported back, but just for "test" realm + session = keycloakRule.startSession(); + try { + ModelProvider model = session.getModel(); + Assert.assertEquals(2, model.getRealms().size()); + + assertNotAuthenticated(model, Config.getAdminRealm(), "admin2", "admin2"); + assertAuthenticated(model, "test", "test-user@localhost", "password"); + assertAuthenticated(model, "test", "user1", "password"); + assertAuthenticated(model, "test", "user2", "password"); + assertAuthenticated(model, "test", "user3", "password"); + + addUser(model.getRealmByName(Config.getAdminRealm()), "admin2", "admin2"); + } finally { + keycloakRule.stopSession(session, true); + } + } + + private void assertAuthenticated(ModelProvider model, String realmName, String username, String password) { + RealmModel realm = model.getRealmByName(realmName); + if (realm == null) { + Assert.fail("realm " + realmName + " not found"); + } + + UserModel user = realm.getUser(username); + if (user == null) { + Assert.fail("user " + username + " not found"); + } + + Assert.assertTrue(realm.validatePassword(user, password)); + } + + private void assertNotAuthenticated(ModelProvider model, String realmName, String username, String password) { + RealmModel realm = model.getRealmByName(realmName); + if (realm == null) { + return; + } + + UserModel user = realm.getUser(username); + if (user == null) { + return; + } + + Assert.assertFalse(realm.validatePassword(user, password)); + } + + private static void addUser(RealmModel appRealm, String username, String password) { + UserModel user = appRealm.addUser(username); + user.setEmail(username + "@test.com"); + user.setEnabled(true); + + UserCredentialModel creds = new UserCredentialModel(); + creds.setType(CredentialRepresentation.PASSWORD); + creds.setValue(password); + user.updateCredential(creds); + } + + private String getExportImportTestDirectory() { + String dirPath = null; + String relativeDirExportImportPath = "testsuite" + File.separator + "integration" + File.separator + "target" + File.separator + "export-import"; + + if (System.getProperties().containsKey("maven.home")) { + dirPath = System.getProperty("user.dir").replaceFirst("testsuite.integration.*", relativeDirExportImportPath); + } else { + for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) { + if (c.contains(File.separator + "testsuite" + File.separator + "integration")) { + dirPath = c.replaceFirst("testsuite.integration.*", relativeDirExportImportPath); + } + } + } + + String absolutePath = new File(dirPath).getAbsolutePath(); + return absolutePath; + } +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java index 0ba9ed3048..f79358f56f 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java @@ -12,7 +12,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.services.managers.ModelToRepresentation; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.testutils.KeycloakServer; import org.keycloak.util.JsonSerialization; @@ -146,4 +146,13 @@ public abstract class AbstractKeycloakRule extends ExternalResource { } session.close(); } + + public void restartServer() { + try { + server.stop(); + server.start(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } } diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java index 4b89245ecb..4077f6b58e 100644 --- a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java @@ -1,7 +1,7 @@ package org.keycloak.test.tools; import org.keycloak.exportimport.ExportImportConfig; -import org.keycloak.exportimport.ExportImportProvider; +import org.keycloak.exportimport.ExportProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; @@ -231,10 +231,10 @@ public class PerfTools { ExportImportConfig.setProvider("dir"); ExportImportConfig.setDir(dir); - Iterator providers = ProviderLoader.load(ExportImportProvider.class).iterator(); + Iterator providers = ProviderLoader.load(ExportProvider.class).iterator(); if (providers.hasNext()) { - ExportImportProvider exportImport = providers.next(); + ExportProvider exportImport = providers.next(); exportImport.checkExportImport(sessionFactory); } else { throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); From c14840a4c0ffcd9970bff03d85af50363d55f356 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 15 Jul 2014 05:45:54 +0200 Subject: [PATCH 2/3] More cleanup after rebase --- .../exportimport/ExportImportManager.java | 8 +-- .../keycloak/exportimport/ExportProvider.java | 4 +- .../keycloak/exportimport/ImportProvider.java | 5 +- .../exportimport/util/ExportImportJob.java | 4 +- .../exportimport/util/ExportImportUtils.java | 12 ++-- .../exportimport/util/ImportUtils.java | 49 +++++++++++-- .../util/MultipleStepsExportProvider.java | 25 +++---- .../exportimport/dir/DirImportProvider.java | 18 +++-- .../singlefile/SingleFileExportProvider.java | 17 ++--- .../singlefile/SingleFileImportProvider.java | 13 ++-- .../exportimport/zip/ZipImportProvider.java | 15 ++-- .../models/utils/KeycloakModelUtils.java | 7 ++ .../keycloak/models/cache/SimpleCache.java | 2 +- .../org/keycloak/models/jpa/RealmAdapter.java | 5 +- .../mongo/keycloak/adapters/RealmAdapter.java | 3 +- .../services/managers/ApplicationManager.java | 5 +- .../services/managers/OAuthClientManager.java | 3 + .../services/managers/RealmManager.java | 28 +------- .../exportimport/ExportImportTest.java | 68 +++++++++++++++---- 19 files changed, 183 insertions(+), 108 deletions(-) diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java index a15a1ce38c..caedb36d39 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportManager.java @@ -35,10 +35,10 @@ public class ExportImportManager { if (realmName == null) { logger.info("Full model export requested"); - exportProvider.exportModel(session); + exportProvider.exportModel(sessionFactory); } else { logger.infof("Export of realm '%s' requested", realmName); - exportProvider.exportRealm(session, realmName); + exportProvider.exportRealm(sessionFactory, realmName); } logger.info("Export finished successfully"); } else { @@ -46,10 +46,10 @@ public class ExportImportManager { Strategy strategy = ExportImportConfig.getStrategy(); if (realmName == null) { logger.infof("Full model import requested. Strategy: %s", strategy.toString()); - importProvider.importModel(session, strategy); + importProvider.importModel(sessionFactory, strategy); } else { logger.infof("Import of realm '%s' requested. Strategy: %s", realmName, strategy.toString()); - importProvider.importRealm(session, realmName, strategy); + importProvider.importRealm(sessionFactory, realmName, strategy); } logger.info("Import finished successfully"); } diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java index 4171b89664..1a0d6a0057 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportProvider.java @@ -11,8 +11,8 @@ import org.keycloak.provider.Provider; */ public interface ExportProvider extends Provider { - void exportModel(KeycloakSession session) throws IOException; + void exportModel(KeycloakSessionFactory factory) throws IOException; - void exportRealm(KeycloakSession session, String realmName) throws IOException; + void exportRealm(KeycloakSessionFactory factory, String realmName) throws IOException; } diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java index 5283977db1..9134516d78 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ImportProvider.java @@ -3,6 +3,7 @@ package org.keycloak.exportimport; import java.io.IOException; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.provider.Provider; /** @@ -10,7 +11,7 @@ import org.keycloak.provider.Provider; */ public interface ImportProvider extends Provider { - void importModel(KeycloakSession session, Strategy strategy) throws IOException; + void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException; - void importRealm(KeycloakSession session, String realmName, Strategy strategy) throws IOException; + void importRealm(KeycloakSessionFactory factory, String realmName, Strategy strategy) throws IOException; } diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java index 12922b4a41..748fe1ff2e 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportJob.java @@ -2,6 +2,8 @@ package org.keycloak.exportimport.util; import java.io.IOException; +import org.keycloak.models.KeycloakSession; + /** * Task to be executed inside transaction * @@ -9,5 +11,5 @@ import java.io.IOException; */ public interface ExportImportJob { - public void run() throws IOException; + public void run(KeycloakSession session) throws IOException; } diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java index 2c0ab44599..b73a4c0e91 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportImportUtils.java @@ -4,6 +4,7 @@ import java.io.IOException; import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.RealmModel; @@ -13,17 +14,17 @@ import org.keycloak.models.RealmModel; public class ExportImportUtils { /** - * Wrap given runnable job into KeycloakTransaction. Assumption is that session already exists and it doesn't need to be closed when finished + * Wrap given runnable job into KeycloakTransaction. * - * @param session + * @param factory * @param job */ - public static void runJobInTransaction(KeycloakSession session, ExportImportJob job) throws IOException { + public static void runJobInTransaction(KeycloakSessionFactory factory, ExportImportJob job) throws IOException { + KeycloakSession session = factory.create(); KeycloakTransaction tx = session.getTransaction(); try { tx.begin(); - - job.run(); + job.run(session); if (tx.isActive()) { if (tx.getRollbackOnly()) { @@ -36,6 +37,7 @@ public class ExportImportUtils { if (tx.isActive()) { tx.rollback(); } + session.close(); } } diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java index 274798751b..e57c750307 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java @@ -17,11 +17,14 @@ import org.codehaus.jackson.map.ObjectMapper; import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.exportimport.Strategy; +import org.keycloak.models.AdminRoles; import org.keycloak.models.ApplicationModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelProvider; import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -43,7 +46,7 @@ public class ImportUtils { */ public static RealmModel importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy) { String realmName = rep.getRealm(); - ModelProvider model = session.getModel(); + ModelProvider model = session.model(); RealmModel realm = model.getRealmByName(realmName); if (realm != null) { @@ -52,6 +55,9 @@ public class ImportUtils { return realm; } else { logger.infof("Realm '%s' already exists. Removing it before import", realmName); + if (Config.getAdminRealm().equals(realm.getId())) { + realm.setMasterAdminApp(null); + } model.removeRealm(realm.getId()); } } @@ -73,13 +79,48 @@ public class ImportUtils { RealmModel adminRealm = realm; for (RealmModel currentRealm : model.getRealms()) { ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(currentRealm)); - currentRealm.setMasterAdminApp(masterApp); + if (masterApp != null) { + currentRealm.setMasterAdminApp(masterApp); + } else { + setupMasterAdminManagement(model, currentRealm); + } } } else { // Need to refresh masterApp for current realm RealmModel adminRealm = model.getRealm(adminRealmId); ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(realm)); - realm.setMasterAdminApp(masterApp); + if (masterApp != null) { + realm.setMasterAdminApp(masterApp); + } else { + setupMasterAdminManagement(model, realm); + } + } + } + + // TODO: We need method here, so we are able to refresh masterAdmin applications after import. Should be RealmManager moved to model/api instead? + public static void setupMasterAdminManagement(ModelProvider model, RealmModel realm) { + RealmModel adminRealm; + RoleModel adminRole; + + if (realm.getName().equals(Config.getAdminRealm())) { + adminRealm = realm; + + adminRole = realm.addRole(AdminRoles.ADMIN); + + RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM); + adminRole.addCompositeRole(createRealmRole); + } else { + adminRealm = model.getRealmByName(Config.getAdminRealm()); + adminRole = adminRealm.getRole(AdminRoles.ADMIN); + } + + ApplicationModel realmAdminApp = KeycloakModelUtils.createApplication(adminRealm, ExportImportUtils.getMasterRealmAdminApplicationName(realm)); + realmAdminApp.setBearerOnly(true); + realm.setMasterAdminApp(realmAdminApp); + + for (String r : AdminRoles.ALL_REALM_ROLES) { + RoleModel role = realmAdminApp.addRole(r); + adminRole.addCompositeRole(role); } } @@ -119,7 +160,7 @@ public class ImportUtils { // Assuming that it's invoked inside transaction public static void importUsersFromStream(KeycloakSession session, String realmName, ObjectMapper mapper, InputStream is) throws IOException { - ModelProvider model = session.getModel(); + ModelProvider model = session.model(); JsonFactory factory = mapper.getJsonFactory(); JsonParser parser = factory.createJsonParser(is); try { diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java index 145ba52ae1..a98ee98e13 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java @@ -7,6 +7,7 @@ import org.jboss.logging.Logger; import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.ExportProvider; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.RealmRepresentation; @@ -19,36 +20,36 @@ public abstract class MultipleStepsExportProvider implements ExportProvider { protected final Logger logger = Logger.getLogger(getClass()); @Override - public void exportModel(final KeycloakSession session) throws IOException { + public void exportModel(KeycloakSessionFactory factory) throws IOException { final RealmsHolder holder = new RealmsHolder(); // Import users into same file with realm - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() { - List realms = session.getModel().getRealms(); + public void run(KeycloakSession session) { + List realms = session.model().getRealms(); holder.realms = realms; } }); for (RealmModel realm : holder.realms) { - exportRealm(session, realm.getName()); + exportRealm(factory, realm.getName()); } } @Override - public void exportRealm(final KeycloakSession session, final String realmName) throws IOException { + public void exportRealm(KeycloakSessionFactory factory, final String realmName) throws IOException { final int usersPerFile = ExportImportConfig.getUsersPerFile(); final UsersHolder usersHolder = new UsersHolder(); final boolean exportUsersIntoSameFile = usersPerFile < 0; - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { - RealmModel realm = session.getModel().getRealmByName(realmName); + public void run(KeycloakSession session) throws IOException { + RealmModel realm = session.model().getRealmByName(realmName); RealmRepresentation rep = ExportUtils.exportRealm(realm, exportUsersIntoSameFile); writeRealm(realmName + "-realm.json", rep); logger.info("Realm '" + realmName + "' - data exported"); @@ -76,11 +77,11 @@ public abstract class MultipleStepsExportProvider implements ExportProvider { usersHolder.currentPageEnd = usersHolder.totalCount; } - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { - RealmModel realm = session.getModel().getRealmByName(realmName); + public void run(KeycloakSession session) throws IOException { + RealmModel realm = session.model().getRealmByName(realmName); // TODO: pagination List users = realm.getUsers(); usersHolder.users = users.subList(usersHolder.currentPageStart, usersHolder.currentPageEnd); diff --git a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java index 3f6fc901df..ef29af9459 100644 --- a/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java +++ b/export-import/export-import-dir/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java @@ -5,16 +5,14 @@ import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; -import org.codehaus.jackson.map.ObjectMapper; import org.jboss.logging.Logger; -import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.ImportProvider; import org.keycloak.exportimport.Strategy; import org.keycloak.exportimport.util.ExportImportJob; import org.keycloak.exportimport.util.ExportImportUtils; import org.keycloak.exportimport.util.ImportUtils; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.util.JsonSerialization; @@ -47,7 +45,7 @@ public class DirImportProvider implements ImportProvider { } @Override - public void importModel(KeycloakSession session, Strategy strategy) throws IOException { + public void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException { File[] realmFiles = this.rootDirectory.listFiles(new FilenameFilter() { @Override @@ -61,12 +59,12 @@ public class DirImportProvider implements ImportProvider { // Parse "foo" from "foo-realm.json" String realmName = fileName.substring(0, fileName.length() - 11); - importRealm(session, realmName, strategy); + importRealm(factory, realmName, strategy); } } @Override - public void importRealm(final KeycloakSession session, final String realmName, final Strategy strategy) throws IOException { + public void importRealm(KeycloakSessionFactory factory, final String realmName, final Strategy strategy) throws IOException { File realmFile = new File(this.rootDirectory + File.separator + realmName + "-realm.json"); File[] userFiles = this.rootDirectory.listFiles(new FilenameFilter() { @@ -80,10 +78,10 @@ public class DirImportProvider implements ImportProvider { FileInputStream is = new FileInputStream(realmFile); final RealmRepresentation realmRep = JsonSerialization.readValue(is, RealmRepresentation.class); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { + public void run(KeycloakSession session) throws IOException { ImportUtils.importRealm(session, realmRep, strategy); } @@ -92,10 +90,10 @@ public class DirImportProvider implements ImportProvider { // Import users for (File userFile : userFiles) { final FileInputStream fis = new FileInputStream(userFile); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { + public void run(KeycloakSession session) throws IOException { ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, fis); } }); diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java index 4a63ca0699..bd799b9e4c 100644 --- a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java +++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java @@ -13,6 +13,7 @@ import org.keycloak.exportimport.util.ExportImportJob; import org.keycloak.exportimport.util.ExportImportUtils; import org.keycloak.exportimport.util.ExportUtils; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.util.JsonSerialization; @@ -35,13 +36,13 @@ public class SingleFileExportProvider implements ExportProvider { } @Override - public void exportModel(final KeycloakSession session) throws IOException { + public void exportModel(KeycloakSessionFactory factory) throws IOException { logger.infof("Exporting model into file %s", this.file.getAbsolutePath()); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { - List realms = session.getModel().getRealms(); + public void run(KeycloakSession session) throws IOException { + List realms = session.model().getRealms(); List reps = new ArrayList(); for (RealmModel realm : realms) { reps.add(ExportUtils.exportRealm(realm, true)); @@ -55,13 +56,13 @@ public class SingleFileExportProvider implements ExportProvider { } @Override - public void exportRealm(final KeycloakSession session, final String realmName) throws IOException { + public void exportRealm(KeycloakSessionFactory factory, final String realmName) throws IOException { logger.infof("Exporting realm '%s' into file %s", realmName, this.file.getAbsolutePath()); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { - RealmModel realm = session.getModel().getRealmByName(realmName); + public void run(KeycloakSession session) throws IOException { + RealmModel realm = session.model().getRealmByName(realmName); RealmRepresentation realmRep = ExportUtils.exportRealm(realm, true); writeToFile(realmRep); } diff --git a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java index 5ac38f400a..07e8cb21f4 100644 --- a/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java +++ b/export-import/export-import-single-file/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProvider.java @@ -14,6 +14,7 @@ import org.keycloak.exportimport.util.ExportImportUtils; import org.keycloak.exportimport.util.ExportUtils; import org.keycloak.exportimport.util.ImportUtils; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.util.JsonSerialization; @@ -32,12 +33,12 @@ public class SingleFileImportProvider implements ImportProvider { } @Override - public void importModel(final KeycloakSession session, final Strategy strategy) throws IOException { + public void importModel(KeycloakSessionFactory factory, final Strategy strategy) throws IOException { logger.infof("Full importing from file %s", this.file.getAbsolutePath()); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { + public void run(KeycloakSession session) throws IOException { FileInputStream is = new FileInputStream(file); ImportUtils.importFromStream(session, JsonSerialization.mapper, is, strategy); } @@ -46,9 +47,9 @@ public class SingleFileImportProvider implements ImportProvider { } @Override - public void importRealm(KeycloakSession session, String realmName, Strategy strategy) throws IOException { - // TODO: just that single realm in case that file contains many realms? - importModel(session, strategy); + public void importRealm(KeycloakSessionFactory factory, String realmName, Strategy strategy) throws IOException { + // TODO: import just that single realm in case that file contains many realms? + importModel(factory, strategy); } @Override diff --git a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java index 6ff1f5a244..20d3dc75bf 100644 --- a/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java +++ b/export-import/export-import-zip/src/main/java/org/keycloak/exportimport/zip/ZipImportProvider.java @@ -17,6 +17,7 @@ import org.keycloak.exportimport.util.ExportImportJob; import org.keycloak.exportimport.util.ExportImportUtils; import org.keycloak.exportimport.util.ImportUtils; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.util.JsonSerialization; @@ -46,19 +47,19 @@ public class ZipImportProvider implements ImportProvider { } @Override - public void importModel(KeycloakSession session, Strategy strategy) throws IOException { + public void importModel(KeycloakSessionFactory factory, Strategy strategy) throws IOException { for (ExtZipEntry entry : this.decrypter.getEntryList()) { String entryName = entry.getName(); if (entryName.endsWith("-realm.json")) { // Parse "foo" from "foo-realm.json" String realmName = entryName.substring(0, entryName.length() - 11); - importRealm(session, realmName, strategy); + importRealm(factory, realmName, strategy); } } } @Override - public void importRealm(final KeycloakSession session, final String realmName, final Strategy strategy) throws IOException { + public void importRealm(KeycloakSessionFactory factory, final String realmName, final Strategy strategy) throws IOException { try { // Import realm first ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -66,10 +67,10 @@ public class ZipImportProvider implements ImportProvider { ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); final RealmRepresentation realmRep = JsonSerialization.mapper.readValue(bis, RealmRepresentation.class); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { + public void run(KeycloakSession session) throws IOException { ImportUtils.importRealm(session, realmRep, strategy); } @@ -84,10 +85,10 @@ public class ZipImportProvider implements ImportProvider { this.decrypter.extractEntry(entry, bos, this.password); final ByteArrayInputStream bis2 = new ByteArrayInputStream(bos.toByteArray()); - ExportImportUtils.runJobInTransaction(session, new ExportImportJob() { + ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() { @Override - public void run() throws IOException { + public void run(KeycloakSession session) throws IOException { ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, bis2); } }); diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index 68eda42528..e32fa3e603 100644 --- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -88,6 +88,13 @@ public final class KeycloakModelUtils { return secret; } + public static ApplicationModel createApplication(RealmModel realm, String name) { + ApplicationModel app = realm.addApplication(name); + generateSecret(app); + + return app; + } + /** * Deep search if given role is descendant of composite role * diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java index f40723b48a..39820206b0 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java @@ -226,7 +226,7 @@ public class SimpleCache implements KeycloakCache { @Override public void invalidateRole(CachedRole role) { - roleCache.remove(role); + roleCache.remove(role.getId()); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index a656b0a1ff..dd32a84930 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -745,7 +745,7 @@ public class RealmAdapter implements RealmModel { public boolean removeOAuthClient(String id) { OAuthClientModel oauth = getOAuthClientById(id); if (oauth == null) return false; - OAuthClientEntity client = (OAuthClientEntity) ((OAuthClientAdapter) oauth).getEntity(); + OAuthClientEntity client = em.getReference(OAuthClientEntity.class, oauth.getId()); em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", client).executeUpdate(); em.remove(client); return true; @@ -1065,7 +1065,8 @@ public class RealmAdapter implements RealmModel { @Override public void setMasterAdminApp(ApplicationModel app) { - realm.setMasterAdminApp(((ApplicationAdapter) app).getJpaEntity()); + ApplicationEntity appEntity = app!=null ? em.getReference(ApplicationEntity.class, app.getId()) : null; + realm.setMasterAdminApp(appEntity); em.flush(); } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index 361d2692a4..2a5658647d 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -986,7 +986,8 @@ public class RealmAdapter extends AbstractMongoAdapter impleme @Override public void setMasterAdminApp(ApplicationModel app) { - realm.setAdminAppId(app.getId()); + String adminAppId = app != null ? app.getId() : null; + realm.setAdminAppId(adminAppId); updateRealm(); } diff --git a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java index 3f9daca73e..a9eecf0613 100755 --- a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java @@ -31,10 +31,7 @@ public class ApplicationManager { } public ApplicationModel createApplication(RealmModel realm, String name) { - ApplicationModel app = realm.addApplication(name); - KeycloakModelUtils.generateSecret(app); - - return app; + return KeycloakModelUtils.createApplication(realm, name); } public boolean removeApplication(RealmModel realm, ApplicationModel application) { diff --git a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java index b8c1c886f1..f69df5155f 100755 --- a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java +++ b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java @@ -20,6 +20,9 @@ public class OAuthClientManager { private RealmManager realmManager; + public OAuthClientManager() { + } + public OAuthClientManager(RealmManager realmManager) { this.realmManager = realmManager; } diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index dcdc27695e..15ef125154 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -2,6 +2,7 @@ package org.keycloak.services.managers; import org.jboss.logging.Logger; import org.keycloak.exportimport.util.ExportImportUtils; +import org.keycloak.exportimport.util.ImportUtils; import org.keycloak.models.AccountRoles; import org.keycloak.models.AdminRoles; import org.keycloak.models.ApplicationModel; @@ -143,32 +144,9 @@ public class RealmManager { } } + // Should be RealmManager moved to model/api instead of referencing methods this way? private void setupMasterAdminManagement(RealmModel realm) { - RealmModel adminRealm; - RoleModel adminRole; - - if (realm.getName().equals(Config.getAdminRealm())) { - adminRealm = realm; - - adminRole = realm.addRole(AdminRoles.ADMIN); - - RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM); - adminRole.addCompositeRole(createRealmRole); - } else { - adminRealm = model.getRealmByName(Config.getAdminRealm()); - adminRole = adminRealm.getRole(AdminRoles.ADMIN); - } - - ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session)); - - ApplicationModel realmAdminApp = applicationManager.createApplication(adminRealm, ExportImportUtils.getMasterRealmAdminApplicationName(realm)); - realmAdminApp.setBearerOnly(true); - realm.setMasterAdminApp(realmAdminApp); - - for (String r : AdminRoles.ALL_REALM_ROLES) { - RoleModel role = realmAdminApp.addRole(r); - adminRole.addCompositeRole(role); - } + ImportUtils.setupMasterAdminManagement(model, realm); } private void setupRealmAdminManagement(RealmModel realm) { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java index 688b54ce33..20b5f09fef 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java @@ -1,12 +1,16 @@ package org.keycloak.testsuite.exportimport; import java.io.File; +import java.util.Properties; import org.junit.Assert; import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExternalResource; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; import org.keycloak.Config; import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.dir.DirExportProvider; @@ -28,8 +32,39 @@ import org.keycloak.testutils.KeycloakServer; */ public class ExportImportTest { - @ClassRule - public static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() { + // We want data to be persisted among server restarts + private static ExternalResource hibernateSetupRule = new ExternalResource() { + + private boolean setupDone = false; + + @Override + protected void before() throws Throwable { + if (System.getProperty("hibernate.connection.url") == null) { + String baseExportImportDir = getExportImportTestDirectory(); + + File oldDBFile = new File(baseExportImportDir, "keycloakDB.h2.db"); + if (oldDBFile.exists()) { + oldDBFile.delete(); + } + + String dbDir = baseExportImportDir + "/keycloakDB"; + System.setProperty("hibernate.connection.url", "jdbc:h2:file:" + dbDir + ";DB_CLOSE_DELAY=-1"); + System.setProperty("hibernate.hbm2ddl.auto", "update"); + setupDone = true; + } + } + + @Override + protected void after() { + if (setupDone) { + Properties sysProps = System.getProperties(); + sysProps.remove("hibernate.connection.url"); + sysProps.remove("hibernate.hbm2ddl.auto"); + } + } + }; + + private static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { @@ -41,7 +76,12 @@ public class ExportImportTest { }); - @Test + @ClassRule + public static TestRule chain = RuleChain + .outerRule(hibernateSetupRule) + .around(keycloakRule); + + //@Test public void testDirFullExportImport() throws Throwable { ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID); String targetDirPath = getExportImportTestDirectory() + File.separator + "dirExport"; @@ -55,7 +95,7 @@ public class ExportImportTest { Assert.assertEquals(4, new File(targetDirPath).listFiles().length); } - @Test + //@Test public void testDirRealmExportImport() throws Throwable { ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID); String targetDirPath = getExportImportTestDirectory() + File.separator + "dirRealmExport"; @@ -78,7 +118,7 @@ public class ExportImportTest { testFullExportImport(); } - @Test + //@Test public void testSingleFileRealmExportImport() throws Throwable { ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json"; @@ -87,7 +127,7 @@ public class ExportImportTest { testRealmExportImport(); } - @Test + //@Test public void testZipFullExportImport() throws Throwable { ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID); String zipFilePath = getExportImportTestDirectory() + File.separator + "export-full.zip"; @@ -99,7 +139,7 @@ public class ExportImportTest { testFullExportImport(); } - @Test + //@Test public void testZipRealmExportImport() throws Throwable { ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID); String zipFilePath = getExportImportTestDirectory() + File.separator + "export-realm.zip"; @@ -121,8 +161,8 @@ public class ExportImportTest { // Delete some realm (and some data in admin realm) KeycloakSession session = keycloakRule.startSession(); try { - ModelProvider model = session.getModel(); - model.removeRealm(model.getRealmByName("test").getId()); + ModelProvider model = session.model(); + new RealmManager(session).removeRealm(model.getRealmByName("test")); Assert.assertEquals(1, model.getRealms().size()); RealmModel master = model.getRealmByName(Config.getAdminRealm()); @@ -146,7 +186,7 @@ public class ExportImportTest { // Ensure data are imported back session = keycloakRule.startSession(); try { - ModelProvider model = session.getModel(); + ModelProvider model = session.model(); Assert.assertEquals(2, model.getRealms().size()); assertAuthenticated(model, Config.getAdminRealm(), "admin2", "admin2"); @@ -170,8 +210,8 @@ public class ExportImportTest { // Delete some realm (and some data in admin realm) KeycloakSession session = keycloakRule.startSession(); try { - ModelProvider model = session.getModel(); - model.removeRealm(model.getRealmByName("test").getId()); + ModelProvider model = session.model(); + new RealmManager(session).removeRealm(model.getRealmByName("test")); Assert.assertEquals(1, model.getRealms().size()); RealmModel master = model.getRealmByName(Config.getAdminRealm()); @@ -195,7 +235,7 @@ public class ExportImportTest { // Ensure data are imported back, but just for "test" realm session = keycloakRule.startSession(); try { - ModelProvider model = session.getModel(); + ModelProvider model = session.model(); Assert.assertEquals(2, model.getRealms().size()); assertNotAuthenticated(model, Config.getAdminRealm(), "admin2", "admin2"); @@ -249,7 +289,7 @@ public class ExportImportTest { user.updateCredential(creds); } - private String getExportImportTestDirectory() { + private static String getExportImportTestDirectory() { String dirPath = null; String relativeDirExportImportPath = "testsuite" + File.separator + "integration" + File.separator + "target" + File.separator + "export-import"; From 726ce08700c212efce5d774f438b0556646dc0e4 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 15 Jul 2014 16:43:55 +0200 Subject: [PATCH 3/3] More cleanup and removed export-import-impl module --- export-import/export-import-impl/pom.xml | 236 ------------ .../ExportImportPropertiesManager.java | 152 -------- .../ExportImportProviderImpl.java | 82 ----- .../keycloak/exportimport/ModelExporter.java | 335 ------------------ .../keycloak/exportimport/ModelImporter.java | 327 ----------------- .../io/ExportImportIOProvider.java | 14 - .../exportimport/io/ExportWriter.java | 13 - .../exportimport/io/ImportReader.java | 13 - .../TmpDirExportImportIOProvider.java | 36 -- .../io/directory/TmpDirExportWriter.java | 82 ----- .../io/directory/TmpDirImportReader.java | 67 ---- .../io/zip/EncryptedZIPExportWriter.java | 66 ---- .../io/zip/EncryptedZIPIOProvider.java | 48 --- .../io/zip/EncryptedZIPImportReader.java | 70 ---- ...keycloak.exportimport.ExportImportProvider | 1 - ...oak.exportimport.io.ExportImportIOProvider | 2 - .../JPAToMongoExportImportTest.java | 14 - .../MongoToJPAExportImportTest.java | 17 - export-import/pom.xml | 1 - .../cache/DefaultCacheModelProvider.java | 14 + .../aerogear-ups/auth-server/pom.xml | 7 +- server/pom.xml | 12 +- .../exportimport/ExportImportTest.java | 39 +- .../performance/CreateRealmsWorker.java | 7 +- testsuite/tools/pom.xml | 12 +- .../org/keycloak/test/tools/PerfTools.java | 10 +- 26 files changed, 78 insertions(+), 1599 deletions(-) delete mode 100755 export-import/export-import-impl/pom.xml delete mode 100755 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportPropertiesManager.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java delete mode 100755 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelExporter.java delete mode 100755 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ExportImportIOProvider.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ExportWriter.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ImportReader.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportWriter.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirImportReader.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPExportWriter.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java delete mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPImportReader.java delete mode 100644 export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportImportProvider delete mode 100644 export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.io.ExportImportIOProvider delete mode 100644 export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java delete mode 100644 export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java diff --git a/export-import/export-import-impl/pom.xml b/export-import/export-import-impl/pom.xml deleted file mode 100755 index d32455e0e2..0000000000 --- a/export-import/export-import-impl/pom.xml +++ /dev/null @@ -1,236 +0,0 @@ - - - - keycloak-export-import-parent - org.keycloak - 1.0-beta-4-SNAPSHOT - ../pom.xml - - 4.0.0 - - keycloak-export-import-impl - Keycloak Export Import Impl - - - - - org.keycloak - keycloak-core - ${project.version} - provided - - - org.keycloak - keycloak-model-api - ${project.version} - provided - - - org.keycloak - keycloak-export-import-api - ${project.version} - provided - - - org.jboss.resteasy - jaxrs-api - provided - - - org.jboss.resteasy - resteasy-jaxrs - provided - - - log4j - log4j - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-simple - - - - - org.jboss.logging - jboss-logging - provided - - - org.codehaus.jackson - jackson-core-asl - provided - - - org.codehaus.jackson - jackson-mapper-asl - provided - - - net.iharder - base64 - provided - - - org.bouncycastle - bcprov-jdk16 - provided - - - - de.idyl - winzipaes - provided - - - - org.keycloak - keycloak-model-tests - ${project.version} - tests - test - - - org.keycloak - keycloak-invalidation-cache-model - ${project.version} - test - - - - - - - - - - - - - - - - - - - - - - org.keycloak - keycloak-model-tests - ${project.version} - test - - - org.hibernate.javax.persistence - hibernate-jpa-2.0-api - test - - - org.hibernate - hibernate-entitymanager - test - - - com.h2database - h2 - test - - - junit - junit - test - - - org.mongodb - mongo-java-driver - test - - - - - localhost - 27018 - keycloak - true - 127.0.0.1 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - ${maven.compiler.source} - ${maven.compiler.target} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportPropertiesManager.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportPropertiesManager.java deleted file mode 100755 index 4d49b5a828..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportPropertiesManager.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.keycloak.exportimport; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jboss.logging.Logger; -import org.keycloak.models.utils.reflection.Property; -import org.keycloak.models.utils.reflection.PropertyCriteria; -import org.keycloak.models.utils.reflection.PropertyQueries; - -/** - * Not thread safe (objectProperties). Assumption is that export/import is executed once per JVM - * - * @author Marek Posolda - */ -public class ExportImportPropertiesManager { - - private static final Logger logger = Logger.getLogger(ExportImportPropertiesManager.class); - - private Map, Map>> objectProperties = new HashMap, Map>>(); - - // Add properties of class to objectProperties - protected void initClassProperties(Class clazz) { - Map> classProps = PropertyQueries.createQuery(clazz).addCriteria(new NonEmptyGetterCriteria()).getWritableResultList(); - this.objectProperties.put(clazz, classProps); - } - - public void setBasicPropertiesFromModel(Object model, Object entity) { - Class modelClass = getModelClass(model); - - // Lazy init of properties - checkPropertiesAvailable(modelClass, entity.getClass()); - - Map> modelProps = this.objectProperties.get(modelClass); - Map> entityProps = this.objectProperties.get(entity.getClass()); - - Map> entityPropsCopy = new HashMap>(entityProps); - - logger.debugf("Properties of entity %s: %s", entity, entityProps.keySet()); - for (Property modelProperty : modelProps.values()) { - Property entityProperty = entityPropsCopy.get(modelProperty.getName()); - - entityPropsCopy.remove(modelProperty.getName()); - - if (entityProperty != null) { - Object propertyValue = modelProperty.getValue(model); - - // Workaround needed because model classes have many getters/setters with "Set", but for entity classes, there are usually "List" - if (propertyValue instanceof Set) { - Set propValueAsSet = (Set)propertyValue; - entityProperty.setValue(entity, new ArrayList(propValueAsSet)); - } else { - entityProperty.setValue(entity, propertyValue); - } - if (logger.isTraceEnabled()) { - logger.tracef("Property %s successfully set in JSON to entity %s", modelProperty.getName(), entity); - } - } else { - logger.debugf("Property %s not known in JSON for entity %s", modelProperty.getName(), entity); - } - } - - logger.debugf("Entity properties for manual setup: %s", entityPropsCopy.keySet()); - } - - private void checkPropertiesAvailable(Class modelClass, Class entityClass) { - if (!objectProperties.containsKey(modelClass)) { - initClassProperties(modelClass); - } - if (!objectProperties.containsKey(entityClass)) { - initClassProperties(entityClass); - } - } - - public void setBasicPropertiesToModel(Object model, Object entity) { - Class modelClass = getModelClass(model); - - // Lazy init of properties - checkPropertiesAvailable(modelClass, entity.getClass()); - - Map> modelProps = this.objectProperties.get(modelClass); - Map> entityProps = this.objectProperties.get(entity.getClass()); - - Map> entityPropsCopy = new HashMap>(entityProps); - - logger.debugf("Properties of exported entity %s: %s", entity, entityProps.keySet()); - - for (Property modelProperty : modelProps.values()) { - Property entityProperty = entityPropsCopy.get(modelProperty.getName()); - - entityPropsCopy.remove(modelProperty.getName()); - - if (entityProperty != null) { - Object propertyValue = entityProperty.getValue(entity); - - // Workaround needed because model classes have many getters/setters with "Set", but for entity classes, there are usually "List" - if (propertyValue instanceof List && Set.class.isAssignableFrom(modelProperty.getJavaClass())) { - List propValueAsList = (List)propertyValue; - modelProperty.setValue(model, new HashSet(propValueAsList)); - } else { - modelProperty.setValue(model, propertyValue); - } - - if (logger.isTraceEnabled()) { - logger.tracef("Property %s successfully set in model from entity %s", modelProperty.getName(), entity); - } - } else { - logger.debugf("Property %s not known for entity %s", modelProperty.getName(), entity); - } - } - - logger.debugf("Entity properties for manual setup: %s", entityPropsCopy.keySet()); - } - - protected Class getModelClass(Object model) { - Class modelClass = model.getClass(); - Class[] interfaces = modelClass.getInterfaces(); - - // Bit unsafe, but looks that it works for all "model adapters" so far - if (interfaces.length == 0) { - return modelClass; - } else { - return interfaces[0]; - } - } - - public static class NonEmptyGetterCriteria implements PropertyCriteria { - - private static final List IGNORED_METHODS = Arrays.asList("getPasswordPolicy", "getAuthenticationProviders", "getAuthenticationLink"); - - @Override - public boolean methodMatches(Method m) { - // Ignore non-empty getters - if (m.getParameterTypes().length > 0) { - return false; - } - - // Ignore some "known" getters (for example incompatible types between model and entity) - if (IGNORED_METHODS.contains(m.getName())) { - return false; - } - - return true; - } - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java deleted file mode 100644 index c43d3c5233..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.keycloak.exportimport; - -import org.jboss.logging.Logger; -import org.keycloak.exportimport.io.ExportImportIOProvider; -import org.keycloak.exportimport.io.ExportWriter; -import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.KeycloakTransaction; -import org.keycloak.util.ProviderLoader; - -/** - * @author Marek Posolda - */ -public class ExportImportProviderImpl { - - private static final Logger logger = Logger.getLogger(ExportImportProviderImpl.class); - - public static final String ACTION_EXPORT = "export"; - public static final String ACTION_IMPORT = "import"; - - public void checkExportImport(KeycloakSessionFactory sessionFactory) { - String exportImportAction = ExportImportConfig.getAction(); - - boolean export = false; - boolean importt = false; - if (ACTION_EXPORT.equals(exportImportAction)) { - logger.infof("Full model export requested"); - export = true; - } else if (ACTION_IMPORT.equals(exportImportAction)) { - logger.infof("Full model import requested"); - importt = true; - } - - if (export || importt) { - KeycloakSession session = sessionFactory.create(); - KeycloakTransaction transaction = session.getTransaction(); - try { - transaction.begin(); - - if (export) { - ExportWriter exportWriter = getProvider().getExportWriter(); - new ModelExporter().exportModel(session.model(), exportWriter); - logger.infof("Export finished successfully"); - } else { - ImportReader importReader = getProvider().getImportReader(); - new ModelImporter().importModel(session.model(), importReader); - logger.infof("Import finished successfully"); - } - - if (transaction.isActive()) { - if (transaction.getRollbackOnly()) { - transaction.rollback(); - } else { - transaction.commit(); - } - } - } catch (Exception e) { - if (transaction.isActive()) { - session.getTransaction().rollback(); - } - throw new RuntimeException(e); - } finally { - session.close(); - } - } - } - - private ExportImportIOProvider getProvider() { - String providerId = ExportImportConfig.getProvider(); - logger.infof("Requested migration provider: " + providerId); - - Iterable providers = ProviderLoader.load(ExportImportIOProvider.class); - for (ExportImportIOProvider provider : providers) { - if (providerId.equals(provider.getId())) { - return provider; - } - } - - throw new IllegalStateException("Provider " + providerId + " not found"); - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelExporter.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelExporter.java deleted file mode 100755 index 2b47bcec6c..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelExporter.java +++ /dev/null @@ -1,335 +0,0 @@ -package org.keycloak.exportimport; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.jboss.logging.Logger; -import org.keycloak.exportimport.io.ExportWriter; -import org.keycloak.models.ApplicationModel; -import org.keycloak.models.AuthenticationLinkModel; -import org.keycloak.models.AuthenticationProviderModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelProvider; -import org.keycloak.models.OAuthClientModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RequiredCredentialModel; -import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.SocialLinkModel; -import org.keycloak.models.UserCredentialValueModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UsernameLoginFailureModel; -import org.keycloak.models.entities.ApplicationEntity; -import org.keycloak.models.entities.AuthenticationLinkEntity; -import org.keycloak.models.entities.AuthenticationProviderEntity; -import org.keycloak.models.entities.CredentialEntity; -import org.keycloak.models.entities.OAuthClientEntity; -import org.keycloak.models.entities.RealmEntity; -import org.keycloak.models.entities.RequiredCredentialEntity; -import org.keycloak.models.entities.RoleEntity; -import org.keycloak.models.entities.SocialLinkEntity; -import org.keycloak.models.entities.UserEntity; -import org.keycloak.models.entities.UsernameLoginFailureEntity; - -/** - * @author Marek Posolda - */ -public class ModelExporter { - - private static final Logger logger = Logger.getLogger(ModelExporter.class); - - private ExportWriter exportWriter; - private ExportImportPropertiesManager propertiesManager; - - public void exportModel(ModelProvider model, ExportWriter exportWriter) { - // Initialize needed objects - this.exportWriter = exportWriter; - this.propertiesManager = new ExportImportPropertiesManager(); - - // Create separate files for "realms", "applications", "oauthClients", "roles" and finally "users". Users may be done in more files (pagination) - exportRealms(model, "realms.json"); - exportApplications(model, "applications.json"); - exportOAuthClients(model, "oauthClients.json"); - exportRoles(model, "roles.json"); - exportUsers(model, "users.json"); -// exportUserFailures(model, "userFailures.json"); - - this.exportWriter.closeExportWriter(); - } - - protected void exportRealms(ModelProvider model, String fileName) { - List realms = model.getRealms(); - - // Convert models to entities, which will be written into JSON file - List result = new LinkedList(); - for (RealmModel realmModel : realms) { - RealmEntity entity = new RealmEntity(); - entity.setId(realmModel.getId()); - result.add(entity); - - // Export all basic properties from realm - this.propertiesManager.setBasicPropertiesFromModel(realmModel, entity); - - // Export 'advanced' properties - ApplicationModel realmAdminApp = realmModel.getMasterAdminApp(); - if (realmAdminApp != null) { - entity.setAdminAppId(realmAdminApp.getId()); - } - entity.setDefaultRoles(realmModel.getDefaultRoles()); - - List reqCredEntities = new ArrayList(); - List requiredCredModels = realmModel.getRequiredCredentials(); - for (RequiredCredentialModel requiredCredModel : requiredCredModels) { - RequiredCredentialEntity reqCredEntity = new RequiredCredentialEntity(); - this.propertiesManager.setBasicPropertiesFromModel(requiredCredModel, reqCredEntity); - reqCredEntities.add(reqCredEntity); - } - entity.setRequiredCredentials(reqCredEntities); - - // password policy - entity.setPasswordPolicy(realmModel.getPasswordPolicy().toString()); - - // authentication providers - List authProviderEntities = new ArrayList(); - for (AuthenticationProviderModel authProvider : realmModel.getAuthenticationProviders()) { - AuthenticationProviderEntity authProviderEntity = new AuthenticationProviderEntity(); - this.propertiesManager.setBasicPropertiesFromModel(authProvider, authProviderEntity); - authProviderEntities.add(authProviderEntity); - - } - entity.setAuthenticationProviders(authProviderEntities); - } - - this.exportWriter.writeEntities(fileName, result); - logger.infof("Realms exported: " + result); - } - - protected void exportApplications(ModelProvider model, String fileName) { - List allApplications = getAllApplications(model); - - List result = new LinkedList(); - for (ApplicationModel appModel : allApplications) { - ApplicationEntity appEntity = new ApplicationEntity(); - appEntity.setId(appModel.getId()); - result.add(appEntity); - - this.propertiesManager.setBasicPropertiesFromModel(appModel, appEntity); - - // Export 'advanced' properties of application - appEntity.setRealmId(appModel.getRealm().getId()); - appEntity.setDefaultRoles(appModel.getDefaultRoles()); - - List scopeIds = getScopeIds(appModel); - appEntity.setScopeIds(scopeIds); - } - - this.exportWriter.writeEntities(fileName, result); - logger.infof("Applications exported: " + result); - } - - protected void exportOAuthClients(ModelProvider model, String fileName) { - List realms = model.getRealms(); - List allClients = new ArrayList(); - for (RealmModel realmModel : realms) { - allClients.addAll(realmModel.getOAuthClients()); - } - - List result = new LinkedList(); - for (OAuthClientModel clientModel : allClients) { - OAuthClientEntity clientEntity = new OAuthClientEntity(); - clientEntity.setId(clientModel.getId()); - result.add(clientEntity); - - this.propertiesManager.setBasicPropertiesFromModel(clientModel, clientEntity); - - // Export 'advanced' properties of client - clientEntity.setName(clientModel.getClientId()); - clientEntity.setRealmId(clientModel.getRealm().getId()); - - List scopeIds = getScopeIds(clientModel); - clientEntity.setScopeIds(scopeIds); - } - - this.exportWriter.writeEntities(fileName, result); - logger.infof("OAuth clients exported: " + result); - } - - protected void exportRoles(ModelProvider model, String fileName) { - List allRoles = getAllRoles(model); - - List result = new LinkedList(); - for (RoleModel roleModel : allRoles) { - RoleEntity roleEntity = new RoleEntity(); - roleEntity.setId(roleModel.getId()); - result.add(roleEntity); - - roleEntity.setName(roleModel.getName()); - roleEntity.setDescription(roleModel.getDescription()); - - RoleContainerModel roleContainer = roleModel.getContainer(); - if (roleContainer instanceof RealmModel) { - RealmModel realm = (RealmModel)roleContainer; - roleEntity.setRealmId(realm.getId()); - } else { - ApplicationModel appModel = (ApplicationModel)roleContainer; - roleEntity.setApplicationId(appModel.getId()); - } - - List compositeRolesIds = null; - for (RoleModel composite : roleModel.getComposites()) { - - // Lazy init - if (compositeRolesIds == null) { - compositeRolesIds = new ArrayList(); - } - - compositeRolesIds.add(composite.getId()); - } - roleEntity.setCompositeRoleIds(compositeRolesIds); - } - - this.exportWriter.writeEntities(fileName, result); - - logger.infof("%d roles exported: ", result.size()); - if (logger.isDebugEnabled()) { - logger.debug("Exported roles: " + result); - } - } - - protected void exportUsers(ModelProvider model, String fileName) { - List realms = model.getRealms(); - List result = new LinkedList(); - - for (RealmModel realm : realms) { - List userModels = realm.getUsers(); - for (UserModel userModel : userModels) { - UserEntity userEntity = new UserEntity(); - userEntity.setId(userModel.getId()); - result.add(userEntity); - - this.propertiesManager.setBasicPropertiesFromModel(userModel, userEntity); - - userEntity.setUsername(userModel.getUsername()); - userEntity.setRealmId(realm.getId()); - - // authentication links - AuthenticationLinkModel authLink = userModel.getAuthenticationLink(); - if (authLink != null) { - AuthenticationLinkEntity authLinkEntity = new AuthenticationLinkEntity(); - this.propertiesManager.setBasicPropertiesFromModel(authLink, authLinkEntity); - - userEntity.setAuthenticationLink(authLinkEntity); - } - - // social links - Set socialLinks = realm.getSocialLinks(userModel); - if (socialLinks != null && !socialLinks.isEmpty()) { - List socialLinkEntities = new ArrayList(); - for (SocialLinkModel socialLink : socialLinks) { - SocialLinkEntity socialLinkEntity = new SocialLinkEntity(); - this.propertiesManager.setBasicPropertiesFromModel(socialLink, socialLinkEntity); - - socialLinkEntities.add(socialLinkEntity); - } - - userEntity.setSocialLinks(socialLinkEntities); - } - - // required actions - Set requiredActions = userModel.getRequiredActions(); - if (requiredActions != null && !requiredActions.isEmpty()) { - userEntity.setRequiredActions(new ArrayList(requiredActions)); - } - - // attributes - userEntity.setAttributes(userModel.getAttributes()); - - // roleIds - Set roles = userModel.getRoleMappings(); - List roleIds = new ArrayList(); - for (RoleModel role : roles) { - roleIds.add(role.getId()); - } - userEntity.setRoleIds(roleIds); - - // credentials - List credentials = userModel.getCredentialsDirectly(); - List credEntities = new ArrayList(); - for (UserCredentialValueModel credModel : credentials) { - CredentialEntity credEntity = new CredentialEntity(); - this.propertiesManager.setBasicPropertiesFromModel(credModel, credEntity); - credEntities.add(credEntity); - } - - userEntity.setCredentials(credEntities); - } - } - - this.exportWriter.writeEntities(fileName, result); - - logger.infof("%d users exported: ", result.size()); - if (logger.isDebugEnabled()) { - logger.debug("Exported users: " + result); - } - } - - - // Does it makes sense to export user failures ? -// protected void exportUserFailures(ModelProvider model, String fileName) { -// List realms = model.getRealms(); -// List allFailures = new ArrayList(); -// for (RealmModel realmModel : realms) { -// allFailures.addAll(realmModel.getAllUserLoginFailures()); -// } -// -// List result = new LinkedList(); -// for (UsernameLoginFailureModel failureModel : allFailures) { -// UsernameLoginFailureEntity failureEntity = new UsernameLoginFailureEntity(); -// this.propertiesManager.setBasicPropertiesFromModel(failureModel, failureEntity); -// result.add(failureEntity); -// -// failureEntity.setUsername(failureModel.getUsername()); -// failureEntity.setNumFailures(failureModel.getNumFailures()); -// } -// -// this.exportWriter.writeEntities(fileName, result); -// } - - private List getScopeIds(ClientModel clientModel) { - Set allScopes = clientModel.getScopeMappings(); - List scopeIds = new ArrayList(); - for (RoleModel role : allScopes) { - scopeIds.add(role.getId()); - } - return scopeIds; - } - - private List getAllApplications(ModelProvider model) { - List realms = model.getRealms(); - List allApplications = new ArrayList(); - for (RealmModel realmModel : realms) { - allApplications.addAll(realmModel.getApplications()); - } - return allApplications; - } - - private List getAllRoles(ModelProvider model) { - List allRoles = new ArrayList(); - - List realms = model.getRealms(); - for (RealmModel realmModel : realms) { - allRoles.addAll(realmModel.getRoles()); - } - - List allApplications = getAllApplications(model); - for (ApplicationModel appModel : allApplications) { - allRoles.addAll(appModel.getRoles()); - } - - return allRoles; - } - -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java deleted file mode 100755 index 35371b44f0..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java +++ /dev/null @@ -1,327 +0,0 @@ -package org.keycloak.exportimport; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jboss.logging.Logger; -import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.models.ApplicationModel; -import org.keycloak.models.AuthenticationLinkModel; -import org.keycloak.models.AuthenticationProviderModel; -import org.keycloak.models.ClientModel; -import org.keycloak.Config; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelProvider; -import org.keycloak.models.OAuthClientModel; -import org.keycloak.models.PasswordPolicy; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.SocialLinkModel; -import org.keycloak.models.UserCredentialValueModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UsernameLoginFailureModel; -import org.keycloak.models.entities.ApplicationEntity; -import org.keycloak.models.entities.AuthenticationLinkEntity; -import org.keycloak.models.entities.AuthenticationProviderEntity; -import org.keycloak.models.entities.ClientEntity; -import org.keycloak.models.entities.CredentialEntity; -import org.keycloak.models.entities.OAuthClientEntity; -import org.keycloak.models.entities.RealmEntity; -import org.keycloak.models.entities.RequiredCredentialEntity; -import org.keycloak.models.entities.RoleEntity; -import org.keycloak.models.entities.SocialLinkEntity; -import org.keycloak.models.entities.UserEntity; -import org.keycloak.models.entities.UsernameLoginFailureEntity; - -/** - * @author Marek Posolda - */ -public class ModelImporter { - - private static final Logger logger = Logger.getLogger(ModelImporter.class); - - private ImportReader importReader; - private ExportImportPropertiesManager propertiesManager; - - public void importModel(ModelProvider model, ImportReader importReader) { - // Initialize needed objects - this.importReader = importReader; - this.propertiesManager = new ExportImportPropertiesManager(); - - importRealms(model, "realms.json"); - importApplications(model, "applications.json"); - importRoles(model, "roles.json"); - - // Now we have all realms,applications and roles filled. So fill other objects (default roles, scopes etc) - importRealmsStep2(model, "realms.json"); - importApplicationsStep2(model, "applications.json"); - - importOAuthClients(model, "oauthClients.json"); - importUsers(model, "users.json"); -// importUserFailures(model, "userFailures.json"); - - this.importReader.closeImportReader(); - } - - protected void importRealms(ModelProvider model, String fileName) { - List realms = this.importReader.readEntities(fileName, RealmEntity.class); - - for (RealmEntity realmEntity : realms) { - RealmModel realm = model.createRealm(realmEntity.getId(), realmEntity.getName()); - - this.propertiesManager.setBasicPropertiesToModel(realm, realmEntity); - - Set reqCredModels = new HashSet(); - for (RequiredCredentialEntity requiredCredEntity : realmEntity.getRequiredCredentials()) { - reqCredModels.add(requiredCredEntity.getType()); - } - realm.updateRequiredCredentials(reqCredModels); - - // AdminApp and defaultRoles are set in step2 - - // password policy - realm.setPasswordPolicy(new PasswordPolicy(realmEntity.getPasswordPolicy())); - - // authentication providers - List authProviderModels = new ArrayList(); - for (AuthenticationProviderEntity authProviderEntity : realmEntity.getAuthenticationProviders()) { - AuthenticationProviderModel authProvider = new AuthenticationProviderModel(); - this.propertiesManager.setBasicPropertiesToModel(authProvider, authProviderEntity); - authProviderModels.add(authProvider); - - } - realm.setAuthenticationProviders(authProviderModels); - } - - logger.infof("Realms imported: " + realms); - } - - protected void importApplications(ModelProvider model, String fileName) { - List apps = this.importReader.readEntities(fileName, ApplicationEntity.class); - for (ApplicationEntity appEntity : apps) { - RealmModel realm = model.getRealm(appEntity.getRealmId()); - ApplicationModel app = realm.addApplication(appEntity.getId(), appEntity.getName()); - - this.propertiesManager.setBasicPropertiesToModel(app , appEntity); - - // scopeIds and default roles will be done in step2 - } - - logger.infof("Applications imported: " + apps); - } - - protected void importRoles(ModelProvider model, String fileName) { - // helper map for composite roles - Map rolesMap = new HashMap(); - - List roles = this.importReader.readEntities(fileName, RoleEntity.class); - for (RoleEntity roleEntity : roles) { - RoleModel role = null; - if (roleEntity.getRealmId() != null) { - RealmModel realm = model.getRealm(roleEntity.getRealmId()); - role = realm.addRole(roleEntity.getId(), roleEntity.getName()); - } else if (roleEntity.getApplicationId() != null) { - ApplicationModel app = findApplicationById(model, roleEntity.getApplicationId()); - role = app.addRole(roleEntity.getId(), roleEntity.getName()); - } else { - throw new IllegalStateException("Role " + roleEntity.getId() + " doesn't have realmId nor applicationId"); - } - - role.setDescription(roleEntity.getDescription()); - - rolesMap.put(roleEntity.getId(), roleEntity); - } - - // All roles were added. Fill composite roles now - for (RealmModel realm : model.getRealms()) { - - // realm roles - fillCompositeRoles(rolesMap, realm, realm); - - // app roles - for (ApplicationModel app : realm.getApplications()) { - fillCompositeRoles(rolesMap, app, realm); - } - } - - logger.infof("%d roles imported: ", roles.size()); - if (logger.isDebugEnabled()) { - logger.debug("Imported roles: " + roles); - } - } - - private void fillCompositeRoles(Map rolesMap, RoleContainerModel roleContainer, RealmModel realm) { - for (RoleModel role : roleContainer.getRoles()) { - RoleEntity roleEntity = rolesMap.get(role.getId()); - - if (roleEntity.getCompositeRoleIds() == null) { - continue; - } - - for (String compositeRoleId : roleEntity.getCompositeRoleIds()) { - RoleModel compositeRole = realm.getRoleById(compositeRoleId); - role.addCompositeRole(compositeRole); - } - } - } - - protected void importRealmsStep2(ModelProvider model, String fileName) { - List realms = this.importReader.readEntities(fileName, RealmEntity.class); - RealmModel adminRealm = model.getRealm(Config.getAdminRealm()); - - for (RealmEntity realmEntity : realms) { - RealmModel realm = model.getRealm(realmEntity.getId()); - - // admin app - String adminAppId = realmEntity.getAdminAppId(); - if (adminAppId != null) { - realm.setMasterAdminApp(adminRealm.getApplicationById(adminAppId)); - } - - // Default roles - realm.updateDefaultRoles(realmEntity.getDefaultRoles().toArray(new String[] {})); - } - } - - protected void importApplicationsStep2(ModelProvider model, String fileName) { - List apps = this.importReader.readEntities(fileName, ApplicationEntity.class); - for (ApplicationEntity appEntity : apps) { - RealmModel realm = model.getRealm(appEntity.getRealmId()); - ApplicationModel application = realm.getApplicationById(appEntity.getId()); - - // Default roles - application.updateDefaultRoles(appEntity.getDefaultRoles().toArray(new String[] {})); - - // Scopes - addScopes(realm, application, appEntity); - } - } - - private void addScopes(RealmModel realm, ClientModel client, ClientEntity clientEntity) { - for (String scopeId : clientEntity.getScopeIds()) { - RoleModel scope = realm.getRoleById(scopeId); - client.addScopeMapping(scope); - } - } - - protected void importOAuthClients(ModelProvider model, String fileName) { - List clients = this.importReader.readEntities(fileName, OAuthClientEntity.class); - for (OAuthClientEntity clientEntity : clients) { - RealmModel realm = model.getRealm(clientEntity.getRealmId()); - OAuthClientModel client = realm.addOAuthClient(clientEntity.getId(), clientEntity.getName()); - - this.propertiesManager.setBasicPropertiesToModel(client, clientEntity); - - client.setClientId(clientEntity.getName()); - - // Scopes. All roles are already added at this point - addScopes(realm, client, clientEntity); - } - - logger.info("OAuth clients imported: " + clients); - } - - protected ApplicationModel findApplicationById(ModelProvider model, String applicationId) { - for (RealmModel realm : model.getRealms()) { - ApplicationModel appModel = realm.getApplicationById(applicationId); - if (appModel != null) { - return appModel; - } - } - - return null; - } - - public void importUsers(ModelProvider model, String fileName) { - List users = this.importReader.readEntities(fileName, UserEntity.class); - for (UserEntity userEntity : users) { - RealmModel realm = model.getRealm(userEntity.getRealmId()); - UserModel user = realm.addUser(userEntity.getId(), userEntity.getUsername(), false); - - // We need to remove defaultRoles here as realm.addUser is automatically adding them. We may add them later during roles mapping processing - for (RoleModel role : user.getRoleMappings()) { - user.deleteRoleMapping(role); - } - - this.propertiesManager.setBasicPropertiesToModel(user, userEntity); - - // authentication links - AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink(); - if (authLinkEntity != null) { - AuthenticationLinkModel authLinkModel = new AuthenticationLinkModel(); - this.propertiesManager.setBasicPropertiesToModel(authLinkModel, authLinkEntity); - - user.setAuthenticationLink(authLinkModel); - } - - // social links - List socialLinks = userEntity.getSocialLinks(); - if (socialLinks != null && !socialLinks.isEmpty()) { - for (SocialLinkEntity socialLinkEntity : socialLinks) { - SocialLinkModel socialLink = new SocialLinkModel(); - this.propertiesManager.setBasicPropertiesToModel(socialLink, socialLinkEntity); - - realm.addSocialLink(user, socialLink); - } - } - - // required actions - List requiredActions = userEntity.getRequiredActions(); - if (requiredActions != null && !requiredActions.isEmpty()) { - for (UserModel.RequiredAction reqAction : requiredActions) { - user.addRequiredAction(reqAction); - } - } - - // attributes - if (userEntity.getAttributes() != null) { - for (Map.Entry attr : userEntity.getAttributes().entrySet()) { - user.setAttribute(attr.getKey(), attr.getValue()); - } - } - - // roles - if (userEntity.getRoleIds() != null) { - for (String roleId : userEntity.getRoleIds()) { - RoleModel role = realm.getRoleById(roleId); - user.grantRole(role); - } - } - - // credentials - List credentials = userEntity.getCredentials(); - if (credentials != null) { - for (CredentialEntity credEntity : credentials) { - UserCredentialValueModel credModel = new UserCredentialValueModel(); - this.propertiesManager.setBasicPropertiesToModel(credModel, credEntity); - - user.updateCredentialDirectly(credModel); - } - } - } - - logger.infof("%d users imported: ", users.size()); - if (logger.isDebugEnabled()) { - logger.debug("Imported users: " + users); - } - } - -// public void importUserFailures(ModelProvider model, String fileName) { -// List userFailures = this.importReader.readEntities(fileName, UsernameLoginFailureEntity.class); -// for (UsernameLoginFailureEntity entity : userFailures) { -// RealmModel realm = model.getRealm(entity.getRealmId()); -// UsernameLoginFailureModel loginFailureModel = realm.addUserLoginFailure(entity.getUsername()); -// -// this.propertiesManager.setBasicPropertiesToModel(loginFailureModel , entity); -// -// for (int i=0 ; iMarek Posolda - */ -public interface ExportImportIOProvider { - - String getId(); - - ExportWriter getExportWriter(); - - ImportReader getImportReader(); - -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ExportWriter.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ExportWriter.java deleted file mode 100644 index c90a57f794..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ExportWriter.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.keycloak.exportimport.io; - -import java.util.List; - -/** - * @author Marek Posolda - */ -public interface ExportWriter { - - void writeEntities(String fileName, List entities); - - void closeExportWriter(); -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ImportReader.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ImportReader.java deleted file mode 100644 index 83faabd3cc..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/ImportReader.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.keycloak.exportimport.io; - -import java.util.List; - -/** - * @author Marek Posolda - */ -public interface ImportReader { - - List readEntities(String fileName, Class entityClass); - - void closeImportReader(); -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java deleted file mode 100644 index b785c4174a..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.keycloak.exportimport.io.directory; - -import org.keycloak.exportimport.ExportImportConfig; -import org.keycloak.exportimport.io.ExportImportIOProvider; -import org.keycloak.exportimport.io.ExportWriter; -import org.keycloak.exportimport.io.ImportReader; - -import java.io.File; - -/** - * Export/import into JSON files inside "tmp" directory. This implementation is used mainly for testing - * (shouldn't be used in production due to passwords in JSON files) - * - * @author Marek Posolda - */ -public class TmpDirExportImportIOProvider implements ExportImportIOProvider { - - public static final String PROVIDER_ID = "dir"; - - @Override - public ExportWriter getExportWriter() { - String dir = ExportImportConfig.getDir(); - return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter(); - } - - @Override - public ImportReader getImportReader() { - String dir = ExportImportConfig.getDir(); - return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader(); - } - - @Override - public String getId() { - return PROVIDER_ID; - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportWriter.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportWriter.java deleted file mode 100644 index d1ee454128..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportWriter.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.keycloak.exportimport.io.directory; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.List; - -import org.codehaus.jackson.map.ObjectMapper; -import org.jboss.logging.Logger; -import org.keycloak.exportimport.io.ExportWriter; -import org.keycloak.util.JsonSerialization; - -/** - * @author Marek Posolda - */ -public class TmpDirExportWriter implements ExportWriter { - - private static final Logger logger = Logger.getLogger(TmpDirExportWriter.class); - - private final ObjectMapper objectMapper; - private final File rootDirectory; - - public TmpDirExportWriter() { - // Determine system tmp directory - String tempDir = System.getProperty("java.io.tmpdir"); - - // Delete and recreate directory inside tmp - this.rootDirectory = new File(tempDir + "/keycloak-export"); - if (this.rootDirectory .exists()) { - recursiveDeleteDir(this.rootDirectory ); - } - this.rootDirectory.mkdirs(); - - logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath()); - this.objectMapper = getObjectMapper(); - } - - public TmpDirExportWriter(File rootDirectory) { - this.rootDirectory = rootDirectory; - this.rootDirectory.mkdirs(); - this.objectMapper = getObjectMapper(); - - logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath()); - } - - private ObjectMapper getObjectMapper() { - return JsonSerialization.prettyMapper; - } - - protected boolean recursiveDeleteDir(File dirPath) { - if (dirPath.exists()) { - File[] files = dirPath.listFiles(); - for (int i = 0; i < files.length; i++) { - if (files[i].isDirectory()) { - recursiveDeleteDir(files[i]); - } else { - files[i].delete(); - } - } - } - if (dirPath.exists()) - return dirPath.delete(); - else - return true; - } - - @Override - public void writeEntities(String fileName, List entities) { - try { - File file = new File(this.rootDirectory, fileName); - FileOutputStream stream = new FileOutputStream(file); - this.objectMapper.writeValue(stream, entities); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - @Override - public void closeExportWriter() { - //To change body of implemented methods use File | Settings | File Templates. - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirImportReader.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirImportReader.java deleted file mode 100644 index 942e23a93b..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirImportReader.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.keycloak.exportimport.io.directory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.List; - -import org.codehaus.jackson.map.ObjectMapper; -import org.jboss.logging.Logger; -import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.util.JsonSerialization; - -/** - * @author Marek Posolda - */ -public class TmpDirImportReader implements ImportReader { - - private static final Logger logger = Logger.getLogger(TmpDirImportReader.class); - - private final ObjectMapper objectMapper; - private final File rootDirectory; - - public TmpDirImportReader() { - // Determine system tmp directory - String tempDir = System.getProperty("java.io.tmpdir"); - - // Delete and recreate directory inside tmp - this.rootDirectory = new File(tempDir + "/keycloak-export"); - if (!this.rootDirectory .exists()) { - throw new IllegalStateException("Directory " + this.rootDirectory + " doesn't exists"); - } - - logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath()); - this.objectMapper = getObjectMapper(); - } - - public TmpDirImportReader(File rootDirectory) { - this.rootDirectory = rootDirectory; - this.objectMapper = getObjectMapper(); - - logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath()); - } - - private ObjectMapper getObjectMapper() { - return JsonSerialization.prettyMapper; - } - - @Override - public List readEntities(String fileName, Class entityClass) { - try { - File file = new File(this.rootDirectory, fileName); - FileInputStream stream = new FileInputStream(file); - T[] template = (T[]) Array.newInstance(entityClass, 0); - T[] result = (T[])this.objectMapper.readValue(stream, template.getClass()); - return Arrays.asList(result); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - @Override - public void closeImportReader() { - //TODO: Should directory be deleted after import? - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPExportWriter.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPExportWriter.java deleted file mode 100644 index 0b49940b84..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPExportWriter.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.keycloak.exportimport.io.zip; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.util.List; - -import de.idyl.winzipaes.AesZipFileEncrypter; -import de.idyl.winzipaes.impl.AESEncrypter; -import de.idyl.winzipaes.impl.AESEncrypterBC; -import org.codehaus.jackson.map.ObjectMapper; -import org.keycloak.exportimport.io.ExportWriter; -import org.keycloak.util.JsonSerialization; - -/** - * @author Marek Posolda - */ -public class EncryptedZIPExportWriter implements ExportWriter { - - private final File zipFile; - private final ObjectMapper objectMapper; - private final String password; - - private final AesZipFileEncrypter encrypter; - - - public EncryptedZIPExportWriter(String zipFileName, String password) { - try { - this.zipFile = new File(zipFileName); - if (zipFile.exists()) { - throw new IllegalStateException("File " + zipFileName + " already exists"); - } - - this.objectMapper = JsonSerialization.mapper; - - AESEncrypter encrypter = new AESEncrypterBC(); - this.encrypter = new AesZipFileEncrypter(this.zipFile, encrypter); - this.password = password; - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - @Override - public void writeEntities(String fileName, List entities) { - try { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - this.objectMapper.writeValue(stream, entities); - - byte[] byteArray = stream.toByteArray(); - this.encrypter.add(fileName, new ByteArrayInputStream(byteArray), this.password); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - @Override - public void closeExportWriter() { - try { - this.encrypter.close(); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java deleted file mode 100644 index 31883acb14..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.keycloak.exportimport.io.zip; - -import org.jboss.logging.Logger; -import org.keycloak.exportimport.ExportImportConfig; -import org.keycloak.exportimport.io.ExportImportIOProvider; -import org.keycloak.exportimport.io.ExportWriter; -import org.keycloak.exportimport.io.ImportReader; - -/** - * @author Marek Posolda - */ -public class EncryptedZIPIOProvider implements ExportImportIOProvider { - - private static final Logger logger = Logger.getLogger(EncryptedZIPIOProvider.class); - - public static final String PROVIDER_ID = "zip"; - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public ExportWriter getExportWriter() { - String zipFile = ExportImportConfig.getZipFile(); - String zipPassword = ExportImportConfig.getZipPassword(); - logger.infof("Using zip for export: " + zipFile); - - if (zipFile==null || zipPassword==null) { - throw new IllegalArgumentException("zipFile or zipPassword are null"); - } - - return new EncryptedZIPExportWriter(zipFile, zipPassword); - } - - @Override - public ImportReader getImportReader() { - String zipFile = ExportImportConfig.getZipFile(); - String zipPassword = ExportImportConfig.getZipPassword(); - logger.infof("Using zip for import: " + zipFile); - - if (zipFile==null || zipPassword==null) { - throw new IllegalArgumentException("zipFile or zipPassword are null"); - } - - return new EncryptedZIPImportReader(zipFile, zipPassword); - } -} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPImportReader.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPImportReader.java deleted file mode 100644 index d115b7d6e9..0000000000 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPImportReader.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.keycloak.exportimport.io.zip; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.List; - -import de.idyl.winzipaes.AesZipFileDecrypter; -import de.idyl.winzipaes.impl.AESDecrypter; -import de.idyl.winzipaes.impl.AESDecrypterBC; -import org.codehaus.jackson.map.ObjectMapper; -import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.util.JsonSerialization; - -/** - * @author Marek Posolda - */ -public class EncryptedZIPImportReader implements ImportReader { - - private final File zipFile; - private final ObjectMapper objectMapper; - private final String password; - - private final AesZipFileDecrypter decrypter; - - - public EncryptedZIPImportReader(String zipFileName, String password) { - try { - this.zipFile = new File(zipFileName); - if (!zipFile.exists()) { - throw new IllegalStateException("File " + zipFileName + " doesn't exists"); - } - - this.objectMapper = JsonSerialization.mapper; - - AESDecrypter decrypter = new AESDecrypterBC(); - this.decrypter = new AesZipFileDecrypter(this.zipFile, decrypter); - this.password = password; - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - @Override - public List readEntities(String fileName, Class entityClass) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - this.decrypter.extractEntry(this.decrypter.getEntry(fileName), bos, this.password); - - ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); - T[] template = (T[]) Array.newInstance(entityClass, 0); - T[] result = (T[])this.objectMapper.readValue(bis, template.getClass()); - return Arrays.asList(result); - } catch (Exception ioe) { - throw new RuntimeException(ioe); - } - } - - @Override - public void closeImportReader() { - try { - this.decrypter.close(); - } catch (Exception ioe) { - throw new RuntimeException(ioe); - } - } -} diff --git a/export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportImportProvider b/export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportImportProvider deleted file mode 100644 index 353987ffb8..0000000000 --- a/export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.ExportImportProvider +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.exportimport.ExportImportProviderImpl diff --git a/export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.io.ExportImportIOProvider b/export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.io.ExportImportIOProvider deleted file mode 100644 index 75c97ec96b..0000000000 --- a/export-import/export-import-impl/src/main/resources/META-INF/services/org.keycloak.exportimport.io.ExportImportIOProvider +++ /dev/null @@ -1,2 +0,0 @@ -org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider -org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java deleted file mode 100644 index 32be243a71..0000000000 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.keycloak.exportimport; - -import org.junit.Ignore; -import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider; -import org.keycloak.models.KeycloakSessionFactory; - -/** - * Test for full export of data from JPA and import them to Mongo. Using "directory" provider - * - * @author Marek Posolda - */ -@Ignore -public class JPAToMongoExportImportTest { -} diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java deleted file mode 100644 index 686722a29c..0000000000 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.keycloak.exportimport; - -import org.junit.Assert; -import org.junit.Ignore; -import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider; -import org.keycloak.models.KeycloakSessionFactory; - -import java.io.File; - -/** - * Test for full export of data from Mongo and import them to JPA. Using export into encrypted ZIP and import from it - * - * @author Marek Posolda - */ -@Ignore -public class MongoToJPAExportImportTest { -} diff --git a/export-import/pom.xml b/export-import/pom.xml index 15d39f6a36..72468fad73 100755 --- a/export-import/pom.xml +++ b/export-import/pom.xml @@ -16,7 +16,6 @@ export-import-api - export-import-impl export-import-dir export-import-single-file export-import-zip diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheModelProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheModelProvider.java index d1167440bc..edc79e2a29 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheModelProvider.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheModelProvider.java @@ -266,9 +266,23 @@ public class DefaultCacheModelProvider implements CacheModelProvider { @Override public boolean removeRealm(String id) { cache.invalidateCachedRealmById(id); + + RealmModel realm = getDelegate().getRealm(id); + Set realmRoles = null; + if (realm != null) { + realmRoles = realm.getRoles(); + } + boolean didIt = getDelegate().removeRealm(id); realmInvalidations.add(id); + // TODO: Temporary workaround to invalidate cached realm roles + if (didIt && realmRoles != null) { + for (RoleModel role : realmRoles) { + roleInvalidations.add(role.getId()); + } + } + return didIt; } diff --git a/project-integrations/aerogear-ups/auth-server/pom.xml b/project-integrations/aerogear-ups/auth-server/pom.xml index 2ea0015550..a68969721b 100755 --- a/project-integrations/aerogear-ups/auth-server/pom.xml +++ b/project-integrations/aerogear-ups/auth-server/pom.xml @@ -167,7 +167,12 @@ org.keycloak - keycloak-export-import-impl + keycloak-export-import-dir + ${project.version} + + + org.keycloak + keycloak-export-import-single-file ${project.version} diff --git a/server/pom.xml b/server/pom.xml index a086cee071..7ab2cbb6a1 100755 --- a/server/pom.xml +++ b/server/pom.xml @@ -284,7 +284,17 @@ org.keycloak - keycloak-export-import-impl + keycloak-export-import-dir + ${project.version} + + + org.keycloak + keycloak-export-import-zip + ${project.version} + + + org.keycloak + keycloak-export-import-single-file ${project.version} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java index 20b5f09fef..a3e9df90ef 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java @@ -5,8 +5,6 @@ import java.util.Properties; import org.junit.Assert; import org.junit.ClassRule; -import org.junit.FixMethodOrder; -import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.rules.RuleChain; @@ -25,7 +23,6 @@ import org.keycloak.models.UserModel; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.rule.KeycloakRule; -import org.keycloak.testutils.KeycloakServer; /** * @author Marek Posolda @@ -39,7 +36,7 @@ public class ExportImportTest { @Override protected void before() throws Throwable { - if (System.getProperty("hibernate.connection.url") == null) { + if (System.getProperty("hibernate.connection.url") == null) { String baseExportImportDir = getExportImportTestDirectory(); File oldDBFile = new File(baseExportImportDir, "keycloakDB.h2.db"); @@ -64,6 +61,29 @@ public class ExportImportTest { } }; + // We want data to be persisted among server restarts + private static ExternalResource mongoRule = new ExternalResource() { + + private static final String MONGO_CLEAR_ON_STARTUP_PROP_NAME = "keycloak.model.mongo.clearOnStartup"; + private String previousMongoClearOnStartup; + + @Override + protected void before() throws Throwable { + previousMongoClearOnStartup = System.getProperty(MONGO_CLEAR_ON_STARTUP_PROP_NAME); + System.setProperty(MONGO_CLEAR_ON_STARTUP_PROP_NAME, "false"); + } + + @Override + protected void after() { + if (previousMongoClearOnStartup != null) { + System.setProperty(MONGO_CLEAR_ON_STARTUP_PROP_NAME, "false"); + } else { + System.getProperties().remove(MONGO_CLEAR_ON_STARTUP_PROP_NAME); + } + } + + }; + private static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() { @Override @@ -79,9 +99,10 @@ public class ExportImportTest { @ClassRule public static TestRule chain = RuleChain .outerRule(hibernateSetupRule) + .around(mongoRule) .around(keycloakRule); - //@Test + @Test public void testDirFullExportImport() throws Throwable { ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID); String targetDirPath = getExportImportTestDirectory() + File.separator + "dirExport"; @@ -95,7 +116,7 @@ public class ExportImportTest { Assert.assertEquals(4, new File(targetDirPath).listFiles().length); } - //@Test + @Test public void testDirRealmExportImport() throws Throwable { ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID); String targetDirPath = getExportImportTestDirectory() + File.separator + "dirRealmExport"; @@ -118,7 +139,7 @@ public class ExportImportTest { testFullExportImport(); } - //@Test + @Test public void testSingleFileRealmExportImport() throws Throwable { ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json"; @@ -127,7 +148,7 @@ public class ExportImportTest { testRealmExportImport(); } - //@Test + @Test public void testZipFullExportImport() throws Throwable { ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID); String zipFilePath = getExportImportTestDirectory() + File.separator + "export-full.zip"; @@ -139,7 +160,7 @@ public class ExportImportTest { testFullExportImport(); } - //@Test + @Test public void testZipRealmExportImport() throws Throwable { ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID); String zipFilePath = getExportImportTestDirectory() + File.separator + "export-realm.zip"; diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java index 8deadb718d..14a106cb48 100755 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateRealmsWorker.java @@ -6,6 +6,7 @@ import org.apache.log.Logger; import org.keycloak.models.ApplicationModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.managers.RealmManager; @@ -77,9 +78,9 @@ public class CreateRealmsWorker implements Worker { // Add required credentials if (createRequiredCredentials) { - realmManager.addRequiredCredential(realm, CredentialRepresentation.PASSWORD); - realmManager.addRequiredCredential(realm, CredentialRepresentation.TOTP); - realmManager.addRequiredCredential(realm, CredentialRepresentation.CLIENT_CERT); + RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.PASSWORD); + RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.TOTP); + RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.CLIENT_CERT); } log.info("Finished creation of realm " + realmName); diff --git a/testsuite/tools/pom.xml b/testsuite/tools/pom.xml index c630624317..d3b9867de2 100755 --- a/testsuite/tools/pom.xml +++ b/testsuite/tools/pom.xml @@ -283,7 +283,17 @@ org.keycloak - keycloak-export-import-impl + keycloak-export-import-dir + ${project.version} + + + org.keycloak + keycloak-export-import-zip + ${project.version} + + + org.keycloak + keycloak-export-import-single-file ${project.version} diff --git a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java index 4077f6b58e..793c8c3323 100644 --- a/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java +++ b/testsuite/tools/src/main/java/org/keycloak/test/tools/PerfTools.java @@ -1,6 +1,7 @@ package org.keycloak.test.tools; import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.ExportImportManager; import org.keycloak.exportimport.ExportProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; @@ -231,14 +232,7 @@ public class PerfTools { ExportImportConfig.setProvider("dir"); ExportImportConfig.setDir(dir); - Iterator providers = ProviderLoader.load(ExportProvider.class).iterator(); - - if (providers.hasNext()) { - ExportProvider exportImport = providers.next(); - exportImport.checkExportImport(sessionFactory); - } else { - throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); - } + new ExportImportManager().checkExportImport(sessionFactory); } public static class JobRepresentation {