diff --git a/model/storage-private/src/main/java/org/keycloak/storage/datastore/DefaultExportImportManager.java b/model/storage-private/src/main/java/org/keycloak/storage/datastore/DefaultExportImportManager.java index 83c558b7b76..41bf70a2f66 100644 --- a/model/storage-private/src/main/java/org/keycloak/storage/datastore/DefaultExportImportManager.java +++ b/model/storage-private/src/main/java/org/keycloak/storage/datastore/DefaultExportImportManager.java @@ -191,7 +191,7 @@ public class DefaultExportImportManager implements ExportImportManager { } @Override - public void importRealm(RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) { + public void importRealm(RealmRepresentation rep, RealmModel newRealm, Runnable userImport) { convertDeprecatedSocialProviders(rep); convertDeprecatedApplications(session, rep); convertDeprecatedClientTemplates(rep); @@ -481,7 +481,8 @@ public class DefaultExportImportManager implements ExportImportManager { }, true); } - if (!skipUserDependent) { + if (userImport != null) { + userImport.run(); importRealmAuthorizationSettings(rep, newRealm, session); } diff --git a/model/storage-services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java b/model/storage-services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java index 1890974dd6b..ea458ae197d 100755 --- a/model/storage-services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java +++ b/model/storage-services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java @@ -40,9 +40,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; -import org.keycloak.services.managers.RealmManager; import org.keycloak.storage.datastore.DefaultExportImportManager; /** @@ -148,37 +146,18 @@ public class DirImportProvider extends AbstractFileBasedImportProvider { if (!realmRep.getRealm().equals(realmName)) { throw new IllegalStateException(String.format("File name / realm name mismatch. %s, contains realm %s. File name should be %s", realmFile.getName(), realmRep.getRealm(), realmRep.getRealm() + "-realm.json")); } - final AtomicBoolean realmImported = new AtomicBoolean(); new ExportImportSessionTask() { @Override public void runExportImportTask(KeycloakSession session) { - boolean imported = ImportUtils.importRealm(session, realmRep, strategy, true); - realmImported.set(imported); + ImportUtils.importRealm(session, realmRep, strategy, () -> { + importUsers(realmName, userFiles, false); + importUsers(realmName, federatedUserFiles, true); + }); } }.runTask(factory); - - if (realmImported.get()) { - // Import users - importUsers(realmName, userFiles, false); - importUsers(realmName, federatedUserFiles, true); - } - - if (realmImported.get()) { - // Import authorization and initialize service accounts last, as they require users already in DB - new ExportImportSessionTask() { - - @Override - public void runExportImportTask(KeycloakSession session) { - session.getContext().setRealm(session.realms().getRealmByName(realmName)); - RealmManager realmManager = new RealmManager(session); - realmManager.setupClientServiceAccountsAndAuthorizationOnImport(realmRep, false); - } - - }.runTask(factory); - } } private void importUsers(final String realmName, File[] userFiles, boolean federated) { diff --git a/model/storage-services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/model/storage-services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java index 320f0859f09..7a4a201b588 100755 --- a/model/storage-services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java +++ b/model/storage-services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java @@ -56,7 +56,7 @@ public class ImportUtils { // Import admin realm first for (RealmRepresentation realm : realms) { if (Config.getAdminRealm().equals(realm.getRealm())) { - if (importRealm(session, realm, strategy, false)) { + if (importRealm(session, realm, strategy, () -> {})) { masterImported = true; } } @@ -64,7 +64,7 @@ public class ImportUtils { for (RealmRepresentation realm : realms) { if (!Config.getAdminRealm().equals(realm.getRealm())) { - importRealm(session, realm, strategy, false); + importRealm(session, realm, strategy, () -> {}); } } @@ -87,8 +87,23 @@ public class ImportUtils { * @param strategy specifies whether to overwrite or ignore existing realm or user entries * @param skipUserDependent If true, then import of any models, which needs users already imported in DB, will be skipped. For example authorization * @return newly imported realm (or existing realm if ignoreExisting is true and realm of this name already exists) + * @deprecated */ + @Deprecated public static boolean importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy, boolean skipUserDependent) { + return importRealm(session, rep, strategy, skipUserDependent ? null : () -> {}); + } + + /** + * 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 + * @param userImport use null to indicate additional users will not be imported + * @return newly imported realm (or existing realm if ignoreExisting is true and realm of this name already exists) + */ + public static boolean importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy, Runnable userImport) { String realmName = rep.getRealm(); RealmProvider model = session.realms(); RealmModel realm = model.getRealmByName(realmName); @@ -111,7 +126,7 @@ public class ImportUtils { } RealmManager realmManager = new RealmManager(session); - realmManager.importRealm(rep, skipUserDependent); + realmManager.importRealm(rep, userImport); if (System.getProperty(ExportImportConfig.ACTION) != null) { logger.infof("Realm '%s' imported", realmName); diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 43bc745febb..61f49c5f02c 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -140,8 +140,8 @@ public class RepresentationToModel { public static final String OIDC = "openid-connect"; - public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) { - session.getProvider(DatastoreProvider.class).getExportImportManager().importRealm(rep, newRealm, skipUserDependent); + public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, Runnable userImport) { + session.getProvider(DatastoreProvider.class).getExportImportManager().importRealm(rep, newRealm, userImport); } public static void importRoles(RolesRepresentation realmRoles, RealmModel realm) { @@ -1180,7 +1180,7 @@ public class RepresentationToModel { if (applyPolicies != null && !applyPolicies.isEmpty()) { PolicyStore policyStore = storeFactory.getPolicyStore(); try { - List policies = (List) JsonSerialization.readValue(applyPolicies, List.class); + List policies = JsonSerialization.readValue(applyPolicies, List.class); Set policyIds = new HashSet<>(); for (String policyName : policies) { diff --git a/server-spi-private/src/main/java/org/keycloak/storage/ExportImportManager.java b/server-spi-private/src/main/java/org/keycloak/storage/ExportImportManager.java index f9dd452eb05..6b628462fef 100644 --- a/server-spi-private/src/main/java/org/keycloak/storage/ExportImportManager.java +++ b/server-spi-private/src/main/java/org/keycloak/storage/ExportImportManager.java @@ -33,7 +33,7 @@ import java.io.InputStream; * @author Alexander Schwartz */ public interface ExportImportManager { - void importRealm(RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent); + void importRealm(RealmRepresentation rep, RealmModel newRealm, Runnable userImport); PartialImportResults partialImportRealm(RealmModel realm, InputStream requestBody); 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 1ddd382f212..8b69bd8198d 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -527,14 +527,24 @@ public class RealmManager { } public RealmModel importRealm(RealmRepresentation rep) { - return importRealm(rep, false); + return importRealm(rep, () -> {}); } /** * if "skipUserDependent" is true, then import of any models, which needs users already imported in DB, will be skipped. For example authorization + * @deprecated use {@link #importRealm(RealmRepresentation, Runnable)} */ + @Deprecated public RealmModel importRealm(RealmRepresentation rep, boolean skipUserDependent) { + return importRealm(rep, skipUserDependent ? null : () -> {}); + } + + /** + * @param userImport if null, then import of any models, which needs users already imported in DB, will be skipped. For example authorization + */ + public RealmModel importRealm(RealmRepresentation rep, Runnable userImport) { + boolean skipUserDependent = userImport == null; String id = rep.getId(); if (id == null || id.trim().isEmpty()) { id = KeycloakModelUtils.generateId(); @@ -612,7 +622,7 @@ public class RealmManager { createDefaultClientScopes(realm); } - RepresentationToModel.importRealm(session, rep, realm, skipUserDependent); + RepresentationToModel.importRealm(session, rep, realm, userImport); setupClientServiceAccountsAndAuthorizationOnImport(rep, skipUserDependent); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/exportimport/OrganizationExportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/exportimport/OrganizationExportTest.java index 232d4498a18..170cb4079b6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/exportimport/OrganizationExportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/exportimport/OrganizationExportTest.java @@ -43,6 +43,8 @@ import org.keycloak.admin.client.resource.OrganizationResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.exportimport.ExportImportConfig; +import org.keycloak.exportimport.dir.DirExportProviderFactory; +import org.keycloak.exportimport.dir.DirImportProviderFactory; import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory; import org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory; import org.keycloak.models.OrganizationModel; @@ -113,8 +115,21 @@ public class OrganizationExportTest extends AbstractOrganizationTest { } } - RealmRepresentation importedRealm = exportRemoveImportRealm(); + RealmRepresentation importedSingleFileRealm = exportRemoveImportRealm(true); + validateImported(expectedOrganizations, expectedManagedMembers, expectedUnmanagedMembers, importedSingleFileRealm); + + testRealm().logoutAll(); + providerRealm.logoutAll(); + + RealmRepresentation importedDirRealm = exportRemoveImportRealm(false); + + validateImported(expectedOrganizations, expectedManagedMembers, expectedUnmanagedMembers, importedDirRealm); + } + + private void validateImported(List expectedOrganizations, + Map> expectedManagedMembers, Map> expectedUnmanagedMembers, + RealmRepresentation importedRealm) { assertTrue(importedRealm.isOrganizationsEnabled()); List organizations = testRealm().organizations().list(-1, -1); @@ -175,31 +190,44 @@ public class OrganizationExportTest extends AbstractOrganizationTest { List orgs = testRealm().organizations().list(-1, -1); assertEquals(1, orgs.size()); - RealmRepresentation importedRealm = exportRemoveImportRealm(); + RealmRepresentation importedSingleFileRealm = exportRemoveImportRealm(true); - assertTrue(importedRealm.isOrganizationsEnabled()); + assertTrue(importedSingleFileRealm.isOrganizationsEnabled()); orgs = testRealm().organizations().list(-1, -1); assertEquals(1, orgs.size()); assertEquals("acme", orgs.get(0).getName()); } - private RealmRepresentation exportRemoveImportRealm() { - //export + private RealmRepresentation exportRemoveImportRealm(boolean file) { TestingExportImportResource exportImport = testingClient.testing().exportImport(); - exportImport.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); + String fileOrDir; + + //export + if (file) { + exportImport.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); + fileOrDir = exportImport.getExportImportTestDirectory() + File.separator + "org-export.json"; + exportImport.setFile(fileOrDir); + } else { + exportImport.setProvider(DirExportProviderFactory.PROVIDER_ID); + fileOrDir = exportImport.getExportImportTestDirectory(); + exportImport.setDir(fileOrDir); + } exportImport.setAction(ExportImportConfig.ACTION_EXPORT); exportImport.setRealmName(testRealm().toRepresentation().getRealm()); - String targetFilePath = exportImport.getExportImportTestDirectory() + File.separator + "org-export.json"; - exportImport.setFile(targetFilePath); exportImport.runExport(); // remove the realm and import it back testRealm().remove(); exportImport = testingClient.testing().exportImport(); - exportImport.setProvider(SingleFileImportProviderFactory.PROVIDER_ID); + if (file) { + exportImport.setProvider(SingleFileImportProviderFactory.PROVIDER_ID); + exportImport.setFile(fileOrDir); + } else { + exportImport.setProvider(DirImportProviderFactory.PROVIDER_ID); + exportImport.setDir(fileOrDir); + } exportImport.setAction(ExportImportConfig.ACTION_IMPORT); - exportImport.setFile(targetFilePath); exportImport.runImport(); getCleanup().addCleanup(() -> { testRealm().remove();