diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo12_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo12_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo12_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo12_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo14_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo14_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo14_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo14_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo18_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo18_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo18_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo18_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java similarity index 96% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java index b98f25b460..bdb0cfed8b 100755 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java +++ b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java @@ -27,6 +27,7 @@ import org.keycloak.provider.ProviderFactory; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.UserStorageUtil; import javax.naming.directory.SearchControls; import java.util.List; @@ -52,7 +53,7 @@ public class MigrateTo1_3_0 implements Migration { } private void migrateLDAPProviders(KeycloakSession session, RealmModel realm) { - realm.getUserStorageProvidersStream().forEachOrdered(fedProvider -> { + UserStorageUtil.getUserStorageProvidersStream(realm).forEachOrdered(fedProvider -> { if (fedProvider.getProviderId().equals(LDAPConstants.LDAP_PROVIDER)) { fedProvider = new UserStorageProviderModel(fedProvider); // copy don't want to muck with cache MultivaluedHashMap config = fedProvider.getConfig(); diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java similarity index 97% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java index ac131753d5..95cb9c26d1 100755 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java +++ b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java @@ -29,6 +29,7 @@ import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.models.utils.DefaultRequiredActions; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.storage.UserStorageUtil; import java.util.Arrays; import java.util.List; @@ -67,7 +68,7 @@ public class MigrateTo1_4_0 implements Migration { private void migrateLDAPMappers(KeycloakSession session, RealmModel realm) { List mandatoryInLdap = Arrays.asList("username", "username-cn", "first name", "last name"); - realm.getUserStorageProvidersStream() + UserStorageUtil.getUserStorageProvidersStream(realm) .filter(providerModel -> Objects.equals(providerModel.getProviderId(), LDAPConstants.LDAP_PROVIDER)) .forEachOrdered(providerModel -> realm.getComponentsStream(providerModel.getId()) .filter(mapper -> mandatoryInLdap.contains(mapper.getName())) diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java similarity index 96% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java index 231e57f84c..4f0d5c0f6d 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java +++ b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java @@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.UserStorageUtil; import java.util.Objects; @@ -50,7 +51,7 @@ public class MigrateTo1_8_0 implements Migration { } protected void migrateRealm(RealmModel realm) { - realm.getUserStorageProvidersStream() + UserStorageUtil.getUserStorageProvidersStream(realm) .filter(fedProvider -> Objects.equals(fedProvider.getProviderId(), LDAPConstants.LDAP_PROVIDER)) .filter(this::isActiveDirectory) .filter(fedProvider -> Objects.isNull(getMapperByName(realm, fedProvider, "MSAD account controls"))) diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_1_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_1.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_1.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_1.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_1.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_2.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_2.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_2.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_4_2.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_2_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_2_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_2_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_2_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_2.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_2.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_2.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo8_0_2.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_0.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_0.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_0.java diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_4.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_4.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_4.java rename to model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo9_0_4.java diff --git a/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyDatastoreProvider.java b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyDatastoreProvider.java index 096225eaad..66857ff158 100644 --- a/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyDatastoreProvider.java +++ b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyDatastoreProvider.java @@ -12,13 +12,14 @@ import org.keycloak.models.cache.UserCache; import org.keycloak.storage.ClientScopeStorageManager; import org.keycloak.storage.ClientStorageManager; import org.keycloak.storage.DatastoreProvider; +import org.keycloak.storage.ExportImportManager; import org.keycloak.storage.GroupStorageManager; import org.keycloak.storage.LegacyStoreManagers; +import org.keycloak.storage.MigrationManager; import org.keycloak.storage.RoleStorageManager; import org.keycloak.storage.UserStorageManager; public class LegacyDatastoreProvider implements DatastoreProvider, LegacyStoreManagers { - private final LegacyDatastoreProviderFactory factory; private final KeycloakSession session; @@ -184,4 +185,15 @@ public class LegacyDatastoreProvider implements DatastoreProvider, LegacyStoreMa } return userProvider; } + + @Override + public ExportImportManager getExportImportManager() { + return new LegacyExportImportManager(session); + } + + @Override + public MigrationManager getMigrationManager() { + return new LegacyMigrationManager(session); + } + } diff --git a/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyExportImportManager.java b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyExportImportManager.java new file mode 100644 index 0000000000..1473793a29 --- /dev/null +++ b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyExportImportManager.java @@ -0,0 +1,1296 @@ +package org.keycloak.storage.datastore; + +import org.jboss.logging.Logger; +import org.keycloak.common.enums.SslRequired; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.component.ComponentModel; +import org.keycloak.keys.KeyProvider; +import org.keycloak.migration.MigrationProvider; +import org.keycloak.migration.migrators.MigrateTo8_0_0; +import org.keycloak.migration.migrators.MigrationUtils; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticationFlowModel; +import org.keycloak.models.AuthenticatorConfigModel; +import org.keycloak.models.BrowserSecurityHeaders; +import org.keycloak.models.CibaConfig; +import org.keycloak.models.ClaimMask; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientScopeModel; +import org.keycloak.models.Constants; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LDAPConstants; +import org.keycloak.models.OAuth2DeviceConfig; +import org.keycloak.models.OTPPolicy; +import org.keycloak.models.ParConfig; +import org.keycloak.models.PasswordPolicy; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RequiredActionProviderModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.ScopeContainerModel; +import org.keycloak.models.WebAuthnPolicy; +import org.keycloak.models.utils.ComponentUtil; +import org.keycloak.models.utils.DefaultAuthenticationFlows; +import org.keycloak.models.utils.DefaultKeyProviders; +import org.keycloak.models.utils.DefaultRequiredActions; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.representations.idm.ApplicationRepresentation; +import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation; +import org.keycloak.representations.idm.AuthenticationFlowRepresentation; +import org.keycloak.representations.idm.AuthenticatorConfigRepresentation; +import org.keycloak.representations.idm.ClaimRepresentation; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientScopeRepresentation; +import org.keycloak.representations.idm.ClientTemplateRepresentation; +import org.keycloak.representations.idm.ComponentExportRepresentation; +import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.OAuthClientRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RequiredActionProviderRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.ScopeMappingRepresentation; +import org.keycloak.representations.idm.UserFederationMapperRepresentation; +import org.keycloak.representations.idm.UserFederationProviderRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.storage.ExportImportManager; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.userprofile.UserProfileProvider; +import org.keycloak.validation.ValidationUtil; + +import java.util.Arrays; +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 java.util.function.Function; +import java.util.stream.Collectors; + +import static org.keycloak.models.utils.RepresentationToModel.createUser; +import static org.keycloak.models.utils.RepresentationToModel.importFederatedUser; +import static org.keycloak.models.utils.RepresentationToModel.importGroup; +import static org.keycloak.models.utils.RepresentationToModel.importRoles; + +/** + * This wraps the functionality about export/import for legacy storage. This will be handled differently for the new map storage, + * therefore, it has been extracted. + * + * @author Alexander Schwartz + */ +public class LegacyExportImportManager implements ExportImportManager { + private final KeycloakSession session; + private static final Logger logger = Logger.getLogger(LegacyExportImportManager.class); + + public LegacyExportImportManager(KeycloakSession session) { + this.session = session; + } + + @Override + public void importRealm(RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) { + convertDeprecatedSocialProviders(rep); + convertDeprecatedApplications(session, rep); + convertDeprecatedClientTemplates(rep); + + newRealm.setName(rep.getRealm()); + if (rep.getDisplayName() != null) newRealm.setDisplayName(rep.getDisplayName()); + if (rep.getDisplayNameHtml() != null) newRealm.setDisplayNameHtml(rep.getDisplayNameHtml()); + if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled()); + if (rep.isUserManagedAccessAllowed() != null) newRealm.setUserManagedAccessAllowed(rep.isUserManagedAccessAllowed()); + if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected()); + if (rep.isPermanentLockout() != null) newRealm.setPermanentLockout(rep.isPermanentLockout()); + 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.isEventsEnabled() != null) newRealm.setEventsEnabled(rep.isEventsEnabled()); + if (rep.getEnabledEventTypes() != null) + newRealm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes())); + if (rep.getEventsExpiration() != null) newRealm.setEventsExpiration(rep.getEventsExpiration()); + if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<>(rep.getEventsListeners())); + if (rep.isAdminEventsEnabled() != null) newRealm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); + if (rep.isAdminEventsDetailsEnabled() != null) + newRealm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); + + if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore()); + + if (rep.getDefaultSignatureAlgorithm() != null) newRealm.setDefaultSignatureAlgorithm(rep.getDefaultSignatureAlgorithm()); + else newRealm.setDefaultSignatureAlgorithm(Constants.DEFAULT_SIGNATURE_ALGORITHM); + + if (rep.getRevokeRefreshToken() != null) newRealm.setRevokeRefreshToken(rep.getRevokeRefreshToken()); + else newRealm.setRevokeRefreshToken(false); + + if (rep.getRefreshTokenMaxReuse() != null) newRealm.setRefreshTokenMaxReuse(rep.getRefreshTokenMaxReuse()); + else newRealm.setRefreshTokenMaxReuse(0); + + if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); + else newRealm.setAccessTokenLifespan(300); + + if (rep.getAccessTokenLifespanForImplicitFlow() != null) + newRealm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow()); + else + newRealm.setAccessTokenLifespanForImplicitFlow(Constants.DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT); + + if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); + else newRealm.setSsoSessionIdleTimeout(1800); + if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); + else newRealm.setSsoSessionMaxLifespan(36000); + if (rep.getSsoSessionMaxLifespanRememberMe() != null) newRealm.setSsoSessionMaxLifespanRememberMe(rep.getSsoSessionMaxLifespanRememberMe()); + if (rep.getSsoSessionIdleTimeoutRememberMe() != null) newRealm.setSsoSessionIdleTimeoutRememberMe(rep.getSsoSessionIdleTimeoutRememberMe()); + if (rep.getOfflineSessionIdleTimeout() != null) + newRealm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout()); + else newRealm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT); + + // KEYCLOAK-7688 Offline Session Max for Offline Token + if (rep.getOfflineSessionMaxLifespanEnabled() != null) newRealm.setOfflineSessionMaxLifespanEnabled(rep.getOfflineSessionMaxLifespanEnabled()); + else newRealm.setOfflineSessionMaxLifespanEnabled(false); + + if (rep.getOfflineSessionMaxLifespan() != null) + newRealm.setOfflineSessionMaxLifespan(rep.getOfflineSessionMaxLifespan()); + else newRealm.setOfflineSessionMaxLifespan(Constants.DEFAULT_OFFLINE_SESSION_MAX_LIFESPAN); + + if (rep.getClientSessionIdleTimeout() != null) + newRealm.setClientSessionIdleTimeout(rep.getClientSessionIdleTimeout()); + if (rep.getClientSessionMaxLifespan() != null) + newRealm.setClientSessionMaxLifespan(rep.getClientSessionMaxLifespan()); + + if (rep.getClientOfflineSessionIdleTimeout() != null) + newRealm.setClientOfflineSessionIdleTimeout(rep.getClientOfflineSessionIdleTimeout()); + if (rep.getClientOfflineSessionMaxLifespan() != null) + newRealm.setClientOfflineSessionMaxLifespan(rep.getClientOfflineSessionMaxLifespan()); + + 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.getAccessCodeLifespanLogin() != null) + newRealm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin()); + else newRealm.setAccessCodeLifespanLogin(1800); + + if (rep.getActionTokenGeneratedByAdminLifespan() != null) + newRealm.setActionTokenGeneratedByAdminLifespan(rep.getActionTokenGeneratedByAdminLifespan()); + else newRealm.setActionTokenGeneratedByAdminLifespan(12 * 60 * 60); + + if (rep.getActionTokenGeneratedByUserLifespan() != null) + newRealm.setActionTokenGeneratedByUserLifespan(rep.getActionTokenGeneratedByUserLifespan()); + else newRealm.setActionTokenGeneratedByUserLifespan(newRealm.getAccessCodeLifespanUserAction()); + + // OAuth 2.0 Device Authorization Grant + OAuth2DeviceConfig deviceConfig = newRealm.getOAuth2DeviceConfig(); + + deviceConfig.setOAuth2DeviceCodeLifespan(rep.getOAuth2DeviceCodeLifespan()); + deviceConfig.setOAuth2DevicePollingInterval(rep.getOAuth2DevicePollingInterval()); + + if (rep.getSslRequired() != null) + newRealm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase())); + if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); + if (rep.isRegistrationEmailAsUsername() != null) + newRealm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername()); + if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe()); + if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail()); + if (rep.isLoginWithEmailAllowed() != null) newRealm.setLoginWithEmailAllowed(rep.isLoginWithEmailAllowed()); + if (rep.isDuplicateEmailsAllowed() != null) newRealm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed()); + if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); + if (rep.isEditUsernameAllowed() != null) newRealm.setEditUsernameAllowed(rep.isEditUsernameAllowed()); + 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()); + + // todo remove this stuff as its all deprecated + if (rep.getRequiredCredentials() != null) { + for (String requiredCred : rep.getRequiredCredentials()) { + newRealm.addRequiredCredential(requiredCred); + } + } else { + newRealm.addRequiredCredential(CredentialRepresentation.PASSWORD); + } + + if (rep.getPasswordPolicy() != null) + newRealm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy())); + if (rep.getOtpPolicyType() != null) newRealm.setOTPPolicy(toPolicy(rep)); + else newRealm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY); + + WebAuthnPolicy webAuthnPolicy = getWebAuthnPolicyTwoFactor(rep); + newRealm.setWebAuthnPolicy(webAuthnPolicy); + + webAuthnPolicy = getWebAuthnPolicyPasswordless(rep); + newRealm.setWebAuthnPolicyPasswordless(webAuthnPolicy); + + updateCibaSettings(rep, newRealm); + + updateParSettings(rep, newRealm); + + Map mappedFlows = importAuthenticationFlows(newRealm, rep); + if (rep.getRequiredActions() != null) { + for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) { + RequiredActionProviderModel model = toModel(action); + + MigrationUtils.updateOTPRequiredAction(model); + + newRealm.addRequiredActionProvider(model); + } + DefaultRequiredActions.addDeleteAccountAction(newRealm); + } else { + DefaultRequiredActions.addActions(newRealm); + } + + importIdentityProviders(rep, newRealm, session); + importIdentityProviderMappers(rep, newRealm); + + Map clientScopes = new HashMap<>(); + if (rep.getClientScopes() != null) { + clientScopes = createClientScopes(session, rep.getClientScopes(), newRealm); + } + if (rep.getDefaultDefaultClientScopes() != null) { + for (String clientScopeName : rep.getDefaultDefaultClientScopes()) { + ClientScopeModel clientScope = clientScopes.get(clientScopeName); + if (clientScope != null) { + newRealm.addDefaultClientScope(clientScope, true); + } else { + logger.warnf("Referenced client scope '%s' doesn't exist", clientScopeName); + } + } + } + if (rep.getDefaultOptionalClientScopes() != null) { + for (String clientScopeName : rep.getDefaultOptionalClientScopes()) { + ClientScopeModel clientScope = clientScopes.get(clientScopeName); + if (clientScope != null) { + newRealm.addDefaultClientScope(clientScope, false); + } else { + logger.warnf("Referenced client scope '%s' doesn't exist", clientScopeName); + } + } + } + + Map createdClients = new HashMap<>(); + if (rep.getClients() != null) { + createdClients = createClients(session, rep, newRealm, mappedFlows); + } + + importRoles(rep.getRoles(), newRealm); + convertDeprecatedDefaultRoles(rep, newRealm); + + // Now that all possible roles and clients are created, create scope mappings + + if (rep.getClientScopeMappings() != null) { + + for (Map.Entry> entry : rep.getClientScopeMappings().entrySet()) { + ClientModel app = createdClients.computeIfAbsent(entry.getKey(), k -> newRealm.getClientByClientId(entry.getKey())); + if (app == null) { + throw new RuntimeException("Unable to find client role mappings for client: " + entry.getKey()); + } + createClientScopeMappings(newRealm, app, entry.getValue()); + } + } + + if (rep.getScopeMappings() != null) { + Map roleModelMap = newRealm.getRolesStream().collect(Collectors.toMap(RoleModel::getId, Function.identity())); + + for (ScopeMappingRepresentation scope : rep.getScopeMappings()) { + ScopeContainerModel scopeContainer = getScopeContainerHavingScope(newRealm, scope); + for (String roleString : scope.getRoles()) { + final String roleStringTrimmed = roleString.trim(); + RoleModel role = roleModelMap.computeIfAbsent(roleStringTrimmed, k -> newRealm.getRole(roleStringTrimmed)); + if (role == null) { + role = newRealm.addRole(roleString); + roleModelMap.put(role.getId(), role); + } + scopeContainer.addScopeMapping(role); + } + } + } + + if (rep.getSmtpServer() != null) { + newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer())); + } + + if (rep.getBrowserSecurityHeaders() != null) { + newRealm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders()); + } else { + newRealm.setBrowserSecurityHeaders(BrowserSecurityHeaders.realmDefaultHeaders); + } + + if (rep.getComponents() != null) { + MultivaluedHashMap components = rep.getComponents(); + String parentId = newRealm.getId(); + importComponents(newRealm, components, parentId); + } + + importUserFederationProvidersAndMappers(session, rep, newRealm); + + if (rep.getGroups() != null) { + importGroups(newRealm, rep); + if (rep.getDefaultGroups() != null) { + for (String path : rep.getDefaultGroups()) { + GroupModel found = KeycloakModelUtils.findGroupByPath(newRealm, path); + if (found == null) throw new RuntimeException("default group in realm rep doesn't exist: " + path); + newRealm.addDefaultGroup(found); + } + } + } + + + // create users and their role mappings and social mappings + + if (rep.getUsers() != null) { + for (UserRepresentation userRep : rep.getUsers()) { + createUser(session, newRealm, userRep); + } + } + + if (rep.getFederatedUsers() != null) { + for (UserRepresentation userRep : rep.getFederatedUsers()) { + importFederatedUser(session, newRealm, userRep); + } + } + + if (!skipUserDependent) { + importRealmAuthorizationSettings(rep, newRealm, session); + } + + if (rep.isInternationalizationEnabled() != null) { + newRealm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); + } + if (rep.getSupportedLocales() != null) { + newRealm.setSupportedLocales(new HashSet(rep.getSupportedLocales())); + } + if (rep.getDefaultLocale() != null) { + newRealm.setDefaultLocale(rep.getDefaultLocale()); + } + + // import attributes + + if (rep.getAttributes() != null) { + for (Map.Entry attr : rep.getAttributes().entrySet()) { + newRealm.setAttribute(attr.getKey(), attr.getValue()); + } + } + + if (newRealm.getComponentsStream(newRealm.getId(), KeyProvider.class.getName()).count() == 0) { + if (rep.getPrivateKey() != null) { + DefaultKeyProviders.createProviders(newRealm, rep.getPrivateKey(), rep.getCertificate()); + } else { + DefaultKeyProviders.createProviders(newRealm); + } + } + } + + private static RoleModel getOrAddRealmRole(RealmModel realm, String name) { + RoleModel role = realm.getRole(name); + if (role == null) { + role = realm.addRole(name); + } + return role; + } + + private static void convertDeprecatedDefaultRoles(RealmRepresentation rep, RealmModel newRealm) { + if (rep.getDefaultRole() == null) { + + // Setup realm default roles + if (rep.getDefaultRoles() != null) { + rep.getDefaultRoles().stream() + .map(String::trim) + .map(name -> getOrAddRealmRole(newRealm, name)) + .forEach(role -> newRealm.getDefaultRole().addCompositeRole(role)); + } + + // Setup client default roles + if (rep.getClients() != null) { + for (ClientRepresentation clientRep : rep.getClients()) { + if (clientRep.getDefaultRoles() != null) { + Arrays.stream(clientRep.getDefaultRoles()) + .map(String::trim) + .map(name -> getOrAddClientRole(newRealm.getClientById(clientRep.getId()), name)) + .forEach(role -> newRealm.getDefaultRole().addCompositeRole(role)); + } + } + } + } + } + + private static RoleModel getOrAddClientRole(ClientModel client, String name) { + RoleModel role = client.getRole(name); + if (role == null) { + role = client.addRole(name); + } + return role; + } + + private static Map createClients(KeycloakSession session, RealmRepresentation rep, RealmModel realm, Map mappedFlows) { + Map appMap = new HashMap(); + for (ClientRepresentation resourceRep : rep.getClients()) { + ClientModel app = RepresentationToModel.createClient(session, realm, resourceRep, mappedFlows); + appMap.put(app.getClientId(), app); + + ValidationUtil.validateClient(session, app, false, r -> { + throw new RuntimeException("Invalid client " + app.getClientId() + ": " + r.getAllErrorsAsString()); + }); + } + return appMap; + } + + private static Map createClientScopes(KeycloakSession session, List clientScopes, RealmModel realm) { + Map appMap = new HashMap<>(); + for (ClientScopeRepresentation resourceRep : clientScopes) { + ClientScopeModel app = RepresentationToModel.createClientScope(session, realm, resourceRep); + appMap.put(app.getName(), app); + } + return appMap; + } + + private static void importIdentityProviders(RealmRepresentation rep, RealmModel newRealm, KeycloakSession session) { + if (rep.getIdentityProviders() != null) { + for (IdentityProviderRepresentation representation : rep.getIdentityProviders()) { + newRealm.addIdentityProvider(RepresentationToModel.toModel(newRealm, representation, session)); + } + } + } + + private static void importIdentityProviderMappers(RealmRepresentation rep, RealmModel newRealm) { + if (rep.getIdentityProviderMappers() != null) { + for (IdentityProviderMapperRepresentation representation : rep.getIdentityProviderMappers()) { + newRealm.addIdentityProviderMapper(RepresentationToModel.toModel(representation)); + } + } + } + + public static long getClaimsMask(ClaimRepresentation rep) { + long mask = ClaimMask.ALL; + + 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; + } + return mask; + } + + public static void createClientScopeMappings(RealmModel realm, ClientModel clientModel, List mappings) { + for (ScopeMappingRepresentation mapping : mappings) { + ScopeContainerModel scopeContainer = getScopeContainerHavingScope(realm, mapping); + + for (String roleString : mapping.getRoles()) { + RoleModel role = clientModel.getRole(roleString.trim()); + if (role == null) { + role = clientModel.addRole(roleString.trim()); + } + scopeContainer.addScopeMapping(role); + } + } + } + + private static ScopeContainerModel getScopeContainerHavingScope(RealmModel realm, ScopeMappingRepresentation scope) { + if (scope.getClient() != null) { + ClientModel client = realm.getClientByClientId(scope.getClient()); + if (client == null) { + throw new RuntimeException("Unknown client specification in scope mappings: " + scope.getClient()); + } + return client; + } else if (scope.getClientScope() != null) { + ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scope.getClientScope()); + if (clientScope == null) { + throw new RuntimeException("Unknown clientScope specification in scope mappings: " + scope.getClientScope()); + } + return clientScope; + } else if (scope.getClientTemplate() != null) { // Backwards compatibility + String templateName = KeycloakModelUtils.convertClientScopeName(scope.getClientTemplate()); + ClientScopeModel clientTemplate = KeycloakModelUtils.getClientScopeByName(realm, templateName); + if (clientTemplate == null) { + throw new RuntimeException("Unknown clientScope specification in scope mappings: " + templateName); + } + return clientTemplate; + } else { + throw new RuntimeException("Either client or clientScope needs to be specified in scope mappings"); + } + } + + + public static void renameRealm(RealmModel realm, String name) { + if (name.equals(realm.getName())) return; + + String oldName = realm.getName(); + + ClientModel masterApp = realm.getMasterAdminClient(); + masterApp.setClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(name)); + realm.setName(name); + + ClientModel adminClient = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID); + if (adminClient != null) { + if (adminClient.getBaseUrl() != null) { + adminClient.setBaseUrl(adminClient.getBaseUrl().replace("/admin/" + oldName + "/", "/admin/" + name + "/")); + } + Set adminRedirectUris = new HashSet<>(); + for (String r : adminClient.getRedirectUris()) { + adminRedirectUris.add(replace(r, "/admin/" + oldName + "/", "/admin/" + name + "/")); + } + adminClient.setRedirectUris(adminRedirectUris); + } + + ClientModel accountClient = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID); + if (accountClient != null) { + if (accountClient.getBaseUrl() != null) { + accountClient.setBaseUrl(accountClient.getBaseUrl().replace("/realms/" + oldName + "/", "/realms/" + name + "/")); + } + Set accountRedirectUris = new HashSet<>(); + for (String r : accountClient.getRedirectUris()) { + accountRedirectUris.add(replace(r, "/realms/" + oldName + "/", "/realms/" + name + "/")); + } + accountClient.setRedirectUris(accountRedirectUris); + } + } + + private static String replace(String url, String target, String replacement) { + return url != null ? url.replace(target, replacement) : null; + } + + @Override + public void updateRealm(RealmRepresentation rep, RealmModel realm) { + if (rep.getRealm() != null) { + renameRealm(realm, rep.getRealm()); + } + + if (!Boolean.parseBoolean(rep.getAttributesOrEmpty().get("userProfileEnabled"))) { + UserProfileProvider provider = session.getProvider(UserProfileProvider.class); + provider.setConfiguration(null); + } + + // Import attributes first, so the stuff saved directly on representation (displayName, bruteForce etc) has bigger priority + if (rep.getAttributes() != null) { + Set attrsToRemove = new HashSet<>(realm.getAttributes().keySet()); + attrsToRemove.removeAll(rep.getAttributes().keySet()); + + for (Map.Entry entry : rep.getAttributes().entrySet()) { + realm.setAttribute(entry.getKey(), entry.getValue()); + } + + for (String attr : attrsToRemove) { + realm.removeAttribute(attr); + } + } + + if (rep.getDisplayName() != null) realm.setDisplayName(rep.getDisplayName()); + if (rep.getDisplayNameHtml() != null) realm.setDisplayNameHtml(rep.getDisplayNameHtml()); + if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); + if (rep.isUserManagedAccessAllowed() != null) realm.setUserManagedAccessAllowed(rep.isUserManagedAccessAllowed()); + if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected()); + if (rep.isPermanentLockout() != null) realm.setPermanentLockout(rep.isPermanentLockout()); + 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.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); + if (rep.isRegistrationEmailAsUsername() != null) + realm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername()); + if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe()); + if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); + if (rep.isLoginWithEmailAllowed() != null) realm.setLoginWithEmailAllowed(rep.isLoginWithEmailAllowed()); + if (rep.isDuplicateEmailsAllowed() != null) realm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed()); + if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); + if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed()); + if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase())); + if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); + if (rep.getAccessCodeLifespanUserAction() != null) + realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); + if (rep.getAccessCodeLifespanLogin() != null) + realm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin()); + if (rep.getActionTokenGeneratedByAdminLifespan() != null) + realm.setActionTokenGeneratedByAdminLifespan(rep.getActionTokenGeneratedByAdminLifespan()); + if (rep.getActionTokenGeneratedByUserLifespan() != null) + realm.setActionTokenGeneratedByUserLifespan(rep.getActionTokenGeneratedByUserLifespan()); + + OAuth2DeviceConfig deviceConfig = realm.getOAuth2DeviceConfig(); + + deviceConfig.setOAuth2DeviceCodeLifespan(rep.getOAuth2DeviceCodeLifespan()); + deviceConfig.setOAuth2DevicePollingInterval(rep.getOAuth2DevicePollingInterval()); + + if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore()); + if (rep.getDefaultSignatureAlgorithm() != null) realm.setDefaultSignatureAlgorithm(rep.getDefaultSignatureAlgorithm()); + if (rep.getRevokeRefreshToken() != null) realm.setRevokeRefreshToken(rep.getRevokeRefreshToken()); + if (rep.getRefreshTokenMaxReuse() != null) realm.setRefreshTokenMaxReuse(rep.getRefreshTokenMaxReuse()); + if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); + if (rep.getAccessTokenLifespanForImplicitFlow() != null) + realm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow()); + if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); + if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); + if (rep.getSsoSessionIdleTimeoutRememberMe() != null) realm.setSsoSessionIdleTimeoutRememberMe(rep.getSsoSessionIdleTimeoutRememberMe()); + if (rep.getSsoSessionMaxLifespanRememberMe() != null) realm.setSsoSessionMaxLifespanRememberMe(rep.getSsoSessionMaxLifespanRememberMe()); + if (rep.getOfflineSessionIdleTimeout() != null) + realm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout()); + // KEYCLOAK-7688 Offline Session Max for Offline Token + if (rep.getOfflineSessionMaxLifespanEnabled() != null) realm.setOfflineSessionMaxLifespanEnabled(rep.getOfflineSessionMaxLifespanEnabled()); + if (rep.getOfflineSessionMaxLifespan() != null) + realm.setOfflineSessionMaxLifespan(rep.getOfflineSessionMaxLifespan()); + if (rep.getClientSessionIdleTimeout() != null) + realm.setClientSessionIdleTimeout(rep.getClientSessionIdleTimeout()); + if (rep.getClientSessionMaxLifespan() != null) + realm.setClientSessionMaxLifespan(rep.getClientSessionMaxLifespan()); + if (rep.getClientOfflineSessionIdleTimeout() != null) + realm.setClientOfflineSessionIdleTimeout(rep.getClientOfflineSessionIdleTimeout()); + if (rep.getClientOfflineSessionMaxLifespan() != null) + realm.setClientOfflineSessionMaxLifespan(rep.getClientOfflineSessionMaxLifespan()); + 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.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled()); + if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration()); + if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<>(rep.getEventsListeners())); + if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes())); + + if (rep.isAdminEventsEnabled() != null) realm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); + if (rep.isAdminEventsDetailsEnabled() != null) + realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); + + + if (rep.getPasswordPolicy() != null) + realm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy())); + if (rep.getOtpPolicyType() != null) realm.setOTPPolicy(toPolicy(rep)); + + WebAuthnPolicy webAuthnPolicy = getWebAuthnPolicyTwoFactor(rep); + realm.setWebAuthnPolicy(webAuthnPolicy); + + webAuthnPolicy = getWebAuthnPolicyPasswordless(rep); + realm.setWebAuthnPolicyPasswordless(webAuthnPolicy); + + updateCibaSettings(rep, realm); + updateParSettings(rep, realm); + session.clientPolicy().updateRealmModelFromRepresentation(realm, rep); + + if (rep.getSmtpServer() != null) { + Map config = new HashMap(rep.getSmtpServer()); + if (rep.getSmtpServer().containsKey("password") && ComponentRepresentation.SECRET_VALUE.equals(rep.getSmtpServer().get("password"))) { + String passwordValue = realm.getSmtpConfig() != null ? realm.getSmtpConfig().get("password") : null; + config.put("password", passwordValue); + } + realm.setSmtpConfig(config); + } + + if (rep.getBrowserSecurityHeaders() != null) { + realm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders()); + } + + if (rep.isInternationalizationEnabled() != null) { + realm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); + } + if (rep.getSupportedLocales() != null) { + realm.setSupportedLocales(new HashSet(rep.getSupportedLocales())); + } + if (rep.getDefaultLocale() != null) { + realm.setDefaultLocale(rep.getDefaultLocale()); + } + if (rep.getBrowserFlow() != null) { + realm.setBrowserFlow(realm.getFlowByAlias(rep.getBrowserFlow())); + } + if (rep.getRegistrationFlow() != null) { + realm.setRegistrationFlow(realm.getFlowByAlias(rep.getRegistrationFlow())); + } + if (rep.getDirectGrantFlow() != null) { + realm.setDirectGrantFlow(realm.getFlowByAlias(rep.getDirectGrantFlow())); + } + if (rep.getResetCredentialsFlow() != null) { + realm.setResetCredentialsFlow(realm.getFlowByAlias(rep.getResetCredentialsFlow())); + } + if (rep.getClientAuthenticationFlow() != null) { + realm.setClientAuthenticationFlow(realm.getFlowByAlias(rep.getClientAuthenticationFlow())); + } + if (rep.getDockerAuthenticationFlow() != null) { + realm.setDockerAuthenticationFlow(realm.getFlowByAlias(rep.getDockerAuthenticationFlow())); + } + } + + private static void convertDeprecatedApplications(KeycloakSession session, RealmRepresentation realm) { + if (realm.getApplications() != null || realm.getOauthClients() != null) { + if (realm.getClients() == null) { + realm.setClients(new LinkedList()); + } + + List clients = new LinkedList<>(); + if (realm.getApplications() != null) { + clients.addAll(realm.getApplications()); + } + if (realm.getOauthClients() != null) { + clients.addAll(realm.getOauthClients()); + } + + for (ApplicationRepresentation app : clients) { + app.setClientId(app.getName()); + app.setName(null); + + if (app instanceof OAuthClientRepresentation) { + app.setConsentRequired(true); + app.setFullScopeAllowed(false); + } + + if (app.getProtocolMappers() == null && app.getClaims() != null) { + long mask = getClaimsMask(app.getClaims()); + List convertedProtocolMappers = session.getProvider(MigrationProvider.class).getMappersForClaimMask(mask); + app.setProtocolMappers(convertedProtocolMappers); + app.setClaims(null); + } + + realm.getClients().add(app); + } + } + + if (realm.getApplicationScopeMappings() != null && realm.getClientScopeMappings() == null) { + realm.setClientScopeMappings(realm.getApplicationScopeMappings()); + } + + if (realm.getRoles() != null && realm.getRoles().getApplication() != null && realm.getRoles().getClient() == null) { + realm.getRoles().setClient(realm.getRoles().getApplication()); + } + + if (realm.getUsers() != null) { + for (UserRepresentation user : realm.getUsers()) { + if (user.getApplicationRoles() != null && user.getClientRoles() == null) { + user.setClientRoles(user.getApplicationRoles()); + } + } + } + + if (realm.getRoles() != null && realm.getRoles().getRealm() != null) { + for (RoleRepresentation role : realm.getRoles().getRealm()) { + if (role.getComposites() != null && role.getComposites().getApplication() != null && role.getComposites().getClient() == null) { + role.getComposites().setClient(role.getComposites().getApplication()); + } + } + } + + if (realm.getRoles() != null && realm.getRoles().getClient() != null) { + for (Map.Entry> clientRoles : realm.getRoles().getClient().entrySet()) { + for (RoleRepresentation role : clientRoles.getValue()) { + if (role.getComposites() != null && role.getComposites().getApplication() != null && role.getComposites().getClient() == null) { + role.getComposites().setClient(role.getComposites().getApplication()); + } + } + } + } + } + + private static void convertDeprecatedSocialProviders(RealmRepresentation rep) { + if (rep.isSocial() != null && rep.isSocial() && rep.getSocialProviders() != null && !rep.getSocialProviders().isEmpty() && rep.getIdentityProviders() == null) { + Boolean updateProfileFirstLogin = rep.isUpdateProfileOnInitialSocialLogin() != null && rep.isUpdateProfileOnInitialSocialLogin(); + if (rep.getSocialProviders() != null) { + + logger.warn("Using deprecated 'social' configuration in JSON representation. It will be removed in future versions"); + List identityProviders = new LinkedList<>(); + for (String k : rep.getSocialProviders().keySet()) { + if (k.endsWith(".key")) { + String providerId = k.split("\\.")[0]; + String key = rep.getSocialProviders().get(k); + String secret = rep.getSocialProviders().get(k.replace(".key", ".secret")); + + IdentityProviderRepresentation identityProvider = new IdentityProviderRepresentation(); + identityProvider.setAlias(providerId); + identityProvider.setProviderId(providerId); + identityProvider.setEnabled(true); + identityProvider.setLinkOnly(false); + identityProvider.setUpdateProfileFirstLogin(updateProfileFirstLogin); + + Map config = new HashMap<>(); + config.put("clientId", key); + config.put("clientSecret", secret); + identityProvider.setConfig(config); + + identityProviders.add(identityProvider); + } + } + rep.setIdentityProviders(identityProviders); + } + } + } + + private static void convertDeprecatedClientTemplates(RealmRepresentation realm) { + if (realm.getClientTemplates() != null) { + + logger.warnf("Using deprecated 'clientTemplates' configuration in JSON representation for realm '%s'. It will be removed in future versions", realm.getRealm()); + + List clientScopes = new LinkedList<>(); + for (ClientTemplateRepresentation template : realm.getClientTemplates()) { + ClientScopeRepresentation scopeRep = new ClientScopeRepresentation(); + scopeRep.setId(template.getId()); + scopeRep.setName(template.getName()); + scopeRep.setProtocol(template.getProtocol()); + scopeRep.setDescription(template.getDescription()); + scopeRep.setAttributes(template.getAttributes()); + scopeRep.setProtocolMappers(template.getProtocolMappers()); + + clientScopes.add(scopeRep); + } + + realm.setClientScopes(clientScopes); + } + } + + + public static void importUserFederationProvidersAndMappers(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm) { + // providers to convert to component model + Set convertSet = new HashSet<>(); + convertSet.add(LDAPConstants.LDAP_PROVIDER); + convertSet.add("kerberos"); + Map mapperConvertSet = new HashMap<>(); + mapperConvertSet.put(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"); + + + Map userStorageModels = new HashMap<>(); + + if (rep.getUserFederationProviders() != null) { + for (UserFederationProviderRepresentation fedRep : rep.getUserFederationProviders()) { + if (convertSet.contains(fedRep.getProviderName())) { + ComponentModel component = convertFedProviderToComponent(newRealm.getId(), fedRep); + userStorageModels.put(fedRep.getDisplayName(), newRealm.importComponentModel(component)); + } + } + } + + // This is for case, when you have hand-written JSON file with LDAP userFederationProvider, but WITHOUT any userFederationMappers configured. Default LDAP mappers need to be created in that case. + Set storageProvidersWhichShouldImportDefaultMappers = new HashSet<>(userStorageModels.keySet()); + + if (rep.getUserFederationMappers() != null) { + for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) { + if (userStorageModels.containsKey(representation.getFederationProviderDisplayName())) { + ComponentModel parent = userStorageModels.get(representation.getFederationProviderDisplayName()); + String newMapperType = mapperConvertSet.get(parent.getProviderId()); + ComponentModel mapper = convertFedMapperToComponent(newRealm, parent, representation, newMapperType); + newRealm.importComponentModel(mapper); + + + storageProvidersWhichShouldImportDefaultMappers.remove(representation.getFederationProviderDisplayName()); + + } + } + } + + for (String providerDisplayName : storageProvidersWhichShouldImportDefaultMappers) { + ComponentUtil.notifyCreated(session, newRealm, userStorageModels.get(providerDisplayName)); + } + } + + public static ComponentModel convertFedMapperToComponent(RealmModel realm, ComponentModel parent, UserFederationMapperRepresentation rep, String newMapperType) { + ComponentModel mapper = new ComponentModel(); + mapper.setId(rep.getId()); + mapper.setName(rep.getName()); + mapper.setProviderId(rep.getFederationMapperType()); + mapper.setProviderType(newMapperType); + mapper.setParentId(parent.getId()); + if (rep.getConfig() != null) { + for (Map.Entry entry : rep.getConfig().entrySet()) { + mapper.getConfig().putSingle(entry.getKey(), entry.getValue()); + } + } + return mapper; + } + + public static ComponentModel convertFedProviderToComponent(String realmId, UserFederationProviderRepresentation fedModel) { + UserStorageProviderModel model = new UserStorageProviderModel(); + model.setId(fedModel.getId()); + model.setName(fedModel.getDisplayName()); + model.setParentId(realmId); + model.setProviderId(fedModel.getProviderName()); + model.setProviderType(UserStorageProvider.class.getName()); + model.setFullSyncPeriod(fedModel.getFullSyncPeriod()); + model.setPriority(fedModel.getPriority()); + model.setChangedSyncPeriod(fedModel.getChangedSyncPeriod()); + model.setLastSync(fedModel.getLastSync()); + if (fedModel.getConfig() != null) { + for (Map.Entry entry : fedModel.getConfig().entrySet()) { + model.getConfig().putSingle(entry.getKey(), entry.getValue()); + } + } + return model; + } + + protected static void importComponents(RealmModel newRealm, MultivaluedHashMap components, String parentId) { + for (Map.Entry> entry : components.entrySet()) { + String providerType = entry.getKey(); + for (ComponentExportRepresentation compRep : entry.getValue()) { + ComponentModel component = new ComponentModel(); + component.setId(compRep.getId()); + component.setName(compRep.getName()); + component.setConfig(compRep.getConfig()); + component.setProviderType(providerType); + component.setProviderId(compRep.getProviderId()); + component.setSubType(compRep.getSubType()); + component.setParentId(parentId); + component = newRealm.importComponentModel(component); + if (compRep.getSubComponents() != null) { + importComponents(newRealm, compRep.getSubComponents(), component.getId()); + } + } + } + } + + public static void importGroups(RealmModel realm, RealmRepresentation rep) { + List groups = rep.getGroups(); + if (groups == null) return; + + GroupModel parent = null; + for (GroupRepresentation group : groups) { + importGroup(realm, parent, group); + } + } + + private static WebAuthnPolicy getWebAuthnPolicyTwoFactor(RealmRepresentation rep) { + WebAuthnPolicy webAuthnPolicy = new WebAuthnPolicy(); + + String webAuthnPolicyRpEntityName = rep.getWebAuthnPolicyRpEntityName(); + if (webAuthnPolicyRpEntityName == null || webAuthnPolicyRpEntityName.isEmpty()) + webAuthnPolicyRpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME; + webAuthnPolicy.setRpEntityName(webAuthnPolicyRpEntityName); + + List webAuthnPolicySignatureAlgorithms = rep.getWebAuthnPolicySignatureAlgorithms(); + if (webAuthnPolicySignatureAlgorithms == null || webAuthnPolicySignatureAlgorithms.isEmpty()) + webAuthnPolicySignatureAlgorithms = Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(",")); + webAuthnPolicy.setSignatureAlgorithm(webAuthnPolicySignatureAlgorithms); + + String webAuthnPolicyRpId = rep.getWebAuthnPolicyRpId(); + if (webAuthnPolicyRpId == null || webAuthnPolicyRpId.isEmpty()) + webAuthnPolicyRpId = ""; + webAuthnPolicy.setRpId(webAuthnPolicyRpId); + + String webAuthnPolicyAttestationConveyancePreference = rep.getWebAuthnPolicyAttestationConveyancePreference(); + if (webAuthnPolicyAttestationConveyancePreference == null || webAuthnPolicyAttestationConveyancePreference.isEmpty()) + webAuthnPolicyAttestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setAttestationConveyancePreference(webAuthnPolicyAttestationConveyancePreference); + + String webAuthnPolicyAuthenticatorAttachment = rep.getWebAuthnPolicyAuthenticatorAttachment(); + if (webAuthnPolicyAuthenticatorAttachment == null || webAuthnPolicyAuthenticatorAttachment.isEmpty()) + webAuthnPolicyAuthenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setAuthenticatorAttachment(webAuthnPolicyAuthenticatorAttachment); + + String webAuthnPolicyRequireResidentKey = rep.getWebAuthnPolicyRequireResidentKey(); + if (webAuthnPolicyRequireResidentKey == null || webAuthnPolicyRequireResidentKey.isEmpty()) + webAuthnPolicyRequireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setRequireResidentKey(webAuthnPolicyRequireResidentKey); + + String webAuthnPolicyUserVerificationRequirement = rep.getWebAuthnPolicyUserVerificationRequirement(); + if (webAuthnPolicyUserVerificationRequirement == null || webAuthnPolicyUserVerificationRequirement.isEmpty()) + webAuthnPolicyUserVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setUserVerificationRequirement(webAuthnPolicyUserVerificationRequirement); + + Integer webAuthnPolicyCreateTimeout = rep.getWebAuthnPolicyCreateTimeout(); + if (webAuthnPolicyCreateTimeout != null) webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout); + else webAuthnPolicy.setCreateTimeout(0); + + Boolean webAuthnPolicyAvoidSameAuthenticatorRegister = rep.isWebAuthnPolicyAvoidSameAuthenticatorRegister(); + if (webAuthnPolicyAvoidSameAuthenticatorRegister != null) webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister); + + List webAuthnPolicyAcceptableAaguids = rep.getWebAuthnPolicyAcceptableAaguids(); + if (webAuthnPolicyAcceptableAaguids != null) webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids); + + return webAuthnPolicy; + } + + + private static WebAuthnPolicy getWebAuthnPolicyPasswordless(RealmRepresentation rep) { + WebAuthnPolicy webAuthnPolicy = new WebAuthnPolicy(); + + String webAuthnPolicyRpEntityName = rep.getWebAuthnPolicyPasswordlessRpEntityName(); + if (webAuthnPolicyRpEntityName == null || webAuthnPolicyRpEntityName.isEmpty()) + webAuthnPolicyRpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME; + webAuthnPolicy.setRpEntityName(webAuthnPolicyRpEntityName); + + List webAuthnPolicySignatureAlgorithms = rep.getWebAuthnPolicyPasswordlessSignatureAlgorithms(); + if (webAuthnPolicySignatureAlgorithms == null || webAuthnPolicySignatureAlgorithms.isEmpty()) + webAuthnPolicySignatureAlgorithms = Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(",")); + webAuthnPolicy.setSignatureAlgorithm(webAuthnPolicySignatureAlgorithms); + + String webAuthnPolicyRpId = rep.getWebAuthnPolicyPasswordlessRpId(); + if (webAuthnPolicyRpId == null || webAuthnPolicyRpId.isEmpty()) + webAuthnPolicyRpId = ""; + webAuthnPolicy.setRpId(webAuthnPolicyRpId); + + String webAuthnPolicyAttestationConveyancePreference = rep.getWebAuthnPolicyPasswordlessAttestationConveyancePreference(); + if (webAuthnPolicyAttestationConveyancePreference == null || webAuthnPolicyAttestationConveyancePreference.isEmpty()) + webAuthnPolicyAttestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setAttestationConveyancePreference(webAuthnPolicyAttestationConveyancePreference); + + String webAuthnPolicyAuthenticatorAttachment = rep.getWebAuthnPolicyPasswordlessAuthenticatorAttachment(); + if (webAuthnPolicyAuthenticatorAttachment == null || webAuthnPolicyAuthenticatorAttachment.isEmpty()) + webAuthnPolicyAuthenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setAuthenticatorAttachment(webAuthnPolicyAuthenticatorAttachment); + + String webAuthnPolicyRequireResidentKey = rep.getWebAuthnPolicyPasswordlessRequireResidentKey(); + if (webAuthnPolicyRequireResidentKey == null || webAuthnPolicyRequireResidentKey.isEmpty()) + webAuthnPolicyRequireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setRequireResidentKey(webAuthnPolicyRequireResidentKey); + + String webAuthnPolicyUserVerificationRequirement = rep.getWebAuthnPolicyPasswordlessUserVerificationRequirement(); + if (webAuthnPolicyUserVerificationRequirement == null || webAuthnPolicyUserVerificationRequirement.isEmpty()) + webAuthnPolicyUserVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; + webAuthnPolicy.setUserVerificationRequirement(webAuthnPolicyUserVerificationRequirement); + + Integer webAuthnPolicyCreateTimeout = rep.getWebAuthnPolicyPasswordlessCreateTimeout(); + if (webAuthnPolicyCreateTimeout != null) webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout); + else webAuthnPolicy.setCreateTimeout(0); + + Boolean webAuthnPolicyAvoidSameAuthenticatorRegister = rep.isWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister(); + if (webAuthnPolicyAvoidSameAuthenticatorRegister != null) webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister); + + List webAuthnPolicyAcceptableAaguids = rep.getWebAuthnPolicyPasswordlessAcceptableAaguids(); + if (webAuthnPolicyAcceptableAaguids != null) webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids); + + return webAuthnPolicy; + } + public static Map importAuthenticationFlows(RealmModel newRealm, RealmRepresentation rep) { + Map mappedFlows = new HashMap<>(); + if (rep.getAuthenticationFlows() == null) { + // assume this is an old version being imported + DefaultAuthenticationFlows.migrateFlows(newRealm); + } else { + if (rep.getAuthenticatorConfig() != null) { + for (AuthenticatorConfigRepresentation configRep : rep.getAuthenticatorConfig()) { + if (configRep.getAlias() == null) { + // this can happen only during import json files from keycloak 3.4.0 and older + throw new IllegalStateException("Provided realm contains authenticator config with null alias. " + + "It should be resolved by adding alias to the authenticator config before exporting the realm."); + } + AuthenticatorConfigModel model = RepresentationToModel.toModel(configRep); + newRealm.addAuthenticatorConfig(model); + } + } + if (rep.getAuthenticationFlows() != null) { + for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { + AuthenticationFlowModel model = RepresentationToModel.toModel(flowRep); + // make sure new id is generated for new AuthenticationFlowModel instance + String previousId = model.getId(); + model.setId(null); + model = newRealm.addAuthenticationFlow(model); + // store the mapped ids so that clients can reference the correct flow when importing the authenticationFlowBindingOverrides + mappedFlows.put(previousId, model.getId()); + } + for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { + AuthenticationFlowModel model = newRealm.getFlowByAlias(flowRep.getAlias()); + for (AuthenticationExecutionExportRepresentation exeRep : flowRep.getAuthenticationExecutions()) { + AuthenticationExecutionModel execution = toModel(newRealm, model, exeRep); + newRealm.addAuthenticatorExecution(execution); + } + } + } + } + if (rep.getBrowserFlow() == null) { + AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW); + if (defaultFlow != null) { + newRealm.setBrowserFlow(defaultFlow); + } + } else { + newRealm.setBrowserFlow(newRealm.getFlowByAlias(rep.getBrowserFlow())); + } + if (rep.getRegistrationFlow() == null) { + AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.REGISTRATION_FLOW); + if (defaultFlow != null) { + newRealm.setRegistrationFlow(defaultFlow); + } + } else { + newRealm.setRegistrationFlow(newRealm.getFlowByAlias(rep.getRegistrationFlow())); + } + if (rep.getDirectGrantFlow() == null) { + AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW); + if (defaultFlow != null) { + newRealm.setDirectGrantFlow(defaultFlow); + } + } else { + newRealm.setDirectGrantFlow(newRealm.getFlowByAlias(rep.getDirectGrantFlow())); + } + + // reset credentials + client flow needs to be more defensive as they were added later (in 1.5 ) + if (rep.getResetCredentialsFlow() == null) { + AuthenticationFlowModel resetFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW); + if (resetFlow == null) { + DefaultAuthenticationFlows.resetCredentialsFlow(newRealm); + } else { + newRealm.setResetCredentialsFlow(resetFlow); + } + } else { + newRealm.setResetCredentialsFlow(newRealm.getFlowByAlias(rep.getResetCredentialsFlow())); + } + if (rep.getClientAuthenticationFlow() == null) { + AuthenticationFlowModel clientFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.CLIENT_AUTHENTICATION_FLOW); + if (clientFlow == null) { + DefaultAuthenticationFlows.clientAuthFlow(newRealm); + } else { + newRealm.setClientAuthenticationFlow(clientFlow); + } + } else { + newRealm.setClientAuthenticationFlow(newRealm.getFlowByAlias(rep.getClientAuthenticationFlow())); + } + + // Added in 1.7 + if (newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW) == null) { + DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true); + } + + // Added in 2.2 + String defaultProvider = null; + if (rep.getIdentityProviders() != null) { + for (IdentityProviderRepresentation i : rep.getIdentityProviders()) { + if (i.isEnabled() && i.isAuthenticateByDefault()) { + defaultProvider = i.getProviderId(); + break; + } + } + } + + // Added in 3.2 + if (rep.getDockerAuthenticationFlow() == null) { + AuthenticationFlowModel dockerAuthenticationFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.DOCKER_AUTH); + if (dockerAuthenticationFlow == null) { + DefaultAuthenticationFlows.dockerAuthenticationFlow(newRealm); + } else { + newRealm.setDockerAuthenticationFlow(dockerAuthenticationFlow); + } + } else { + newRealm.setDockerAuthenticationFlow(newRealm.getFlowByAlias(rep.getDockerAuthenticationFlow())); + } + + DefaultAuthenticationFlows.addIdentityProviderAuthenticator(newRealm, defaultProvider); + + return mappedFlows; + } + + private static AuthenticationExecutionModel toModel(RealmModel realm, AuthenticationFlowModel parentFlow, AuthenticationExecutionExportRepresentation rep) { + AuthenticationExecutionModel model = new AuthenticationExecutionModel(); + if (rep.getAuthenticatorConfig() != null) { + AuthenticatorConfigModel config = realm.getAuthenticatorConfigByAlias(rep.getAuthenticatorConfig()); + model.setAuthenticatorConfig(config.getId()); + } + model.setAuthenticator(rep.getAuthenticator()); + model.setAuthenticatorFlow(rep.isAuthenticatorFlow()); + if (rep.getFlowAlias() != null) { + AuthenticationFlowModel flow = realm.getFlowByAlias(rep.getFlowAlias()); + model.setFlowId(flow.getId()); + } + model.setPriority(rep.getPriority()); + try { + model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement())); + model.setParentFlow(parentFlow.getId()); + } catch (IllegalArgumentException iae) { + //retro-compatible for previous OPTIONAL being changed to CONDITIONAL + if ("OPTIONAL".equals(rep.getRequirement())){ + MigrateTo8_0_0.migrateOptionalAuthenticationExecution(realm, parentFlow, model, false); + } + } + return model; + } + + private static void updateCibaSettings(RealmRepresentation rep, RealmModel realm) { + Map newAttributes = rep.getAttributesOrEmpty(); + CibaConfig cibaPolicy = realm.getCibaPolicy(); + + cibaPolicy.setBackchannelTokenDeliveryMode(newAttributes.get(CibaConfig.CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE)); + cibaPolicy.setExpiresIn(newAttributes.get(CibaConfig.CIBA_EXPIRES_IN)); + cibaPolicy.setPoolingInterval(newAttributes.get(CibaConfig.CIBA_INTERVAL)); + cibaPolicy.setAuthRequestedUserHint(newAttributes.get(CibaConfig.CIBA_AUTH_REQUESTED_USER_HINT)); + } + + private static void updateParSettings(RealmRepresentation rep, RealmModel realm) { + Map newAttributes = rep.getAttributesOrEmpty(); + ParConfig parPolicy = realm.getParPolicy(); + + parPolicy.setRequestUriLifespan(newAttributes.get(ParConfig.PAR_REQUEST_URI_LIFESPAN)); + } + + public static OTPPolicy toPolicy(RealmRepresentation rep) { + OTPPolicy policy = new OTPPolicy(); + if (rep.getOtpPolicyType() != null) policy.setType(rep.getOtpPolicyType()); + if (rep.getOtpPolicyLookAheadWindow() != null) policy.setLookAheadWindow(rep.getOtpPolicyLookAheadWindow()); + if (rep.getOtpPolicyInitialCounter() != null) policy.setInitialCounter(rep.getOtpPolicyInitialCounter()); + if (rep.getOtpPolicyAlgorithm() != null) policy.setAlgorithm(rep.getOtpPolicyAlgorithm()); + if (rep.getOtpPolicyDigits() != null) policy.setDigits(rep.getOtpPolicyDigits()); + if (rep.getOtpPolicyPeriod() != null) policy.setPeriod(rep.getOtpPolicyPeriod()); + return policy; + + } + + + public static RequiredActionProviderModel toModel(RequiredActionProviderRepresentation rep) { + RequiredActionProviderModel model = new RequiredActionProviderModel(); + model.setConfig(RepresentationToModel.removeEmptyString(rep.getConfig())); + model.setPriority(rep.getPriority()); + model.setDefaultAction(rep.isDefaultAction()); + model.setEnabled(rep.isEnabled()); + model.setProviderId(rep.getProviderId()); + model.setName(rep.getName()); + model.setAlias(rep.getAlias()); + return model; + } + + + public static void importRealmAuthorizationSettings(RealmRepresentation rep, RealmModel newRealm, KeycloakSession session) { + if (rep.getClients() != null) { + rep.getClients().forEach(clientRepresentation -> { + ClientModel client = newRealm.getClientByClientId(clientRepresentation.getClientId()); + RepresentationToModel.importAuthorizationSettings(clientRepresentation, client, session); + }); + } + } + +} diff --git a/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java new file mode 100644 index 0000000000..7fa09376e3 --- /dev/null +++ b/model/legacy-private/src/main/java/org/keycloak/storage/datastore/LegacyMigrationManager.java @@ -0,0 +1,181 @@ +package org.keycloak.storage.datastore; + +import org.jboss.logging.Logger; +import org.keycloak.common.Version; +import org.keycloak.migration.MigrationModel; +import org.keycloak.migration.ModelVersion; +import org.keycloak.migration.migrators.MigrateTo12_0_0; +import org.keycloak.migration.migrators.MigrateTo14_0_0; +import org.keycloak.migration.migrators.MigrateTo18_0_0; +import org.keycloak.migration.migrators.MigrateTo1_2_0; +import org.keycloak.migration.migrators.MigrateTo1_3_0; +import org.keycloak.migration.migrators.MigrateTo1_4_0; +import org.keycloak.migration.migrators.MigrateTo1_5_0; +import org.keycloak.migration.migrators.MigrateTo1_6_0; +import org.keycloak.migration.migrators.MigrateTo1_7_0; +import org.keycloak.migration.migrators.MigrateTo1_8_0; +import org.keycloak.migration.migrators.MigrateTo1_9_0; +import org.keycloak.migration.migrators.MigrateTo1_9_2; +import org.keycloak.migration.migrators.MigrateTo2_0_0; +import org.keycloak.migration.migrators.MigrateTo2_1_0; +import org.keycloak.migration.migrators.MigrateTo2_2_0; +import org.keycloak.migration.migrators.MigrateTo2_3_0; +import org.keycloak.migration.migrators.MigrateTo2_5_0; +import org.keycloak.migration.migrators.MigrateTo3_0_0; +import org.keycloak.migration.migrators.MigrateTo3_1_0; +import org.keycloak.migration.migrators.MigrateTo3_2_0; +import org.keycloak.migration.migrators.MigrateTo3_4_0; +import org.keycloak.migration.migrators.MigrateTo3_4_1; +import org.keycloak.migration.migrators.MigrateTo3_4_2; +import org.keycloak.migration.migrators.MigrateTo4_0_0; +import org.keycloak.migration.migrators.MigrateTo4_2_0; +import org.keycloak.migration.migrators.MigrateTo4_6_0; +import org.keycloak.migration.migrators.MigrateTo6_0_0; +import org.keycloak.migration.migrators.MigrateTo8_0_0; +import org.keycloak.migration.migrators.MigrateTo8_0_2; +import org.keycloak.migration.migrators.MigrateTo9_0_0; +import org.keycloak.migration.migrators.MigrateTo9_0_4; +import org.keycloak.migration.migrators.Migration; +import org.keycloak.models.Constants; +import org.keycloak.models.DeploymentStateProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.storage.MigrationManager; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * This wraps the functionality for migrations of the legacy storage. This will be handled differently for the new map storage, + * therefore, it has been extracted. + * + * @author Alexander Schwartz + */ +public class LegacyMigrationManager implements MigrationManager { + private static final Logger logger = Logger.getLogger(LegacyMigrationManager.class); + + private static final Migration[] migrations = { + new MigrateTo1_2_0(), + new MigrateTo1_3_0(), + new MigrateTo1_4_0(), + new MigrateTo1_5_0(), + new MigrateTo1_6_0(), + new MigrateTo1_7_0(), + new MigrateTo1_8_0(), + new MigrateTo1_9_0(), + new MigrateTo1_9_2(), + new MigrateTo2_0_0(), + new MigrateTo2_1_0(), + new MigrateTo2_2_0(), + new MigrateTo2_3_0(), + new MigrateTo2_5_0(), + new MigrateTo3_0_0(), + new MigrateTo3_1_0(), + new MigrateTo3_2_0(), + new MigrateTo3_4_0(), + new MigrateTo3_4_1(), + new MigrateTo3_4_2(), + new MigrateTo4_0_0(), + new MigrateTo4_2_0(), + new MigrateTo4_6_0(), + new MigrateTo6_0_0(), + new MigrateTo8_0_0(), + new MigrateTo8_0_2(), + new MigrateTo9_0_0(), + new MigrateTo9_0_4(), + new MigrateTo12_0_0(), + new MigrateTo14_0_0(), + new MigrateTo18_0_0() + }; + + private final KeycloakSession session; + + public LegacyMigrationManager(KeycloakSession session) { + this.session = session; + } + + public void migrate() { + session.setAttribute(Constants.STORAGE_BATCH_ENABLED, Boolean.getBoolean("keycloak.migration.batch-enabled")); + session.setAttribute(Constants.STORAGE_BATCH_SIZE, Integer.getInteger("keycloak.migration.batch-size")); + MigrationModel model = session.getProvider(DeploymentStateProvider.class).getMigrationModel(); + + ModelVersion currentVersion = new ModelVersion(Version.VERSION_KEYCLOAK); + ModelVersion latestUpdate = migrations[migrations.length-1].getVersion(); + ModelVersion databaseVersion = model.getStoredVersion() != null ? new ModelVersion(model.getStoredVersion()) : null; + + if (databaseVersion == null || databaseVersion.lessThan(latestUpdate)) { + for (Migration m : migrations) { + if (databaseVersion == null || databaseVersion.lessThan(m.getVersion())) { + if (databaseVersion != null) { + logger.debugf("Migrating older model to %s", m.getVersion()); + } + m.migrate(session); + } + } + } + + if (databaseVersion == null || databaseVersion.lessThan(currentVersion)) { + model.setStoredVersion(currentVersion.toString()); + } + + Version.RESOURCES_VERSION = model.getResourcesTag(); + } + + public static final ModelVersion RHSSO_VERSION_7_0_KEYCLOAK_VERSION = new ModelVersion("1.9.8"); + public static final ModelVersion RHSSO_VERSION_7_1_KEYCLOAK_VERSION = new ModelVersion("2.5.5"); + public static final ModelVersion RHSSO_VERSION_7_2_KEYCLOAK_VERSION = new ModelVersion("3.4.3"); + public static final ModelVersion RHSSO_VERSION_7_3_KEYCLOAK_VERSION = new ModelVersion("4.8.3"); + public static final ModelVersion RHSSO_VERSION_7_4_KEYCLOAK_VERSION = new ModelVersion("9.0.3"); + + private static final Map PATTERN_MATCHER = new LinkedHashMap<>(); + static { + PATTERN_MATCHER.put(Pattern.compile("^7\\.0\\.\\d+\\.GA$"), RHSSO_VERSION_7_0_KEYCLOAK_VERSION); + PATTERN_MATCHER.put(Pattern.compile("^7\\.1\\.\\d+\\.GA$"), RHSSO_VERSION_7_1_KEYCLOAK_VERSION); + PATTERN_MATCHER.put(Pattern.compile("^7\\.2\\.\\d+\\.GA$"), RHSSO_VERSION_7_2_KEYCLOAK_VERSION); + PATTERN_MATCHER.put(Pattern.compile("^7\\.3\\.\\d+\\.GA$"), RHSSO_VERSION_7_3_KEYCLOAK_VERSION); + PATTERN_MATCHER.put(Pattern.compile("^7\\.4\\.\\d+\\.GA$"), RHSSO_VERSION_7_4_KEYCLOAK_VERSION); + } + + public void migrate(RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) { + ModelVersion stored = null; + if (rep.getKeycloakVersion() != null) { + stored = convertRHSSOVersionToKeycloakVersion(rep.getKeycloakVersion()); + if (stored == null) { + stored = new ModelVersion(rep.getKeycloakVersion()); + } + } + if (stored == null) { + stored = migrations[0].getVersion(); + } + + for (Migration m : migrations) { + if (stored == null || stored.lessThan(m.getVersion())) { + if (stored != null) { + logger.debugf("Migrating older json representation to %s", m.getVersion()); + } + try { + m.migrateImport(session, realm, rep, skipUserDependent); + } catch (Exception e) { + logger.error("Failed to migrate json representation for version: " + m.getVersion(), e); + } + } + } + } + + public static ModelVersion convertRHSSOVersionToKeycloakVersion(String version) { + // look for the keycloakVersion pattern to identify it as RH SSO + for (Pattern pattern : PATTERN_MATCHER.keySet()) { + if (pattern.matcher(version).find()) { + return PATTERN_MATCHER.get(pattern); + } + } + // chceck if the version is in format for CD releases, e.g.: "keycloakVersion": "6" + if (Pattern.compile("^[0-9]*$").matcher(version).find()) { + return new ModelVersion(Integer.parseInt(version), 0, 0); + } + return null; + } + +} diff --git a/model/legacy-private/src/main/java/org/keycloak/storage/managers/UserStorageSyncManager.java b/model/legacy-private/src/main/java/org/keycloak/storage/managers/UserStorageSyncManager.java index c2a0cd48ea..cca71208ba 100755 --- a/model/legacy-private/src/main/java/org/keycloak/storage/managers/UserStorageSyncManager.java +++ b/model/legacy-private/src/main/java/org/keycloak/storage/managers/UserStorageSyncManager.java @@ -31,6 +31,7 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderFactory; import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.UserStorageUtil; import org.keycloak.storage.user.ImportSynchronization; import org.keycloak.storage.user.SynchronizationResult; import org.keycloak.timer.TimerProvider; @@ -61,7 +62,7 @@ public class UserStorageSyncManager { public void run(KeycloakSession session) { Stream realms = session.realms().getRealmsWithProviderTypeStream(UserStorageProvider.class); realms.forEach(realm -> { - Stream providers = realm.getUserStorageProvidersStream(); + Stream providers = UserStorageUtil.getUserStorageProvidersStream(realm); providers.forEachOrdered(provider -> { UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, provider.getProviderId()); if (factory instanceof ImportSynchronization && provider.isImportEnabled()) { @@ -165,7 +166,7 @@ public class UserStorageSyncManager { public static void notifyToRefreshPeriodicSyncAll(KeycloakSession session, RealmModel realm, boolean removed) { - realm.getUserStorageProvidersStream().forEachOrdered(fedProvider -> + UserStorageUtil.getUserStorageProvidersStream(realm).forEachOrdered(fedProvider -> notifyToRefreshPeriodicSync(session, realm, fedProvider, removed)); } @@ -267,7 +268,7 @@ public class UserStorageSyncManager { @Override public void run(KeycloakSession session) { RealmModel persistentRealm = session.realms().getRealm(realmId); - persistentRealm.getUserStorageProvidersStream() + UserStorageUtil.getUserStorageProvidersStream(persistentRealm) .filter(persistentFedProvider -> Objects.equals(provider.getId(), persistentFedProvider.getId())) .forEachOrdered(persistentFedProvider -> { // Update persistent provider in DB diff --git a/model/legacy-private/src/test/java/org/keycloak/models/datastore/LegacyMigrationManagerTest.java b/model/legacy-private/src/test/java/org/keycloak/models/datastore/LegacyMigrationManagerTest.java new file mode 100755 index 0000000000..e6e4fd1f55 --- /dev/null +++ b/model/legacy-private/src/test/java/org/keycloak/models/datastore/LegacyMigrationManagerTest.java @@ -0,0 +1,71 @@ +/* + * 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.datastore; + +import org.junit.Assert; +import org.junit.Test; +import org.keycloak.migration.ModelVersion; + +import static org.keycloak.storage.datastore.LegacyMigrationManager.RHSSO_VERSION_7_0_KEYCLOAK_VERSION; +import static org.keycloak.storage.datastore.LegacyMigrationManager.RHSSO_VERSION_7_1_KEYCLOAK_VERSION; +import static org.keycloak.storage.datastore.LegacyMigrationManager.RHSSO_VERSION_7_2_KEYCLOAK_VERSION; +import static org.keycloak.storage.datastore.LegacyMigrationManager.RHSSO_VERSION_7_3_KEYCLOAK_VERSION; +import static org.keycloak.storage.datastore.LegacyMigrationManager.RHSSO_VERSION_7_4_KEYCLOAK_VERSION; +import static org.keycloak.storage.datastore.LegacyMigrationManager.convertRHSSOVersionToKeycloakVersion; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class LegacyMigrationManagerTest { + + @Test + public void testRHSSOVersionToKeycloakVersionConversion() { + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.0.GA"), is(equalTo(RHSSO_VERSION_7_0_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.1.GA"), is(equalTo(RHSSO_VERSION_7_0_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.2.GA"), is(equalTo(RHSSO_VERSION_7_0_KEYCLOAK_VERSION))); + + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.1.0.GA"), is(equalTo(RHSSO_VERSION_7_1_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.1.1.GA"), is(equalTo(RHSSO_VERSION_7_1_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.1.2.GA"), is(equalTo(RHSSO_VERSION_7_1_KEYCLOAK_VERSION))); + + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.2.0.GA"), is(equalTo(RHSSO_VERSION_7_2_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.2.1.GA"), is(equalTo(RHSSO_VERSION_7_2_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.2.2.GA"), is(equalTo(RHSSO_VERSION_7_2_KEYCLOAK_VERSION))); + + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.0.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.1.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.2.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.10.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); + + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.4.0.GA"), is(equalTo(RHSSO_VERSION_7_4_KEYCLOAK_VERSION))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.4.15.GA"), is(equalTo(RHSSO_VERSION_7_4_KEYCLOAK_VERSION))); + + // check the conversion doesn't change version for keycloak + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.0"), is(nullValue())); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("8.0.0"), is(nullValue())); + + // check for CD releases + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("6"), is(equalTo(new ModelVersion("6.0.0")))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7"), is(equalTo(new ModelVersion("7.0.0")))); + Assert.assertThat(convertRHSSOVersionToKeycloakVersion("10"), is(equalTo(new ModelVersion("10.0.0")))); + } +} diff --git a/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java b/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java index b41c7db329..ba8991ceb1 100644 --- a/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java +++ b/model/legacy-services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java @@ -25,7 +25,7 @@ import org.keycloak.events.admin.OperationType; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.services.ServicesLogger; -import org.keycloak.services.managers.UserStorageSyncManager; +import org.keycloak.storage.managers.UserStorageSyncManager; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProvider.java b/model/legacy/src/main/java/org/keycloak/storage/UserStorageProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/storage/UserStorageProvider.java rename to model/legacy/src/main/java/org/keycloak/storage/UserStorageProvider.java diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java b/model/legacy/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java rename to model/legacy/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java b/model/legacy/src/main/java/org/keycloak/storage/UserStorageProviderModel.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java rename to model/legacy/src/main/java/org/keycloak/storage/UserStorageProviderModel.java diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java b/model/legacy/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java rename to model/legacy/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java diff --git a/model/legacy/src/main/java/org/keycloak/storage/UserStorageUtil.java b/model/legacy/src/main/java/org/keycloak/storage/UserStorageUtil.java new file mode 100644 index 0000000000..51eb6069a3 --- /dev/null +++ b/model/legacy/src/main/java/org/keycloak/storage/UserStorageUtil.java @@ -0,0 +1,21 @@ +package org.keycloak.storage; + +import org.keycloak.models.RealmModel; + +import java.util.stream.Stream; + +/** + * @author Alexander Schwartz + */ +public class UserStorageUtil { + /** + * Returns sorted {@link UserStorageProviderModel UserStorageProviderModel} as a stream. + * It should be used with forEachOrdered if the ordering is required. + * @return Sorted stream of {@link UserStorageProviderModel}. Never returns {@code null}. + */ + public static Stream getUserStorageProvidersStream(RealmModel realm) { + return realm.getComponentsStream(realm.getId(), UserStorageProvider.class.getName()) + .map(UserStorageProviderModel::new) + .sorted(UserStorageProviderModel.comparator); + } +} diff --git a/server-spi/src/main/java/org/keycloak/storage/user/ImportSynchronization.java b/model/legacy/src/main/java/org/keycloak/storage/user/ImportSynchronization.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/storage/user/ImportSynchronization.java rename to model/legacy/src/main/java/org/keycloak/storage/user/ImportSynchronization.java diff --git a/model/map-ldap/src/main/java/org/keycloak/models/map/storage/ldap/config/LdapMapConfig.java b/model/map-ldap/src/main/java/org/keycloak/models/map/storage/ldap/config/LdapMapConfig.java index ad5f0248aa..4490981172 100644 --- a/model/map-ldap/src/main/java/org/keycloak/models/map/storage/ldap/config/LdapMapConfig.java +++ b/model/map-ldap/src/main/java/org/keycloak/models/map/storage/ldap/config/LdapMapConfig.java @@ -21,7 +21,6 @@ import org.keycloak.Config; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.LDAPConstants; import org.keycloak.models.map.storage.ldap.role.config.LdapMapRoleMapperConfig; -import org.keycloak.storage.UserStorageProvider; import javax.naming.directory.SearchControls; import java.util.Collection; @@ -271,15 +270,6 @@ public class LdapMapConfig { return Boolean.parseBoolean(config.getFirst(LDAPConstants.START_TLS)); } - public UserStorageProvider.EditMode getEditMode() { - String editModeString = config.getFirst(LDAPConstants.EDIT_MODE); - if (editModeString == null) { - return UserStorageProvider.EditMode.READ_ONLY; - } else { - return UserStorageProvider.EditMode.valueOf(editModeString); - } - } - public void addBinaryAttribute(String attrName) { binaryAttributeNames.add(attrName); } diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java index 04f304a02f..ca8c76e7dc 100644 --- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java @@ -48,7 +48,6 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.storage.StorageId; -import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.client.ClientStorageProvider; import java.util.Collection; @@ -456,9 +455,6 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor public void preRemove(RealmModel realm, ComponentModel component) { String componentId = component.getId(); LOG.tracef("preRemove[ComponentModel](%s, %s)%s", realm, componentId, getShortStackTrace()); - if (component.getProviderType().equals(UserStorageProvider.class.getName())) { - removeImportedUsers(realm, componentId); - } if (component.getProviderType().equals(ClientStorageProvider.class.getName())) { DefaultModelCriteria mcb = criteria(); mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) diff --git a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java index eb9eedb8a4..07a334f8ef 100755 --- a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -17,169 +17,25 @@ package org.keycloak.migration; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.regex.Pattern; -import org.jboss.logging.Logger; -import org.keycloak.common.Version; -import org.keycloak.migration.migrators.MigrateTo12_0_0; -import org.keycloak.migration.migrators.MigrateTo14_0_0; -import org.keycloak.migration.migrators.MigrateTo18_0_0; -import org.keycloak.migration.migrators.MigrateTo1_2_0; -import org.keycloak.migration.migrators.MigrateTo1_3_0; -import org.keycloak.migration.migrators.MigrateTo1_4_0; -import org.keycloak.migration.migrators.MigrateTo1_5_0; -import org.keycloak.migration.migrators.MigrateTo1_6_0; -import org.keycloak.migration.migrators.MigrateTo1_7_0; -import org.keycloak.migration.migrators.MigrateTo1_8_0; -import org.keycloak.migration.migrators.MigrateTo1_9_0; -import org.keycloak.migration.migrators.MigrateTo1_9_2; -import org.keycloak.migration.migrators.MigrateTo2_0_0; -import org.keycloak.migration.migrators.MigrateTo2_1_0; -import org.keycloak.migration.migrators.MigrateTo2_2_0; -import org.keycloak.migration.migrators.MigrateTo2_3_0; -import org.keycloak.migration.migrators.MigrateTo2_5_0; -import org.keycloak.migration.migrators.MigrateTo3_0_0; -import org.keycloak.migration.migrators.MigrateTo3_1_0; -import org.keycloak.migration.migrators.MigrateTo3_2_0; -import org.keycloak.migration.migrators.MigrateTo3_4_0; -import org.keycloak.migration.migrators.MigrateTo3_4_1; -import org.keycloak.migration.migrators.MigrateTo3_4_2; -import org.keycloak.migration.migrators.MigrateTo4_0_0; -import org.keycloak.migration.migrators.MigrateTo4_2_0; -import org.keycloak.migration.migrators.MigrateTo4_6_0; -import org.keycloak.migration.migrators.MigrateTo6_0_0; -import org.keycloak.migration.migrators.MigrateTo8_0_0; -import org.keycloak.migration.migrators.MigrateTo8_0_2; -import org.keycloak.migration.migrators.MigrateTo9_0_0; -import org.keycloak.migration.migrators.MigrateTo9_0_4; -import org.keycloak.migration.migrators.Migration; -import org.keycloak.models.Constants; -import org.keycloak.models.DeploymentStateProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.storage.DatastoreProvider; /** * @author Bill Burke * @version $Revision: 1 $ */ +@Deprecated // as this will be moved to a legacy module public class MigrationModelManager { - private static Logger logger = Logger.getLogger(MigrationModelManager.class); - private static final Migration[] migrations = { - new MigrateTo1_2_0(), - new MigrateTo1_3_0(), - new MigrateTo1_4_0(), - new MigrateTo1_5_0(), - new MigrateTo1_6_0(), - new MigrateTo1_7_0(), - new MigrateTo1_8_0(), - new MigrateTo1_9_0(), - new MigrateTo1_9_2(), - new MigrateTo2_0_0(), - new MigrateTo2_1_0(), - new MigrateTo2_2_0(), - new MigrateTo2_3_0(), - new MigrateTo2_5_0(), - new MigrateTo3_0_0(), - new MigrateTo3_1_0(), - new MigrateTo3_2_0(), - new MigrateTo3_4_0(), - new MigrateTo3_4_1(), - new MigrateTo3_4_2(), - new MigrateTo4_0_0(), - new MigrateTo4_2_0(), - new MigrateTo4_6_0(), - new MigrateTo6_0_0(), - new MigrateTo8_0_0(), - new MigrateTo8_0_2(), - new MigrateTo9_0_0(), - new MigrateTo9_0_4(), - new MigrateTo12_0_0(), - new MigrateTo14_0_0(), - new MigrateTo18_0_0() - }; public static void migrate(KeycloakSession session) { - session.setAttribute(Constants.STORAGE_BATCH_ENABLED, Boolean.getBoolean("keycloak.migration.batch-enabled")); - session.setAttribute(Constants.STORAGE_BATCH_SIZE, Integer.getInteger("keycloak.migration.batch-size")); - MigrationModel model = session.getProvider(DeploymentStateProvider.class).getMigrationModel(); - - ModelVersion currentVersion = new ModelVersion(Version.VERSION_KEYCLOAK); - ModelVersion latestUpdate = migrations[migrations.length-1].getVersion(); - ModelVersion databaseVersion = model.getStoredVersion() != null ? new ModelVersion(model.getStoredVersion()) : null; - - if (databaseVersion == null || databaseVersion.lessThan(latestUpdate)) { - for (Migration m : migrations) { - if (databaseVersion == null || databaseVersion.lessThan(m.getVersion())) { - if (databaseVersion != null) { - logger.debugf("Migrating older model to %s", m.getVersion()); - } - m.migrate(session); - } - } - } - - if (databaseVersion == null || databaseVersion.lessThan(currentVersion)) { - model.setStoredVersion(currentVersion.toString()); - } - - Version.RESOURCES_VERSION = model.getResourcesTag(); - } - - public static final ModelVersion RHSSO_VERSION_7_0_KEYCLOAK_VERSION = new ModelVersion("1.9.8"); - public static final ModelVersion RHSSO_VERSION_7_1_KEYCLOAK_VERSION = new ModelVersion("2.5.5"); - public static final ModelVersion RHSSO_VERSION_7_2_KEYCLOAK_VERSION = new ModelVersion("3.4.3"); - public static final ModelVersion RHSSO_VERSION_7_3_KEYCLOAK_VERSION = new ModelVersion("4.8.3"); - public static final ModelVersion RHSSO_VERSION_7_4_KEYCLOAK_VERSION = new ModelVersion("9.0.3"); - - private static final Map PATTERN_MATCHER = new LinkedHashMap<>(); - static { - PATTERN_MATCHER.put(Pattern.compile("^7\\.0\\.\\d+\\.GA$"), RHSSO_VERSION_7_0_KEYCLOAK_VERSION); - PATTERN_MATCHER.put(Pattern.compile("^7\\.1\\.\\d+\\.GA$"), RHSSO_VERSION_7_1_KEYCLOAK_VERSION); - PATTERN_MATCHER.put(Pattern.compile("^7\\.2\\.\\d+\\.GA$"), RHSSO_VERSION_7_2_KEYCLOAK_VERSION); - PATTERN_MATCHER.put(Pattern.compile("^7\\.3\\.\\d+\\.GA$"), RHSSO_VERSION_7_3_KEYCLOAK_VERSION); - PATTERN_MATCHER.put(Pattern.compile("^7\\.4\\.\\d+\\.GA$"), RHSSO_VERSION_7_4_KEYCLOAK_VERSION); + session.getProvider(DatastoreProvider.class).getMigrationManager().migrate(); } public static void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) { - ModelVersion stored = null; - if (rep.getKeycloakVersion() != null) { - stored = convertRHSSOVersionToKeycloakVersion(rep.getKeycloakVersion()); - if (stored == null) { - stored = new ModelVersion(rep.getKeycloakVersion()); - } - } - if (stored == null) { - stored = migrations[0].getVersion(); - } - - for (Migration m : migrations) { - if (stored == null || stored.lessThan(m.getVersion())) { - if (stored != null) { - logger.debugf("Migrating older json representation to %s", m.getVersion()); - } - try { - m.migrateImport(session, realm, rep, skipUserDependent); - } catch (Exception e) { - logger.error("Failed to migrate json representation for version: " + m.getVersion(), e); - } - } - } + session.getProvider(DatastoreProvider.class).getMigrationManager().migrate(realm, rep, skipUserDependent); } - public static ModelVersion convertRHSSOVersionToKeycloakVersion(String version) { - // look for the keycloakVersion pattern to identify it as RH SSO - for (Pattern pattern : PATTERN_MATCHER.keySet()) { - if (pattern.matcher(version).find()) { - return PATTERN_MATCHER.get(pattern); - } - } - // chceck if the version is in format for CD releases, e.g.: "keycloakVersion": "6" - if (Pattern.compile("^[0-9]*$").matcher(version).find()) { - return new ModelVersion(Integer.parseInt(version), 0, 0); - } - return null; - } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index d7d3faec05..4d286e67f0 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -45,7 +45,6 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.ScopeContainerModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.CertificateRepresentation; -import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.transaction.JtaTransactionManagerLookup; import javax.crypto.spec.SecretKeySpec; @@ -392,24 +391,6 @@ public final class KeycloakModelUtils { // USER FEDERATION RELATED STUFF - public static UserStorageProviderModel findUserStorageProviderByName(String displayName, RealmModel realm) { - if (displayName == null) { - return null; - } - - return realm.getUserStorageProvidersStream() - .filter(fedProvider -> Objects.equals(fedProvider.getName(), displayName)) - .findFirst() - .orElse(null); - } - - public static UserStorageProviderModel findUserStorageProviderById(String fedProviderId, RealmModel realm) { - return realm.getUserStorageProvidersStream() - .filter(fedProvider -> Objects.equals(fedProvider.getId(), fedProviderId)) - .findFirst() - .orElse(null); - } - public static ComponentModel createComponentModel(String name, String parentId, String providerId, String providerType, String... config) { ComponentModel mapperModel = new ComponentModel(); mapperModel.setParentId(parentId); 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 6ee852b118..37ae6549cb 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 @@ -19,7 +19,6 @@ package org.keycloak.models.utils; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -54,21 +53,14 @@ import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.broker.provider.IdentityProviderFactory; import org.keycloak.broker.social.SocialIdentityProvider; import org.keycloak.common.Profile; -import org.keycloak.common.enums.SslRequired; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.UriUtils; import org.keycloak.component.ComponentModel; import org.keycloak.credential.CredentialModel; -import org.keycloak.keys.KeyProvider; -import org.keycloak.migration.MigrationProvider; -import org.keycloak.migration.migrators.MigrateTo8_0_0; import org.keycloak.migration.migrators.MigrationUtils; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.AuthenticatorConfigModel; -import org.keycloak.models.BrowserSecurityHeaders; -import org.keycloak.models.CibaConfig; -import org.keycloak.models.ClaimMask; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.Constants; @@ -77,22 +69,14 @@ import org.keycloak.models.GroupModel; import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.LDAPConstants; import org.keycloak.models.ModelException; -import org.keycloak.models.OAuth2DeviceConfig; -import org.keycloak.models.OTPPolicy; -import org.keycloak.models.ParConfig; -import org.keycloak.models.PasswordPolicy; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; -import org.keycloak.models.RequiredActionProviderModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.ScopeContainerModel; import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; -import org.keycloak.models.WebAuthnPolicy; import org.keycloak.models.credential.OTPCredentialModel; import org.keycloak.models.credential.PasswordCredentialModel; import org.keycloak.models.credential.dto.OTPCredentialData; @@ -100,33 +84,23 @@ import org.keycloak.models.credential.dto.OTPSecretData; import org.keycloak.models.credential.dto.PasswordCredentialData; import org.keycloak.policy.PasswordPolicyNotMetException; import org.keycloak.provider.ProviderConfigProperty; -import org.keycloak.representations.idm.ApplicationRepresentation; -import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation; import org.keycloak.representations.idm.AuthenticationExecutionRepresentation; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.representations.idm.AuthenticatorConfigRepresentation; -import org.keycloak.representations.idm.ClaimRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientScopeRepresentation; -import org.keycloak.representations.idm.ClientTemplateRepresentation; -import org.keycloak.representations.idm.ComponentExportRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; -import org.keycloak.representations.idm.OAuthClientRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.representations.idm.RequiredActionProviderRepresentation; 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.UserConsentRepresentation; -import org.keycloak.representations.idm.UserFederationMapperRepresentation; -import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; import org.keycloak.representations.idm.authorization.DecisionStrategy; @@ -137,12 +111,9 @@ import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentatio import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; -import org.keycloak.storage.UserStorageProvider; -import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.DatastoreProvider; import org.keycloak.storage.federated.UserFederatedStorageProvider; -import org.keycloak.userprofile.UserProfileProvider; import org.keycloak.util.JsonSerialization; -import org.keycloak.validation.ValidationUtil; import static org.keycloak.protocol.saml.util.ArtifactBindingUtils.computeArtifactBindingIdentifierString; @@ -151,479 +122,9 @@ public class RepresentationToModel { private static Logger logger = Logger.getLogger(RepresentationToModel.class); public static final String OIDC = "openid-connect"; - public static OTPPolicy toPolicy(RealmRepresentation rep) { - OTPPolicy policy = new OTPPolicy(); - if (rep.getOtpPolicyType() != null) policy.setType(rep.getOtpPolicyType()); - if (rep.getOtpPolicyLookAheadWindow() != null) policy.setLookAheadWindow(rep.getOtpPolicyLookAheadWindow()); - if (rep.getOtpPolicyInitialCounter() != null) policy.setInitialCounter(rep.getOtpPolicyInitialCounter()); - if (rep.getOtpPolicyAlgorithm() != null) policy.setAlgorithm(rep.getOtpPolicyAlgorithm()); - if (rep.getOtpPolicyDigits() != null) policy.setDigits(rep.getOtpPolicyDigits()); - if (rep.getOtpPolicyPeriod() != null) policy.setPeriod(rep.getOtpPolicyPeriod()); - return policy; - - } public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) { - convertDeprecatedSocialProviders(rep); - convertDeprecatedApplications(session, rep); - convertDeprecatedClientTemplates(rep); - - newRealm.setName(rep.getRealm()); - if (rep.getDisplayName() != null) newRealm.setDisplayName(rep.getDisplayName()); - if (rep.getDisplayNameHtml() != null) newRealm.setDisplayNameHtml(rep.getDisplayNameHtml()); - if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled()); - if (rep.isUserManagedAccessAllowed() != null) newRealm.setUserManagedAccessAllowed(rep.isUserManagedAccessAllowed()); - if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected()); - if (rep.isPermanentLockout() != null) newRealm.setPermanentLockout(rep.isPermanentLockout()); - 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.isEventsEnabled() != null) newRealm.setEventsEnabled(rep.isEventsEnabled()); - if (rep.getEnabledEventTypes() != null) - newRealm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes())); - if (rep.getEventsExpiration() != null) newRealm.setEventsExpiration(rep.getEventsExpiration()); - if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<>(rep.getEventsListeners())); - if (rep.isAdminEventsEnabled() != null) newRealm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); - if (rep.isAdminEventsDetailsEnabled() != null) - newRealm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); - - if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore()); - - if (rep.getDefaultSignatureAlgorithm() != null) newRealm.setDefaultSignatureAlgorithm(rep.getDefaultSignatureAlgorithm()); - else newRealm.setDefaultSignatureAlgorithm(Constants.DEFAULT_SIGNATURE_ALGORITHM); - - if (rep.getRevokeRefreshToken() != null) newRealm.setRevokeRefreshToken(rep.getRevokeRefreshToken()); - else newRealm.setRevokeRefreshToken(false); - - if (rep.getRefreshTokenMaxReuse() != null) newRealm.setRefreshTokenMaxReuse(rep.getRefreshTokenMaxReuse()); - else newRealm.setRefreshTokenMaxReuse(0); - - if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); - else newRealm.setAccessTokenLifespan(300); - - if (rep.getAccessTokenLifespanForImplicitFlow() != null) - newRealm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow()); - else - newRealm.setAccessTokenLifespanForImplicitFlow(Constants.DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT); - - if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); - else newRealm.setSsoSessionIdleTimeout(1800); - if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); - else newRealm.setSsoSessionMaxLifespan(36000); - if (rep.getSsoSessionMaxLifespanRememberMe() != null) newRealm.setSsoSessionMaxLifespanRememberMe(rep.getSsoSessionMaxLifespanRememberMe()); - if (rep.getSsoSessionIdleTimeoutRememberMe() != null) newRealm.setSsoSessionIdleTimeoutRememberMe(rep.getSsoSessionIdleTimeoutRememberMe()); - if (rep.getOfflineSessionIdleTimeout() != null) - newRealm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout()); - else newRealm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT); - - // KEYCLOAK-7688 Offline Session Max for Offline Token - if (rep.getOfflineSessionMaxLifespanEnabled() != null) newRealm.setOfflineSessionMaxLifespanEnabled(rep.getOfflineSessionMaxLifespanEnabled()); - else newRealm.setOfflineSessionMaxLifespanEnabled(false); - - if (rep.getOfflineSessionMaxLifespan() != null) - newRealm.setOfflineSessionMaxLifespan(rep.getOfflineSessionMaxLifespan()); - else newRealm.setOfflineSessionMaxLifespan(Constants.DEFAULT_OFFLINE_SESSION_MAX_LIFESPAN); - - if (rep.getClientSessionIdleTimeout() != null) - newRealm.setClientSessionIdleTimeout(rep.getClientSessionIdleTimeout()); - if (rep.getClientSessionMaxLifespan() != null) - newRealm.setClientSessionMaxLifespan(rep.getClientSessionMaxLifespan()); - - if (rep.getClientOfflineSessionIdleTimeout() != null) - newRealm.setClientOfflineSessionIdleTimeout(rep.getClientOfflineSessionIdleTimeout()); - if (rep.getClientOfflineSessionMaxLifespan() != null) - newRealm.setClientOfflineSessionMaxLifespan(rep.getClientOfflineSessionMaxLifespan()); - - 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.getAccessCodeLifespanLogin() != null) - newRealm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin()); - else newRealm.setAccessCodeLifespanLogin(1800); - - if (rep.getActionTokenGeneratedByAdminLifespan() != null) - newRealm.setActionTokenGeneratedByAdminLifespan(rep.getActionTokenGeneratedByAdminLifespan()); - else newRealm.setActionTokenGeneratedByAdminLifespan(12 * 60 * 60); - - if (rep.getActionTokenGeneratedByUserLifespan() != null) - newRealm.setActionTokenGeneratedByUserLifespan(rep.getActionTokenGeneratedByUserLifespan()); - else newRealm.setActionTokenGeneratedByUserLifespan(newRealm.getAccessCodeLifespanUserAction()); - - // OAuth 2.0 Device Authorization Grant - OAuth2DeviceConfig deviceConfig = newRealm.getOAuth2DeviceConfig(); - - deviceConfig.setOAuth2DeviceCodeLifespan(rep.getOAuth2DeviceCodeLifespan()); - deviceConfig.setOAuth2DevicePollingInterval(rep.getOAuth2DevicePollingInterval()); - - if (rep.getSslRequired() != null) - newRealm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase())); - if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); - if (rep.isRegistrationEmailAsUsername() != null) - newRealm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername()); - if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe()); - if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail()); - if (rep.isLoginWithEmailAllowed() != null) newRealm.setLoginWithEmailAllowed(rep.isLoginWithEmailAllowed()); - if (rep.isDuplicateEmailsAllowed() != null) newRealm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed()); - if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); - if (rep.isEditUsernameAllowed() != null) newRealm.setEditUsernameAllowed(rep.isEditUsernameAllowed()); - 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()); - - // todo remove this stuff as its all deprecated - if (rep.getRequiredCredentials() != null) { - for (String requiredCred : rep.getRequiredCredentials()) { - newRealm.addRequiredCredential(requiredCred); - } - } else { - newRealm.addRequiredCredential(CredentialRepresentation.PASSWORD); - } - - if (rep.getPasswordPolicy() != null) - newRealm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy())); - if (rep.getOtpPolicyType() != null) newRealm.setOTPPolicy(toPolicy(rep)); - else newRealm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY); - - WebAuthnPolicy webAuthnPolicy = getWebAuthnPolicyTwoFactor(rep); - newRealm.setWebAuthnPolicy(webAuthnPolicy); - - webAuthnPolicy = getWebAuthnPolicyPasswordless(rep); - newRealm.setWebAuthnPolicyPasswordless(webAuthnPolicy); - - updateCibaSettings(rep, newRealm); - - updateParSettings(rep, newRealm); - - Map mappedFlows = importAuthenticationFlows(newRealm, rep); - if (rep.getRequiredActions() != null) { - for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) { - RequiredActionProviderModel model = toModel(action); - - MigrationUtils.updateOTPRequiredAction(model); - - newRealm.addRequiredActionProvider(model); - } - DefaultRequiredActions.addDeleteAccountAction(newRealm); - } else { - DefaultRequiredActions.addActions(newRealm); - } - - importIdentityProviders(rep, newRealm, session); - importIdentityProviderMappers(rep, newRealm); - - Map clientScopes = new HashMap<>(); - if (rep.getClientScopes() != null) { - clientScopes = createClientScopes(session, rep.getClientScopes(), newRealm); - } - if (rep.getDefaultDefaultClientScopes() != null) { - for (String clientScopeName : rep.getDefaultDefaultClientScopes()) { - ClientScopeModel clientScope = clientScopes.get(clientScopeName); - if (clientScope != null) { - newRealm.addDefaultClientScope(clientScope, true); - } else { - logger.warnf("Referenced client scope '%s' doesn't exist", clientScopeName); - } - } - } - if (rep.getDefaultOptionalClientScopes() != null) { - for (String clientScopeName : rep.getDefaultOptionalClientScopes()) { - ClientScopeModel clientScope = clientScopes.get(clientScopeName); - if (clientScope != null) { - newRealm.addDefaultClientScope(clientScope, false); - } else { - logger.warnf("Referenced client scope '%s' doesn't exist", clientScopeName); - } - } - } - - Map createdClients = new HashMap<>(); - if (rep.getClients() != null) { - createdClients = createClients(session, rep, newRealm, mappedFlows); - } - - importRoles(rep.getRoles(), newRealm); - convertDeprecatedDefaultRoles(rep, newRealm); - - // Now that all possible roles and clients are created, create scope mappings - - if (rep.getClientScopeMappings() != null) { - - for (Map.Entry> entry : rep.getClientScopeMappings().entrySet()) { - ClientModel app = createdClients.computeIfAbsent(entry.getKey(), k -> newRealm.getClientByClientId(entry.getKey())); - if (app == null) { - throw new RuntimeException("Unable to find client role mappings for client: " + entry.getKey()); - } - createClientScopeMappings(newRealm, app, entry.getValue()); - } - } - - if (rep.getScopeMappings() != null) { - Map roleModelMap = newRealm.getRolesStream().collect(Collectors.toMap(RoleModel::getId, Function.identity())); - - for (ScopeMappingRepresentation scope : rep.getScopeMappings()) { - ScopeContainerModel scopeContainer = getScopeContainerHavingScope(newRealm, scope); - for (String roleString : scope.getRoles()) { - final String roleStringTrimmed = roleString.trim(); - RoleModel role = roleModelMap.computeIfAbsent(roleStringTrimmed, k -> newRealm.getRole(roleStringTrimmed)); - if (role == null) { - role = newRealm.addRole(roleString); - roleModelMap.put(role.getId(), role); - } - scopeContainer.addScopeMapping(role); - } - } - } - - if (rep.getSmtpServer() != null) { - newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer())); - } - - if (rep.getBrowserSecurityHeaders() != null) { - newRealm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders()); - } else { - newRealm.setBrowserSecurityHeaders(BrowserSecurityHeaders.realmDefaultHeaders); - } - - if (rep.getComponents() != null) { - MultivaluedHashMap components = rep.getComponents(); - String parentId = newRealm.getId(); - importComponents(newRealm, components, parentId); - } - importUserFederationProvidersAndMappers(session, rep, newRealm); - - - if (rep.getGroups() != null) { - importGroups(newRealm, rep); - if (rep.getDefaultGroups() != null) { - for (String path : rep.getDefaultGroups()) { - GroupModel found = KeycloakModelUtils.findGroupByPath(newRealm, path); - if (found == null) throw new RuntimeException("default group in realm rep doesn't exist: " + path); - newRealm.addDefaultGroup(found); - } - } - } - - - // create users and their role mappings and social mappings - - if (rep.getUsers() != null) { - for (UserRepresentation userRep : rep.getUsers()) { - createUser(session, newRealm, userRep); - } - } - - if (rep.getFederatedUsers() != null) { - for (UserRepresentation userRep : rep.getFederatedUsers()) { - importFederatedUser(session, newRealm, userRep); - } - } - - if (!skipUserDependent) { - importRealmAuthorizationSettings(rep, newRealm, session); - } - - if (rep.isInternationalizationEnabled() != null) { - newRealm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); - } - if (rep.getSupportedLocales() != null) { - newRealm.setSupportedLocales(new HashSet(rep.getSupportedLocales())); - } - if (rep.getDefaultLocale() != null) { - newRealm.setDefaultLocale(rep.getDefaultLocale()); - } - - // import attributes - - if (rep.getAttributes() != null) { - for (Map.Entry attr : rep.getAttributes().entrySet()) { - newRealm.setAttribute(attr.getKey(), attr.getValue()); - } - } - - if (newRealm.getComponentsStream(newRealm.getId(), KeyProvider.class.getName()).count() == 0) { - if (rep.getPrivateKey() != null) { - DefaultKeyProviders.createProviders(newRealm, rep.getPrivateKey(), rep.getCertificate()); - } else { - DefaultKeyProviders.createProviders(newRealm); - } - } - } - - - private static WebAuthnPolicy getWebAuthnPolicyTwoFactor(RealmRepresentation rep) { - WebAuthnPolicy webAuthnPolicy = new WebAuthnPolicy(); - - String webAuthnPolicyRpEntityName = rep.getWebAuthnPolicyRpEntityName(); - if (webAuthnPolicyRpEntityName == null || webAuthnPolicyRpEntityName.isEmpty()) - webAuthnPolicyRpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME; - webAuthnPolicy.setRpEntityName(webAuthnPolicyRpEntityName); - - List webAuthnPolicySignatureAlgorithms = rep.getWebAuthnPolicySignatureAlgorithms(); - if (webAuthnPolicySignatureAlgorithms == null || webAuthnPolicySignatureAlgorithms.isEmpty()) - webAuthnPolicySignatureAlgorithms = Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(",")); - webAuthnPolicy.setSignatureAlgorithm(webAuthnPolicySignatureAlgorithms); - - String webAuthnPolicyRpId = rep.getWebAuthnPolicyRpId(); - if (webAuthnPolicyRpId == null || webAuthnPolicyRpId.isEmpty()) - webAuthnPolicyRpId = ""; - webAuthnPolicy.setRpId(webAuthnPolicyRpId); - - String webAuthnPolicyAttestationConveyancePreference = rep.getWebAuthnPolicyAttestationConveyancePreference(); - if (webAuthnPolicyAttestationConveyancePreference == null || webAuthnPolicyAttestationConveyancePreference.isEmpty()) - webAuthnPolicyAttestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setAttestationConveyancePreference(webAuthnPolicyAttestationConveyancePreference); - - String webAuthnPolicyAuthenticatorAttachment = rep.getWebAuthnPolicyAuthenticatorAttachment(); - if (webAuthnPolicyAuthenticatorAttachment == null || webAuthnPolicyAuthenticatorAttachment.isEmpty()) - webAuthnPolicyAuthenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setAuthenticatorAttachment(webAuthnPolicyAuthenticatorAttachment); - - String webAuthnPolicyRequireResidentKey = rep.getWebAuthnPolicyRequireResidentKey(); - if (webAuthnPolicyRequireResidentKey == null || webAuthnPolicyRequireResidentKey.isEmpty()) - webAuthnPolicyRequireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setRequireResidentKey(webAuthnPolicyRequireResidentKey); - - String webAuthnPolicyUserVerificationRequirement = rep.getWebAuthnPolicyUserVerificationRequirement(); - if (webAuthnPolicyUserVerificationRequirement == null || webAuthnPolicyUserVerificationRequirement.isEmpty()) - webAuthnPolicyUserVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setUserVerificationRequirement(webAuthnPolicyUserVerificationRequirement); - - Integer webAuthnPolicyCreateTimeout = rep.getWebAuthnPolicyCreateTimeout(); - if (webAuthnPolicyCreateTimeout != null) webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout); - else webAuthnPolicy.setCreateTimeout(0); - - Boolean webAuthnPolicyAvoidSameAuthenticatorRegister = rep.isWebAuthnPolicyAvoidSameAuthenticatorRegister(); - if (webAuthnPolicyAvoidSameAuthenticatorRegister != null) webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister); - - List webAuthnPolicyAcceptableAaguids = rep.getWebAuthnPolicyAcceptableAaguids(); - if (webAuthnPolicyAcceptableAaguids != null) webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids); - - return webAuthnPolicy; - } - - - private static WebAuthnPolicy getWebAuthnPolicyPasswordless(RealmRepresentation rep) { - WebAuthnPolicy webAuthnPolicy = new WebAuthnPolicy(); - - String webAuthnPolicyRpEntityName = rep.getWebAuthnPolicyPasswordlessRpEntityName(); - if (webAuthnPolicyRpEntityName == null || webAuthnPolicyRpEntityName.isEmpty()) - webAuthnPolicyRpEntityName = Constants.DEFAULT_WEBAUTHN_POLICY_RP_ENTITY_NAME; - webAuthnPolicy.setRpEntityName(webAuthnPolicyRpEntityName); - - List webAuthnPolicySignatureAlgorithms = rep.getWebAuthnPolicyPasswordlessSignatureAlgorithms(); - if (webAuthnPolicySignatureAlgorithms == null || webAuthnPolicySignatureAlgorithms.isEmpty()) - webAuthnPolicySignatureAlgorithms = Arrays.asList(Constants.DEFAULT_WEBAUTHN_POLICY_SIGNATURE_ALGORITHMS.split(",")); - webAuthnPolicy.setSignatureAlgorithm(webAuthnPolicySignatureAlgorithms); - - String webAuthnPolicyRpId = rep.getWebAuthnPolicyPasswordlessRpId(); - if (webAuthnPolicyRpId == null || webAuthnPolicyRpId.isEmpty()) - webAuthnPolicyRpId = ""; - webAuthnPolicy.setRpId(webAuthnPolicyRpId); - - String webAuthnPolicyAttestationConveyancePreference = rep.getWebAuthnPolicyPasswordlessAttestationConveyancePreference(); - if (webAuthnPolicyAttestationConveyancePreference == null || webAuthnPolicyAttestationConveyancePreference.isEmpty()) - webAuthnPolicyAttestationConveyancePreference = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setAttestationConveyancePreference(webAuthnPolicyAttestationConveyancePreference); - - String webAuthnPolicyAuthenticatorAttachment = rep.getWebAuthnPolicyPasswordlessAuthenticatorAttachment(); - if (webAuthnPolicyAuthenticatorAttachment == null || webAuthnPolicyAuthenticatorAttachment.isEmpty()) - webAuthnPolicyAuthenticatorAttachment = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setAuthenticatorAttachment(webAuthnPolicyAuthenticatorAttachment); - - String webAuthnPolicyRequireResidentKey = rep.getWebAuthnPolicyPasswordlessRequireResidentKey(); - if (webAuthnPolicyRequireResidentKey == null || webAuthnPolicyRequireResidentKey.isEmpty()) - webAuthnPolicyRequireResidentKey = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setRequireResidentKey(webAuthnPolicyRequireResidentKey); - - String webAuthnPolicyUserVerificationRequirement = rep.getWebAuthnPolicyPasswordlessUserVerificationRequirement(); - if (webAuthnPolicyUserVerificationRequirement == null || webAuthnPolicyUserVerificationRequirement.isEmpty()) - webAuthnPolicyUserVerificationRequirement = Constants.DEFAULT_WEBAUTHN_POLICY_NOT_SPECIFIED; - webAuthnPolicy.setUserVerificationRequirement(webAuthnPolicyUserVerificationRequirement); - - Integer webAuthnPolicyCreateTimeout = rep.getWebAuthnPolicyPasswordlessCreateTimeout(); - if (webAuthnPolicyCreateTimeout != null) webAuthnPolicy.setCreateTimeout(webAuthnPolicyCreateTimeout); - else webAuthnPolicy.setCreateTimeout(0); - - Boolean webAuthnPolicyAvoidSameAuthenticatorRegister = rep.isWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister(); - if (webAuthnPolicyAvoidSameAuthenticatorRegister != null) webAuthnPolicy.setAvoidSameAuthenticatorRegister(webAuthnPolicyAvoidSameAuthenticatorRegister); - - List webAuthnPolicyAcceptableAaguids = rep.getWebAuthnPolicyPasswordlessAcceptableAaguids(); - if (webAuthnPolicyAcceptableAaguids != null) webAuthnPolicy.setAcceptableAaguids(webAuthnPolicyAcceptableAaguids); - - return webAuthnPolicy; - } - - public static void importUserFederationProvidersAndMappers(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm) { - // providers to convert to component model - Set convertSet = new HashSet<>(); - convertSet.add(LDAPConstants.LDAP_PROVIDER); - convertSet.add("kerberos"); - Map mapperConvertSet = new HashMap<>(); - mapperConvertSet.put(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"); - - - Map userStorageModels = new HashMap<>(); - - if (rep.getUserFederationProviders() != null) { - for (UserFederationProviderRepresentation fedRep : rep.getUserFederationProviders()) { - if (convertSet.contains(fedRep.getProviderName())) { - ComponentModel component = convertFedProviderToComponent(newRealm.getId(), fedRep); - userStorageModels.put(fedRep.getDisplayName(), newRealm.importComponentModel(component)); - } - } - } - - // This is for case, when you have hand-written JSON file with LDAP userFederationProvider, but WITHOUT any userFederationMappers configured. Default LDAP mappers need to be created in that case. - Set storageProvidersWhichShouldImportDefaultMappers = new HashSet<>(userStorageModels.keySet()); - - if (rep.getUserFederationMappers() != null) { - for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) { - if (userStorageModels.containsKey(representation.getFederationProviderDisplayName())) { - ComponentModel parent = userStorageModels.get(representation.getFederationProviderDisplayName()); - String newMapperType = mapperConvertSet.get(parent.getProviderId()); - ComponentModel mapper = convertFedMapperToComponent(newRealm, parent, representation, newMapperType); - newRealm.importComponentModel(mapper); - - - storageProvidersWhichShouldImportDefaultMappers.remove(representation.getFederationProviderDisplayName()); - - } - } - } - - for (String providerDisplayName : storageProvidersWhichShouldImportDefaultMappers) { - ComponentUtil.notifyCreated(session, newRealm, userStorageModels.get(providerDisplayName)); - } - } - - protected static void importComponents(RealmModel newRealm, MultivaluedHashMap components, String parentId) { - for (Map.Entry> entry : components.entrySet()) { - String providerType = entry.getKey(); - for (ComponentExportRepresentation compRep : entry.getValue()) { - ComponentModel component = new ComponentModel(); - component.setId(compRep.getId()); - component.setName(compRep.getName()); - component.setConfig(compRep.getConfig()); - component.setProviderType(providerType); - component.setProviderId(compRep.getProviderId()); - component.setSubType(compRep.getSubType()); - component.setParentId(parentId); - component = newRealm.importComponentModel(component); - if (compRep.getSubComponents() != null) { - importComponents(newRealm, compRep.getSubComponents(), component.getId()); - } - } - } + session.getProvider(DatastoreProvider.class).getExportImportManager().importRealm(rep, newRealm, skipUserDependent); } public static void importRoles(RolesRepresentation realmRoles, RealmModel realm) { @@ -673,15 +174,6 @@ public class RepresentationToModel { } } - public static void importGroups(RealmModel realm, RealmRepresentation rep) { - List groups = rep.getGroups(); - if (groups == null) return; - - GroupModel parent = null; - for (GroupRepresentation group : groups) { - importGroup(realm, parent, group); - } - } public static void importGroup(RealmModel realm, GroupModel parent, GroupRepresentation group) { GroupModel newGroup = realm.createGroup(group.getId(), group.getName(), parent); @@ -724,154 +216,6 @@ public class RepresentationToModel { } } - public static Map importAuthenticationFlows(RealmModel newRealm, RealmRepresentation rep) { - Map mappedFlows = new HashMap<>(); - if (rep.getAuthenticationFlows() == null) { - // assume this is an old version being imported - DefaultAuthenticationFlows.migrateFlows(newRealm); - } else { - if (rep.getAuthenticatorConfig() != null) { - for (AuthenticatorConfigRepresentation configRep : rep.getAuthenticatorConfig()) { - if (configRep.getAlias() == null) { - // this can happen only during import json files from keycloak 3.4.0 and older - throw new IllegalStateException("Provided realm contains authenticator config with null alias. " - + "It should be resolved by adding alias to the authenticator config before exporting the realm."); - } - AuthenticatorConfigModel model = toModel(configRep); - newRealm.addAuthenticatorConfig(model); - } - } - if (rep.getAuthenticationFlows() != null) { - for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { - AuthenticationFlowModel model = toModel(flowRep); - // make sure new id is generated for new AuthenticationFlowModel instance - String previousId = model.getId(); - model.setId(null); - model = newRealm.addAuthenticationFlow(model); - // store the mapped ids so that clients can reference the correct flow when importing the authenticationFlowBindingOverrides - mappedFlows.put(previousId, model.getId()); - } - for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { - AuthenticationFlowModel model = newRealm.getFlowByAlias(flowRep.getAlias()); - for (AuthenticationExecutionExportRepresentation exeRep : flowRep.getAuthenticationExecutions()) { - AuthenticationExecutionModel execution = toModel(newRealm, model, exeRep); - newRealm.addAuthenticatorExecution(execution); - } - } - } - } - if (rep.getBrowserFlow() == null) { - AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW); - if (defaultFlow != null) { - newRealm.setBrowserFlow(defaultFlow); - } - } else { - newRealm.setBrowserFlow(newRealm.getFlowByAlias(rep.getBrowserFlow())); - } - if (rep.getRegistrationFlow() == null) { - AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.REGISTRATION_FLOW); - if (defaultFlow != null) { - newRealm.setRegistrationFlow(defaultFlow); - } - } else { - newRealm.setRegistrationFlow(newRealm.getFlowByAlias(rep.getRegistrationFlow())); - } - if (rep.getDirectGrantFlow() == null) { - AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW); - if (defaultFlow != null) { - newRealm.setDirectGrantFlow(defaultFlow); - } - } else { - newRealm.setDirectGrantFlow(newRealm.getFlowByAlias(rep.getDirectGrantFlow())); - } - - // reset credentials + client flow needs to be more defensive as they were added later (in 1.5 ) - if (rep.getResetCredentialsFlow() == null) { - AuthenticationFlowModel resetFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW); - if (resetFlow == null) { - DefaultAuthenticationFlows.resetCredentialsFlow(newRealm); - } else { - newRealm.setResetCredentialsFlow(resetFlow); - } - } else { - newRealm.setResetCredentialsFlow(newRealm.getFlowByAlias(rep.getResetCredentialsFlow())); - } - if (rep.getClientAuthenticationFlow() == null) { - AuthenticationFlowModel clientFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.CLIENT_AUTHENTICATION_FLOW); - if (clientFlow == null) { - DefaultAuthenticationFlows.clientAuthFlow(newRealm); - } else { - newRealm.setClientAuthenticationFlow(clientFlow); - } - } else { - newRealm.setClientAuthenticationFlow(newRealm.getFlowByAlias(rep.getClientAuthenticationFlow())); - } - - // Added in 1.7 - if (newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW) == null) { - DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true); - } - - // Added in 2.2 - String defaultProvider = null; - if (rep.getIdentityProviders() != null) { - for (IdentityProviderRepresentation i : rep.getIdentityProviders()) { - if (i.isEnabled() && i.isAuthenticateByDefault()) { - defaultProvider = i.getProviderId(); - break; - } - } - } - - // Added in 3.2 - if (rep.getDockerAuthenticationFlow() == null) { - AuthenticationFlowModel dockerAuthenticationFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.DOCKER_AUTH); - if (dockerAuthenticationFlow == null) { - DefaultAuthenticationFlows.dockerAuthenticationFlow(newRealm); - } else { - newRealm.setDockerAuthenticationFlow(dockerAuthenticationFlow); - } - } else { - newRealm.setDockerAuthenticationFlow(newRealm.getFlowByAlias(rep.getDockerAuthenticationFlow())); - } - - DefaultAuthenticationFlows.addIdentityProviderAuthenticator(newRealm, defaultProvider); - - return mappedFlows; - } - - private static void convertDeprecatedSocialProviders(RealmRepresentation rep) { - if (rep.isSocial() != null && rep.isSocial() && rep.getSocialProviders() != null && !rep.getSocialProviders().isEmpty() && rep.getIdentityProviders() == null) { - Boolean updateProfileFirstLogin = rep.isUpdateProfileOnInitialSocialLogin() != null && rep.isUpdateProfileOnInitialSocialLogin(); - if (rep.getSocialProviders() != null) { - - logger.warn("Using deprecated 'social' configuration in JSON representation. It will be removed in future versions"); - List identityProviders = new LinkedList<>(); - for (String k : rep.getSocialProviders().keySet()) { - if (k.endsWith(".key")) { - String providerId = k.split("\\.")[0]; - String key = rep.getSocialProviders().get(k); - String secret = rep.getSocialProviders().get(k.replace(".key", ".secret")); - - IdentityProviderRepresentation identityProvider = new IdentityProviderRepresentation(); - identityProvider.setAlias(providerId); - identityProvider.setProviderId(providerId); - identityProvider.setEnabled(true); - identityProvider.setLinkOnly(false); - identityProvider.setUpdateProfileFirstLogin(updateProfileFirstLogin); - - Map config = new HashMap<>(); - config.put("clientId", key); - config.put("clientSecret", secret); - identityProvider.setConfig(config); - - identityProviders.add(identityProvider); - } - } - rep.setIdentityProviders(identityProviders); - } - } - } private static void convertDeprecatedSocialProviders(UserRepresentation user) { if (user.getSocialLinks() != null && !user.getSocialLinks().isEmpty() && user.getFederatedIdentities() == null) { @@ -891,97 +235,6 @@ public class RepresentationToModel { user.setSocialLinks(null); } - private static void convertDeprecatedApplications(KeycloakSession session, RealmRepresentation realm) { - if (realm.getApplications() != null || realm.getOauthClients() != null) { - if (realm.getClients() == null) { - realm.setClients(new LinkedList()); - } - - List clients = new LinkedList<>(); - if (realm.getApplications() != null) { - clients.addAll(realm.getApplications()); - } - if (realm.getOauthClients() != null) { - clients.addAll(realm.getOauthClients()); - } - - for (ApplicationRepresentation app : clients) { - app.setClientId(app.getName()); - app.setName(null); - - if (app instanceof OAuthClientRepresentation) { - app.setConsentRequired(true); - app.setFullScopeAllowed(false); - } - - if (app.getProtocolMappers() == null && app.getClaims() != null) { - long mask = getClaimsMask(app.getClaims()); - List convertedProtocolMappers = session.getProvider(MigrationProvider.class).getMappersForClaimMask(mask); - app.setProtocolMappers(convertedProtocolMappers); - app.setClaims(null); - } - - realm.getClients().add(app); - } - } - - if (realm.getApplicationScopeMappings() != null && realm.getClientScopeMappings() == null) { - realm.setClientScopeMappings(realm.getApplicationScopeMappings()); - } - - if (realm.getRoles() != null && realm.getRoles().getApplication() != null && realm.getRoles().getClient() == null) { - realm.getRoles().setClient(realm.getRoles().getApplication()); - } - - if (realm.getUsers() != null) { - for (UserRepresentation user : realm.getUsers()) { - if (user.getApplicationRoles() != null && user.getClientRoles() == null) { - user.setClientRoles(user.getApplicationRoles()); - } - } - } - - if (realm.getRoles() != null && realm.getRoles().getRealm() != null) { - for (RoleRepresentation role : realm.getRoles().getRealm()) { - if (role.getComposites() != null && role.getComposites().getApplication() != null && role.getComposites().getClient() == null) { - role.getComposites().setClient(role.getComposites().getApplication()); - } - } - } - - if (realm.getRoles() != null && realm.getRoles().getClient() != null) { - for (Map.Entry> clientRoles : realm.getRoles().getClient().entrySet()) { - for (RoleRepresentation role : clientRoles.getValue()) { - if (role.getComposites() != null && role.getComposites().getApplication() != null && role.getComposites().getClient() == null) { - role.getComposites().setClient(role.getComposites().getApplication()); - } - } - } - } - } - - private static void convertDeprecatedClientTemplates(RealmRepresentation realm) { - if (realm.getClientTemplates() != null) { - - logger.warnf("Using deprecated 'clientTemplates' configuration in JSON representation for realm '%s'. It will be removed in future versions", realm.getRealm()); - - List clientScopes = new LinkedList<>(); - for (ClientTemplateRepresentation template : realm.getClientTemplates()) { - ClientScopeRepresentation scopeRep = new ClientScopeRepresentation(); - scopeRep.setId(template.getId()); - scopeRep.setName(template.getName()); - scopeRep.setProtocol(template.getProtocol()); - scopeRep.setDescription(template.getDescription()); - scopeRep.setAttributes(template.getAttributes()); - scopeRep.setProtocolMappers(template.getProtocolMappers()); - - clientScopes.add(scopeRep); - } - - realm.setClientScopes(clientScopes); - } - } - private static void convertDeprecatedCredentialsFormat(UserRepresentation user) { if (user.getCredentials() != null) { for (CredentialRepresentation cred : user.getCredentials()) { @@ -1011,303 +264,16 @@ public class RepresentationToModel { } } - private static void convertDeprecatedDefaultRoles(RealmRepresentation rep, RealmModel newRealm) { - if (rep.getDefaultRole() == null) { - // Setup realm default roles - if (rep.getDefaultRoles() != null) { - rep.getDefaultRoles().stream() - .map(String::trim) - .map(name -> getOrAddRealmRole(newRealm, name)) - .forEach(role -> newRealm.getDefaultRole().addCompositeRole(role)); - } - // Setup client default roles - if (rep.getClients() != null) { - for (ClientRepresentation clientRep : rep.getClients()) { - if (clientRep.getDefaultRoles() != null) { - Arrays.stream(clientRep.getDefaultRoles()) - .map(String::trim) - .map(name -> getOrAddClientRole(newRealm.getClientById(clientRep.getId()), name)) - .forEach(role -> newRealm.getDefaultRole().addCompositeRole(role)); - } - } - } - } - } - - private static RoleModel getOrAddRealmRole(RealmModel realm, String name) { - RoleModel role = realm.getRole(name); - if (role == null) { - role = realm.addRole(name); - } - return role; - } - - private static RoleModel getOrAddClientRole(ClientModel client, String name) { - RoleModel role = client.getRole(name); - if (role == null) { - role = client.addRole(name); - } - return role; - } - - public static void renameRealm(RealmModel realm, String name) { - if (name.equals(realm.getName())) return; - - String oldName = realm.getName(); - - ClientModel masterApp = realm.getMasterAdminClient(); - masterApp.setClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(name)); - realm.setName(name); - - ClientModel adminClient = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID); - if (adminClient != null) { - if (adminClient.getBaseUrl() != null) { - adminClient.setBaseUrl(adminClient.getBaseUrl().replace("/admin/" + oldName + "/", "/admin/" + name + "/")); - } - Set adminRedirectUris = new HashSet<>(); - for (String r : adminClient.getRedirectUris()) { - adminRedirectUris.add(replace(r, "/admin/" + oldName + "/", "/admin/" + name + "/")); - } - adminClient.setRedirectUris(adminRedirectUris); - } - - ClientModel accountClient = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID); - if (accountClient != null) { - if (accountClient.getBaseUrl() != null) { - accountClient.setBaseUrl(accountClient.getBaseUrl().replace("/realms/" + oldName + "/", "/realms/" + name + "/")); - } - Set accountRedirectUris = new HashSet<>(); - for (String r : accountClient.getRedirectUris()) { - accountRedirectUris.add(replace(r, "/realms/" + oldName + "/", "/realms/" + name + "/")); - } - accountClient.setRedirectUris(accountRedirectUris); - } - } - - private static String replace(String url, String target, String replacement) { - return url != null ? url.replace(target, replacement) : null; - } public static void updateRealm(RealmRepresentation rep, RealmModel realm, KeycloakSession session) { - if (rep.getRealm() != null) { - renameRealm(realm, rep.getRealm()); - } + session.getProvider(DatastoreProvider.class).getExportImportManager().updateRealm(rep, realm); - if (!Boolean.parseBoolean(rep.getAttributesOrEmpty().get("userProfileEnabled"))) { - UserProfileProvider provider = session.getProvider(UserProfileProvider.class); - provider.setConfiguration(null); - } - - // Import attributes first, so the stuff saved directly on representation (displayName, bruteForce etc) has bigger priority - if (rep.getAttributes() != null) { - Set attrsToRemove = new HashSet<>(realm.getAttributes().keySet()); - attrsToRemove.removeAll(rep.getAttributes().keySet()); - - for (Map.Entry entry : rep.getAttributes().entrySet()) { - realm.setAttribute(entry.getKey(), entry.getValue()); - } - - for (String attr : attrsToRemove) { - realm.removeAttribute(attr); - } - } - - if (rep.getDisplayName() != null) realm.setDisplayName(rep.getDisplayName()); - if (rep.getDisplayNameHtml() != null) realm.setDisplayNameHtml(rep.getDisplayNameHtml()); - if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); - if (rep.isUserManagedAccessAllowed() != null) realm.setUserManagedAccessAllowed(rep.isUserManagedAccessAllowed()); - if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected()); - if (rep.isPermanentLockout() != null) realm.setPermanentLockout(rep.isPermanentLockout()); - 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.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); - if (rep.isRegistrationEmailAsUsername() != null) - realm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername()); - if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe()); - if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); - if (rep.isLoginWithEmailAllowed() != null) realm.setLoginWithEmailAllowed(rep.isLoginWithEmailAllowed()); - if (rep.isDuplicateEmailsAllowed() != null) realm.setDuplicateEmailsAllowed(rep.isDuplicateEmailsAllowed()); - if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); - if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed()); - if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase())); - if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); - if (rep.getAccessCodeLifespanUserAction() != null) - realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); - if (rep.getAccessCodeLifespanLogin() != null) - realm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin()); - if (rep.getActionTokenGeneratedByAdminLifespan() != null) - realm.setActionTokenGeneratedByAdminLifespan(rep.getActionTokenGeneratedByAdminLifespan()); - if (rep.getActionTokenGeneratedByUserLifespan() != null) - realm.setActionTokenGeneratedByUserLifespan(rep.getActionTokenGeneratedByUserLifespan()); - - OAuth2DeviceConfig deviceConfig = realm.getOAuth2DeviceConfig(); - - deviceConfig.setOAuth2DeviceCodeLifespan(rep.getOAuth2DeviceCodeLifespan()); - deviceConfig.setOAuth2DevicePollingInterval(rep.getOAuth2DevicePollingInterval()); - - if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore()); - if (rep.getDefaultSignatureAlgorithm() != null) realm.setDefaultSignatureAlgorithm(rep.getDefaultSignatureAlgorithm()); - if (rep.getRevokeRefreshToken() != null) realm.setRevokeRefreshToken(rep.getRevokeRefreshToken()); - if (rep.getRefreshTokenMaxReuse() != null) realm.setRefreshTokenMaxReuse(rep.getRefreshTokenMaxReuse()); - if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); - if (rep.getAccessTokenLifespanForImplicitFlow() != null) - realm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow()); - if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); - if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); - if (rep.getSsoSessionIdleTimeoutRememberMe() != null) realm.setSsoSessionIdleTimeoutRememberMe(rep.getSsoSessionIdleTimeoutRememberMe()); - if (rep.getSsoSessionMaxLifespanRememberMe() != null) realm.setSsoSessionMaxLifespanRememberMe(rep.getSsoSessionMaxLifespanRememberMe()); - if (rep.getOfflineSessionIdleTimeout() != null) - realm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout()); - // KEYCLOAK-7688 Offline Session Max for Offline Token - if (rep.getOfflineSessionMaxLifespanEnabled() != null) realm.setOfflineSessionMaxLifespanEnabled(rep.getOfflineSessionMaxLifespanEnabled()); - if (rep.getOfflineSessionMaxLifespan() != null) - realm.setOfflineSessionMaxLifespan(rep.getOfflineSessionMaxLifespan()); - if (rep.getClientSessionIdleTimeout() != null) - realm.setClientSessionIdleTimeout(rep.getClientSessionIdleTimeout()); - if (rep.getClientSessionMaxLifespan() != null) - realm.setClientSessionMaxLifespan(rep.getClientSessionMaxLifespan()); - if (rep.getClientOfflineSessionIdleTimeout() != null) - realm.setClientOfflineSessionIdleTimeout(rep.getClientOfflineSessionIdleTimeout()); - if (rep.getClientOfflineSessionMaxLifespan() != null) - realm.setClientOfflineSessionMaxLifespan(rep.getClientOfflineSessionMaxLifespan()); - 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.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled()); - if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration()); - if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<>(rep.getEventsListeners())); - if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes())); - - if (rep.isAdminEventsEnabled() != null) realm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); - if (rep.isAdminEventsDetailsEnabled() != null) - realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); - - - if (rep.getPasswordPolicy() != null) - realm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy())); - if (rep.getOtpPolicyType() != null) realm.setOTPPolicy(toPolicy(rep)); - - WebAuthnPolicy webAuthnPolicy = getWebAuthnPolicyTwoFactor(rep); - realm.setWebAuthnPolicy(webAuthnPolicy); - - webAuthnPolicy = getWebAuthnPolicyPasswordless(rep); - realm.setWebAuthnPolicyPasswordless(webAuthnPolicy); - - updateCibaSettings(rep, realm); - updateParSettings(rep, realm); - session.clientPolicy().updateRealmModelFromRepresentation(realm, rep); - - if (rep.getSmtpServer() != null) { - Map config = new HashMap(rep.getSmtpServer()); - if (rep.getSmtpServer().containsKey("password") && ComponentRepresentation.SECRET_VALUE.equals(rep.getSmtpServer().get("password"))) { - String passwordValue = realm.getSmtpConfig() != null ? realm.getSmtpConfig().get("password") : null; - config.put("password", passwordValue); - } - realm.setSmtpConfig(config); - } - - if (rep.getBrowserSecurityHeaders() != null) { - realm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders()); - } - - if (rep.isInternationalizationEnabled() != null) { - realm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); - } - if (rep.getSupportedLocales() != null) { - realm.setSupportedLocales(new HashSet(rep.getSupportedLocales())); - } - if (rep.getDefaultLocale() != null) { - realm.setDefaultLocale(rep.getDefaultLocale()); - } - if (rep.getBrowserFlow() != null) { - realm.setBrowserFlow(realm.getFlowByAlias(rep.getBrowserFlow())); - } - if (rep.getRegistrationFlow() != null) { - realm.setRegistrationFlow(realm.getFlowByAlias(rep.getRegistrationFlow())); - } - if (rep.getDirectGrantFlow() != null) { - realm.setDirectGrantFlow(realm.getFlowByAlias(rep.getDirectGrantFlow())); - } - if (rep.getResetCredentialsFlow() != null) { - realm.setResetCredentialsFlow(realm.getFlowByAlias(rep.getResetCredentialsFlow())); - } - if (rep.getClientAuthenticationFlow() != null) { - realm.setClientAuthenticationFlow(realm.getFlowByAlias(rep.getClientAuthenticationFlow())); - } - if (rep.getDockerAuthenticationFlow() != null) { - realm.setDockerAuthenticationFlow(realm.getFlowByAlias(rep.getDockerAuthenticationFlow())); - } - - } - - private static void updateCibaSettings(RealmRepresentation rep, RealmModel realm) { - Map newAttributes = rep.getAttributesOrEmpty(); - CibaConfig cibaPolicy = realm.getCibaPolicy(); - - cibaPolicy.setBackchannelTokenDeliveryMode(newAttributes.get(CibaConfig.CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE)); - cibaPolicy.setExpiresIn(newAttributes.get(CibaConfig.CIBA_EXPIRES_IN)); - cibaPolicy.setPoolingInterval(newAttributes.get(CibaConfig.CIBA_INTERVAL)); - cibaPolicy.setAuthRequestedUserHint(newAttributes.get(CibaConfig.CIBA_AUTH_REQUESTED_USER_HINT)); - } - - private static void updateParSettings(RealmRepresentation rep, RealmModel realm) { - Map newAttributes = rep.getAttributesOrEmpty(); - ParConfig parPolicy = realm.getParPolicy(); - - parPolicy.setRequestUriLifespan(newAttributes.get(ParConfig.PAR_REQUEST_URI_LIFESPAN)); } // Basic realm stuff - - public static ComponentModel convertFedProviderToComponent(String realmId, UserFederationProviderRepresentation fedModel) { - UserStorageProviderModel model = new UserStorageProviderModel(); - model.setId(fedModel.getId()); - model.setName(fedModel.getDisplayName()); - model.setParentId(realmId); - model.setProviderId(fedModel.getProviderName()); - model.setProviderType(UserStorageProvider.class.getName()); - model.setFullSyncPeriod(fedModel.getFullSyncPeriod()); - model.setPriority(fedModel.getPriority()); - model.setChangedSyncPeriod(fedModel.getChangedSyncPeriod()); - model.setLastSync(fedModel.getLastSync()); - if (fedModel.getConfig() != null) { - for (Map.Entry entry : fedModel.getConfig().entrySet()) { - model.getConfig().putSingle(entry.getKey(), entry.getValue()); - } - } - return model; - } - - public static ComponentModel convertFedMapperToComponent(RealmModel realm, ComponentModel parent, UserFederationMapperRepresentation rep, String newMapperType) { - ComponentModel mapper = new ComponentModel(); - mapper.setId(rep.getId()); - mapper.setName(rep.getName()); - mapper.setProviderId(rep.getFederationMapperType()); - mapper.setProviderType(newMapperType); - mapper.setParentId(parent.getId()); - if (rep.getConfig() != null) { - for (Map.Entry entry : rep.getConfig().entrySet()) { - mapper.getConfig().putSingle(entry.getKey(), entry.getValue()); - } - } - return mapper; - } - - // Roles public static RoleModel createRole(RealmModel newRealm, RoleRepresentation roleRep) { @@ -1350,19 +316,6 @@ public class RepresentationToModel { // CLIENTS - private static Map createClients(KeycloakSession session, RealmRepresentation rep, RealmModel realm, Map mappedFlows) { - Map appMap = new HashMap(); - for (ClientRepresentation resourceRep : rep.getClients()) { - ClientModel app = createClient(session, realm, resourceRep, mappedFlows); - appMap.put(app.getClientId(), app); - - ValidationUtil.validateClient(session, app, false, r -> { - throw new RuntimeException("Invalid client " + app.getClientId() + ": " + r.getAllErrorsAsString()); - }); - } - return appMap; - } - /** * Does not create scope or role mappings! * @@ -1374,7 +327,7 @@ public class RepresentationToModel { return createClient(session, realm, resourceRep, null); } - private static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, Map mappedFlows) { + public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, Map mappedFlows) { logger.debugv("Create client: {0}", resourceRep.getClientId()); ClientModel client = resourceRep.getId() != null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId()); @@ -1697,14 +650,6 @@ public class RepresentationToModel { // CLIENT SCOPES - private static Map createClientScopes(KeycloakSession session, List clientScopes, RealmModel realm) { - Map appMap = new HashMap<>(); - for (ClientScopeRepresentation resourceRep : clientScopes) { - ClientScopeModel app = createClientScope(session, realm, resourceRep); - appMap.put(app.getName(), app); - } - return appMap; - } public static ClientScopeModel createClientScope(KeycloakSession session, RealmModel realm, ClientScopeRepresentation resourceRep) { logger.debugv("Create client scope: {0}", resourceRep.getName()); @@ -1748,102 +693,8 @@ public class RepresentationToModel { } - public static long getClaimsMask(ClaimRepresentation rep) { - long mask = ClaimMask.ALL; - - 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; - } - return mask; - } - // Scope mappings - public static void createClientScopeMappings(RealmModel realm, ClientModel clientModel, List mappings) { - for (ScopeMappingRepresentation mapping : mappings) { - ScopeContainerModel scopeContainer = getScopeContainerHavingScope(realm, mapping); - - for (String roleString : mapping.getRoles()) { - RoleModel role = clientModel.getRole(roleString.trim()); - if (role == null) { - role = clientModel.addRole(roleString.trim()); - } - scopeContainer.addScopeMapping(role); - } - } - } - - private static ScopeContainerModel getScopeContainerHavingScope(RealmModel realm, ScopeMappingRepresentation scope) { - if (scope.getClient() != null) { - ClientModel client = realm.getClientByClientId(scope.getClient()); - if (client == null) { - throw new RuntimeException("Unknown client specification in scope mappings: " + scope.getClient()); - } - return client; - } else if (scope.getClientScope() != null) { - ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scope.getClientScope()); - if (clientScope == null) { - throw new RuntimeException("Unknown clientScope specification in scope mappings: " + scope.getClientScope()); - } - return clientScope; - } else if (scope.getClientTemplate() != null) { // Backwards compatibility - String templateName = KeycloakModelUtils.convertClientScopeName(scope.getClientTemplate()); - ClientScopeModel clientTemplate = KeycloakModelUtils.getClientScopeByName(realm, templateName); - if (clientTemplate == null) { - throw new RuntimeException("Unknown clientScope specification in scope mappings: " + templateName); - } - return clientTemplate; - } else { - throw new RuntimeException("Either client or clientScope needs to be specified in scope mappings"); - } - } // Users @@ -1997,22 +848,6 @@ public class RepresentationToModel { } } - private static void importIdentityProviders(RealmRepresentation rep, RealmModel newRealm, KeycloakSession session) { - if (rep.getIdentityProviders() != null) { - for (IdentityProviderRepresentation representation : rep.getIdentityProviders()) { - newRealm.addIdentityProvider(toModel(newRealm, representation, session)); - } - } - } - - private static void importIdentityProviderMappers(RealmRepresentation rep, RealmModel newRealm) { - if (rep.getIdentityProviderMappers() != null) { - for (IdentityProviderMapperRepresentation representation : rep.getIdentityProviderMappers()) { - newRealm.addIdentityProviderMapper(toModel(representation)); - } - } - } - public static IdentityProviderModel toModel(RealmModel realm, IdentityProviderRepresentation representation, KeycloakSession session) { IdentityProviderFactory providerFactory = (IdentityProviderFactory) session.getKeycloakSessionFactory().getProviderFactory( IdentityProvider.class, representation.getProviderId()); @@ -2133,30 +968,6 @@ public class RepresentationToModel { } - private static AuthenticationExecutionModel toModel(RealmModel realm, AuthenticationFlowModel parentFlow, AuthenticationExecutionExportRepresentation rep) { - AuthenticationExecutionModel model = new AuthenticationExecutionModel(); - if (rep.getAuthenticatorConfig() != null) { - AuthenticatorConfigModel config = realm.getAuthenticatorConfigByAlias(rep.getAuthenticatorConfig()); - model.setAuthenticatorConfig(config.getId()); - } - model.setAuthenticator(rep.getAuthenticator()); - model.setAuthenticatorFlow(rep.isAuthenticatorFlow()); - if (rep.getFlowAlias() != null) { - AuthenticationFlowModel flow = realm.getFlowByAlias(rep.getFlowAlias()); - model.setFlowId(flow.getId()); - } - model.setPriority(rep.getPriority()); - try { - model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement())); - model.setParentFlow(parentFlow.getId()); - } catch (IllegalArgumentException iae) { - //retro-compatible for previous OPTIONAL being changed to CONDITIONAL - if ("OPTIONAL".equals(rep.getRequirement())){ - MigrateTo8_0_0.migrateOptionalAuthenticationExecution(realm, parentFlow, model, false); - } - } - return model; - } public static AuthenticationExecutionModel toModel(RealmModel realm, AuthenticationExecutionRepresentation rep) { AuthenticationExecutionModel model = new AuthenticationExecutionModel(); @@ -2183,18 +994,6 @@ public class RepresentationToModel { return model; } - public static RequiredActionProviderModel toModel(RequiredActionProviderRepresentation rep) { - RequiredActionProviderModel model = new RequiredActionProviderModel(); - model.setConfig(removeEmptyString(rep.getConfig())); - model.setPriority(rep.getPriority()); - model.setDefaultAction(rep.isDefaultAction()); - model.setEnabled(rep.isEnabled()); - model.setProviderId(rep.getProviderId()); - model.setName(rep.getName()); - model.setAlias(rep.getAlias()); - return model; - } - public static ComponentModel toModel(KeycloakSession session, ComponentRepresentation rep) { ComponentModel model = new ComponentModel(); model.setId(rep.getId()); @@ -2281,15 +1080,6 @@ 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 (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION) && Boolean.TRUE.equals(clientRepresentation.getAuthorizationServicesEnabled())) { AuthorizationProviderFactory authorizationFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); diff --git a/server-spi-private/src/main/java/org/keycloak/storage/DatastoreProvider.java b/server-spi-private/src/main/java/org/keycloak/storage/DatastoreProvider.java index a7077e97a8..28f3480d1c 100644 --- a/server-spi-private/src/main/java/org/keycloak/storage/DatastoreProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/storage/DatastoreProvider.java @@ -21,5 +21,8 @@ public interface DatastoreProvider extends Provider { public RoleProvider roles(); public UserProvider users(); - + + ExportImportManager getExportImportManager(); + + MigrationManager getMigrationManager(); } 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 new file mode 100644 index 0000000000..7709de5957 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/storage/ExportImportManager.java @@ -0,0 +1,16 @@ +package org.keycloak.storage; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; + +/** + * Manage importing and updating of realms for the legacy store. + * + * @author Alexander Schwartz + */ +public interface ExportImportManager { + void importRealm(RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent); + + void updateRealm(RealmRepresentation rep, RealmModel realm); +} diff --git a/server-spi-private/src/main/java/org/keycloak/storage/MigrationManager.java b/server-spi-private/src/main/java/org/keycloak/storage/MigrationManager.java new file mode 100644 index 0000000000..afda63e31e --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/storage/MigrationManager.java @@ -0,0 +1,18 @@ +package org.keycloak.storage; + +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; + +/** + * Handle the migration of the datastore and an imported realm representation. + * Will eventually be handled by the store directly. + * + * @author Alexander Schwartz + */ +@Deprecated +public interface MigrationManager { + + void migrate(); + + void migrate(RealmModel realm, RealmRepresentation rep, boolean skipUserDependent); +} diff --git a/server-spi-private/src/test/java/org/keycloak/models/MigrationVersionTest.java b/server-spi-private/src/test/java/org/keycloak/models/MigrationVersionTest.java index 38a613e490..39fa8da85e 100755 --- a/server-spi-private/src/test/java/org/keycloak/models/MigrationVersionTest.java +++ b/server-spi-private/src/test/java/org/keycloak/models/MigrationVersionTest.java @@ -21,11 +21,6 @@ import org.junit.Assert; import org.junit.Test; import org.keycloak.migration.ModelVersion; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.keycloak.migration.MigrationModelManager.*; - /** * @author Bill Burke * @version $Revision: 1 $ @@ -100,35 +95,4 @@ public class MigrationVersionTest { Assert.assertNull(versionProduct.getQualifier()); } - @Test - public void testRHSSOVersionToKeycloakVersionConversion() { - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.0.GA"), is(equalTo(RHSSO_VERSION_7_0_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.1.GA"), is(equalTo(RHSSO_VERSION_7_0_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.2.GA"), is(equalTo(RHSSO_VERSION_7_0_KEYCLOAK_VERSION))); - - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.1.0.GA"), is(equalTo(RHSSO_VERSION_7_1_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.1.1.GA"), is(equalTo(RHSSO_VERSION_7_1_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.1.2.GA"), is(equalTo(RHSSO_VERSION_7_1_KEYCLOAK_VERSION))); - - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.2.0.GA"), is(equalTo(RHSSO_VERSION_7_2_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.2.1.GA"), is(equalTo(RHSSO_VERSION_7_2_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.2.2.GA"), is(equalTo(RHSSO_VERSION_7_2_KEYCLOAK_VERSION))); - - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.0.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.1.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.2.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.3.10.GA"), is(equalTo(RHSSO_VERSION_7_3_KEYCLOAK_VERSION))); - - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.4.0.GA"), is(equalTo(RHSSO_VERSION_7_4_KEYCLOAK_VERSION))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.4.15.GA"), is(equalTo(RHSSO_VERSION_7_4_KEYCLOAK_VERSION))); - - // check the conversion doesn't change version for keycloak - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7.0.0"), is(nullValue())); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("8.0.0"), is(nullValue())); - - // check for CD releases - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("6"), is(equalTo(new ModelVersion("6.0.0")))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("7"), is(equalTo(new ModelVersion("7.0.0")))); - Assert.assertThat(convertRHSSOVersionToKeycloakVersion("10"), is(equalTo(new ModelVersion("10.0.0")))); - } } diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java index 8717a7a40b..45890b1419 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java @@ -23,8 +23,6 @@ import org.keycloak.component.ComponentModel; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderEvent; import org.keycloak.storage.SearchableModelField; -import org.keycloak.storage.UserStorageProvider; -import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.client.ClientStorageProvider; import org.keycloak.storage.client.ClientStorageProviderModel; import org.keycloak.storage.role.RoleStorageProvider; @@ -662,25 +660,6 @@ public interface RealmModel extends RoleContainerModel { ComponentModel getComponent(String id); - /** - * @deprecated Use {@link #getUserStorageProvidersStream() getUserStorageProvidersStream} instead. - */ - @Deprecated - default List getUserStorageProviders() { - return getUserStorageProvidersStream().collect(Collectors.toList()); - } - - /** - * Returns sorted {@link UserStorageProviderModel UserStorageProviderModel} as a stream. - * It should be used with forEachOrdered if the ordering is required. - * @return Sorted stream of {@link UserStorageProviderModel}. Never returns {@code null}. - */ - default Stream getUserStorageProvidersStream() { - return getComponentsStream(getId(), UserStorageProvider.class.getName()) - .map(UserStorageProviderModel::new) - .sorted(UserStorageProviderModel.comparator); - } - /** * @deprecated Use {@link #getClientStorageProvidersStream() getClientStorageProvidersStream} instead. */ diff --git a/server-spi/src/main/java/org/keycloak/storage/CacheableStorageProviderModel.java b/server-spi/src/main/java/org/keycloak/storage/CacheableStorageProviderModel.java index 3fb8384de3..5372d99be6 100644 --- a/server-spi/src/main/java/org/keycloak/storage/CacheableStorageProviderModel.java +++ b/server-spi/src/main/java/org/keycloak/storage/CacheableStorageProviderModel.java @@ -162,9 +162,9 @@ public class CacheableStorageProviderModel extends PrioritizedComponentModel { } public long getLifespan() { - UserStorageProviderModel.CachePolicy policy = getCachePolicy(); + CachePolicy policy = getCachePolicy(); long lifespan = -1; - if (policy == null || policy == UserStorageProviderModel.CachePolicy.DEFAULT) { + if (policy == null || policy == CachePolicy.DEFAULT) { lifespan = -1; } else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_DAILY) { if (getEvictionHour() > -1 && getEvictionMinute() > -1) { diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserBulkUpdateProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserBulkUpdateProvider.java index 4ec605150e..79e2b78a31 100644 --- a/server-spi/src/main/java/org/keycloak/storage/user/UserBulkUpdateProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/user/UserBulkUpdateProvider.java @@ -21,7 +21,7 @@ import org.keycloak.models.RoleModel; /** * This is an optional capability interface that is intended to be implemented by any - * {@link org.keycloak.storage.UserStorageProvider UserStorageProvider} that supports bulk operations. + * UserStorageProvider that supports bulk operations. * * @author Bill Burke * @version $Revision: 1 $ diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserLookupProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserLookupProvider.java index a8ab8e3057..6112ea52e2 100644 --- a/server-spi/src/main/java/org/keycloak/storage/user/UserLookupProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/user/UserLookupProvider.java @@ -21,7 +21,7 @@ import org.keycloak.models.UserModel; /** * This is an optional capability interface that is intended to be implemented by any - * {@link org.keycloak.storage.UserStorageProvider UserStorageProvider} that supports basic user querying. You must + * UserStorageProvider that supports basic user querying. You must * implement this interface if you want to be able to log in to keycloak using users from your storage. *

* Note that all methods in this interface should limit search only to data available within the storage that is diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java index 7ab5fcb03f..e31d8b509e 100644 --- a/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java @@ -31,7 +31,7 @@ import java.util.stream.Stream; /** * * This is an optional capability interface that is intended to be implemented by any - * {@link org.keycloak.storage.UserStorageProvider UserStorageProvider} that supports complex user querying. You must + * UserStorageProvider that supports complex user querying. You must * implement this interface if you want to view and manage users from the administration console. *

* Note that all methods in this interface should limit search only to data available within the storage that is diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java index bd4dd5c4d8..22ac5d4833 100644 --- a/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java @@ -22,7 +22,7 @@ import org.keycloak.models.UserModel; /** * This is an optional capability interface that is intended to be implemented by any - * {@link org.keycloak.storage.UserStorageProvider UserStorageProvider} that supports addition of new users. You must + * UserStorageProvider that supports addition of new users. You must * implement this interface if you want to use this storage for registering new users. * * @author Bill Burke diff --git a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java index e71da637ab..479ed9c06e 100644 --- a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java +++ b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java @@ -25,11 +25,11 @@ import org.keycloak.models.UserModel; import org.keycloak.models.cache.CachedUserModel; import org.keycloak.models.cache.OnUserCache; import org.keycloak.models.cache.UserCache; +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; import org.keycloak.storage.AbstractStorageManager; +import org.keycloak.storage.CacheableStorageProviderModel; import org.keycloak.storage.StorageId; -import org.keycloak.storage.UserStorageProvider; -import org.keycloak.storage.UserStorageProviderFactory; -import org.keycloak.storage.UserStorageProviderModel; import java.util.Arrays; import java.util.LinkedList; @@ -41,11 +41,11 @@ import java.util.stream.Stream; * @author Bill Burke * @version $Revision: 1 $ */ -public class UserCredentialStoreManager extends AbstractStorageManager +public class UserCredentialStoreManager extends AbstractStorageManager implements UserCredentialManager.Streams, OnUserCache { public UserCredentialStoreManager(KeycloakSession session) { - super(session, UserStorageProviderFactory.class, UserStorageProvider.class, UserStorageProviderModel::new, "user"); + super(session, ProviderFactory.class, Provider.class, componentModel -> null, "user"); } protected UserCredentialStore getStoreForUser(UserModel user) { @@ -143,6 +143,7 @@ public class UserCredentialStoreManager extends AbstractStorageManager toValidate = new LinkedList<>(inputs); String providerId = StorageId.isLocalStorage(user) ? user.getFederationLink() : StorageId.resolveProviderId(user); if (providerId != null) { + /* UserStorageProviderModel model = getStorageProviderModel(realm, providerId); if (model == null || !model.isEnabled()) return false; @@ -150,6 +151,7 @@ public class UserCredentialStoreManager extends AbstractStorageManager types = Stream.empty(); String providerId = StorageId.isLocalStorage(user) ? user.getFederationLink() : StorageId.resolveProviderId(user); if (providerId != null) { + /* UserStorageProviderModel model = getStorageProviderModel(realm, providerId); if (model == null || !model.isEnabled()) return types; CredentialInputUpdater updater = getStorageProviderInstance(model, CredentialInputUpdater.class); if (updater != null) types = updater.getDisableableCredentialTypesStream(realm, user); + */ } return Stream.concat(types, getCredentialProviders(session, CredentialInputUpdater.class) @@ -251,6 +260,7 @@ public class UserCredentialStoreManager extends AbstractStorageManager 0); - testRealm.getUserStorageProvidersStream().forEachOrdered(persistentFedModel -> { + UserStorageUtil.getUserStorageProvidersStream(testRealm).forEachOrdered(persistentFedModel -> { if (LDAPStorageProviderFactory.PROVIDER_NAME.equals(persistentFedModel.getProviderId())) { Assert.assertTrue(persistentFedModel.getLastSync() > 0); } else { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageProvidersTestUtils.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageProvidersTestUtils.java index ddfe59564d..e7d26de6f6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageProvidersTestUtils.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageProvidersTestUtils.java @@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderFactory; import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.UserStorageUtil; import java.util.stream.Stream; @@ -22,10 +23,6 @@ public class UserStorageProvidersTestUtils { return model.isEnabled(); } - public static Stream getStorageProviders(RealmModel realm) { - return realm.getUserStorageProvidersStream(); - } - private static UserStorageProviderFactory getUserStorageProviderFactory(UserStorageProviderModel model, KeycloakSession session) { return (UserStorageProviderFactory) session.getKeycloakSessionFactory() .getProviderFactory(UserStorageProvider.class, model.getProviderId()); @@ -50,7 +47,7 @@ public class UserStorageProvidersTestUtils { } public static Stream getStorageProviders(RealmModel realm, KeycloakSession session, Class type) { - return realm.getUserStorageProvidersStream() + return UserStorageUtil.getUserStorageProvidersStream(realm) .filter(model -> { UserStorageProviderFactory factory = getUserStorageProviderFactory(model, session); if (factory == null) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java index 64ffd69dd2..917ac735f0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java @@ -25,7 +25,7 @@ import org.junit.runners.MethodSorters; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; -import org.keycloak.services.managers.UserStorageSyncManager; +import org.keycloak.storage.managers.UserStorageSyncManager; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.user.SynchronizationResult; diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java index f894916720..366473a73c 100644 --- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java @@ -20,9 +20,11 @@ package org.keycloak.testsuite.util.cli; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.services.managers.UserStorageSyncManager; +import org.keycloak.storage.managers.UserStorageSyncManager; import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.UserStorageUtil; + +import java.util.Objects; /** * @author Marek Posolda @@ -35,7 +37,7 @@ public class SyncDummyFederationProviderCommand extends AbstractCommand { int changedSyncPeriod = getIntArg(1); RealmModel realm = session.realms().getRealmByName("master"); - UserStorageProviderModel fedProviderModel = KeycloakModelUtils.findUserStorageProviderByName("cluster-dummy", realm); + UserStorageProviderModel fedProviderModel = findUserStorageProviderByName(session, "cluster-dummy", realm); if (fedProviderModel == null) { MultivaluedHashMap cfg = fedProviderModel.getConfig(); updateConfig(cfg, waitTime); @@ -64,6 +66,16 @@ public class SyncDummyFederationProviderCommand extends AbstractCommand { cfg.putSingle("wait-time", String.valueOf(waitTime)); } + public static UserStorageProviderModel findUserStorageProviderByName(KeycloakSession session, String displayName, RealmModel realm) { + if (displayName == null) { + return null; + } + + return UserStorageUtil.getUserStorageProvidersStream(realm) + .filter(fedProvider -> Objects.equals(fedProvider.getName(), displayName)) + .findFirst() + .orElse(null); + } @Override public String getName() {