diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java deleted file mode 100644 index e2c34011e3..0000000000 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.utils; - -import org.keycloak.models.RealmModel; -import org.keycloak.representations.idm.RealmRepresentation; - -/** - * Helper interface used just because RealmManager is in keycloak-services and not accessible for ImportUtils - * - * @author Marek Posolda - */ -public interface RealmImporter { - - RealmModel importRealm(RealmRepresentation rep); -} 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 a21d545136..e3e3557704 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 @@ -129,7 +129,7 @@ public class RepresentationToModel { return policy; } - public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm) { + public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) { convertDeprecatedSocialProviders(rep); convertDeprecatedApplications(session, rep); @@ -279,13 +279,6 @@ public class RepresentationToModel { } } - if (rep.getClients() != null) { - rep.getClients().forEach(clientRepresentation -> { - ClientModel client = newRealm.getClientByClientId(clientRepresentation.getClientId()); - importAuthorizationSettings(clientRepresentation, client, session); - }); - } - if (rep.getSmtpServer() != null) { newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer())); } @@ -331,6 +324,10 @@ public class RepresentationToModel { } } + if (!skipUserDependent) { + importRealmAuthorizationSettings(rep, newRealm, session); + } + if(rep.isInternationalizationEnabled() != null){ newRealm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); } @@ -1813,6 +1810,15 @@ public class RepresentationToModel { } } + public static void importRealmAuthorizationSettings(RealmRepresentation rep, RealmModel newRealm, KeycloakSession session) { + if (rep.getClients() != null) { + rep.getClients().forEach(clientRepresentation -> { + ClientModel client = newRealm.getClientByClientId(clientRepresentation.getClientId()); + importAuthorizationSettings(clientRepresentation, client, session); + }); + } + } + public static void importAuthorizationSettings(ClientRepresentation clientRepresentation, ClientModel client, KeycloakSession session) { if (Boolean.TRUE.equals(clientRepresentation.getAuthorizationServicesEnabled())) { AuthorizationProviderFactory authorizationFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); diff --git a/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java b/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java index e31cb8efc1..a2a0699c72 100755 --- a/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java +++ b/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java @@ -25,7 +25,9 @@ import org.keycloak.exportimport.util.ExportImportSessionTask; import org.keycloak.exportimport.util.ImportUtils; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.util.JsonSerialization; @@ -136,7 +138,7 @@ public class DirImportProvider implements ImportProvider { @Override public void runExportImportTask(KeycloakSession session) throws IOException { - boolean imported = ImportUtils.importRealm(session, realmRep, strategy); + boolean imported = ImportUtils.importRealm(session, realmRep, strategy, true); realmImported.set(imported); } @@ -165,6 +167,17 @@ public class DirImportProvider implements ImportProvider { }); } } + + // Import authorization last, as authzPolicies can require users already in DB + KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() { + + @Override + public void runExportImportTask(KeycloakSession session) throws IOException { + RealmModel realm = session.realms().getRealmByName(realmName); + RepresentationToModel.importRealmAuthorizationSettings(realmRep, realm, session); + } + + }); } @Override diff --git a/services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java b/services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java index 5b486c900b..a54af3fa90 100755 --- a/services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java +++ b/services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileImportProviderFactory.java @@ -34,6 +34,9 @@ public class SingleFileImportProviderFactory implements ImportProviderFactory { @Override public ImportProvider create(KeycloakSession session) { String fileName = ExportImportConfig.getFile(); + if (fileName == null) { + throw new IllegalArgumentException("Property " + ExportImportConfig.FILE + " needs to be provided!"); + } return new SingleFileImportProvider(new File(fileName)); } diff --git a/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java index 2357a65df9..0996c9cd54 100755 --- a/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java +++ b/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java @@ -28,7 +28,6 @@ import org.keycloak.exportimport.Strategy; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RealmProvider; -import org.keycloak.models.utils.RealmImporter; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -55,7 +54,7 @@ public class ImportUtils { // Import admin realm first for (RealmRepresentation realm : realms) { if (Config.getAdminRealm().equals(realm.getRealm())) { - if (importRealm(session, realm, strategy)) { + if (importRealm(session, realm, strategy, false)) { masterImported = true; } } @@ -63,7 +62,7 @@ public class ImportUtils { for (RealmRepresentation realm : realms) { if (!Config.getAdminRealm().equals(realm.getRealm())) { - importRealm(session, realm, strategy); + importRealm(session, realm, strategy, false); } } @@ -84,9 +83,10 @@ public class ImportUtils { * @param session * @param rep * @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) */ - public static boolean importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy) { + public static boolean importRealm(KeycloakSession session, RealmRepresentation rep, Strategy strategy, boolean skipUserDependent) { String realmName = rep.getRealm(); RealmProvider model = session.realms(); RealmModel realm = model.getRealmByName(realmName); @@ -110,7 +110,7 @@ public class ImportUtils { RealmManager realmManager = new RealmManager(session); realmManager.setContextPath(session.getContext().getContextPath()); - realmManager.importRealm(rep); + realmManager.importRealm(rep, skipUserDependent); if (System.getProperty(ExportImportConfig.ACTION) != null) { logger.infof("Realm '%s' imported", realmName); diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java index 07313d87ba..e1e0880fca 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java @@ -24,8 +24,6 @@ import org.keycloak.models.KeycloakContext; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; -import org.keycloak.models.utils.RealmImporter; -import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.util.LocaleHelper; 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 c6476234fe..cb80590b3b 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -37,7 +37,6 @@ import org.keycloak.models.session.UserSessionPersisterProvider; import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.models.utils.DefaultRequiredActions; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.models.utils.RealmImporter; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; @@ -61,7 +60,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class RealmManager implements RealmImporter { +public class RealmManager { protected KeycloakSession session; protected RealmProvider model; @@ -420,8 +419,15 @@ public class RealmManager implements RealmImporter { } } - @Override public RealmModel importRealm(RealmRepresentation rep) { + return importRealm(rep, false); + } + + + /** + * if "skipUserDependent" is true, then import of any models, which needs users already imported in DB, will be skipped. For example authorization + */ + public RealmModel importRealm(RealmRepresentation rep, boolean skipUserDependent) { String id = rep.getId(); if (id == null) { id = KeycloakModelUtils.generateId(); @@ -463,7 +469,7 @@ public class RealmManager implements RealmImporter { if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE)) setupOfflineTokens(realm); - RepresentationToModel.importRealm(session, rep, realm); + RepresentationToModel.importRealm(session, rep, realm, skipUserDependent); setupAdminConsoleLocaleMapper(realm); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java index 3c296a12cf..f923cf4ac2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java @@ -627,11 +627,12 @@ public class ExportImportUtil { assertPredicate(scopes, scopePredicates); List policies = authzResource.policies().policies(); - Assert.assertEquals(10, policies.size()); + Assert.assertEquals(11, policies.size()); List> policyPredicates = new ArrayList<>(); policyPredicates.add(policyRepresentation -> "Any Admin Policy".equals(policyRepresentation.getName())); policyPredicates.add(policyRepresentation -> "Any User Policy".equals(policyRepresentation.getName())); policyPredicates.add(policyRepresentation -> "Only Premium User Policy".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "wburke policy".equals(policyRepresentation.getName())); policyPredicates.add(policyRepresentation -> "All Users Policy".equals(policyRepresentation.getName())); policyPredicates.add(policyRepresentation -> "Premium Resource Permission".equals(policyRepresentation.getName())); policyPredicates.add(policyRepresentation -> "Administrative Resource Permission".equals(policyRepresentation.getName())); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json index 6cd3ef6256..b540ab1bdb 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json @@ -291,6 +291,15 @@ "roles": "[{\"id\":\"customer-user-premium\"}]" } }, + { + "name": "wburke policy", + "description": "Defines that only wburke can do something", + "type": "user", + "logic": "POSITIVE", + "config": { + "users" : "[\"wburke\"]" + } + }, { "name": "All Users Policy", "description": "Defines that all users can do something",