diff --git a/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo24_0_0.java b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo24_0_0.java new file mode 100644 index 0000000000..74b7f905ec --- /dev/null +++ b/model/legacy-private/src/main/java/org/keycloak/migration/migrators/MigrateTo24_0_0.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 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.migration.migrators; + +import org.jboss.logging.Logger; +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.KeycloakContext; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.userprofile.config.UPConfig; +import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; +import org.keycloak.userprofile.UserProfileProvider; + +public class MigrateTo24_0_0 implements Migration { + + private static final Logger LOG = Logger.getLogger(MigrateTo24_0_0.class); + public static final ModelVersion VERSION = new ModelVersion("24.0.0"); + private static final String REALM_USER_PROFILE_ENABLED = "userProfileEnabled"; + + @Override + public void migrate(KeycloakSession session) { + session.realms().getRealmsStream().forEach(realm -> migrateRealm(session, realm)); + } + + @Override + public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) { + migrateRealm(session, realm); + } + + @Override + public ModelVersion getVersion() { + return VERSION; + } + + private void migrateRealm(KeycloakSession session, RealmModel realm) { + KeycloakContext context = session.getContext(); + try { + context.setRealm(realm); + updateUserProfileSettings(session); + } finally { + context.setRealm(null); + } + } + + private void updateUserProfileSettings(KeycloakSession session) { + RealmModel realm = session.getContext().getRealm(); + boolean isUserProfileEnabled = Boolean.parseBoolean(realm.getAttribute(REALM_USER_PROFILE_ENABLED)); + + if (isUserProfileEnabled) { + // existing realms with user profile enabled does not need any addition migration step + LOG.debugf("Skipping migration for realm %s. The declarative user profile is already enabled.", realm.getName()); + return; + } + + // user profile is enabled by default since this version + realm.setAttribute(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString()); + + // for backward compatibility in terms of behavior, we enable unmanaged attributes for existing realms + // that don't have the declarative user profile enabled + UserProfileProvider provider = session.getProvider(UserProfileProvider.class); + UPConfig upConfig = provider.getConfiguration(); + upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED); + provider.setConfiguration(upConfig); + + LOG.debugf("Enabled the declarative user profile to realm %s with support for unmanaged attributes", realm.getName()); + } +} 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 index 30833d2d80..6cb08edbc9 100644 --- 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 @@ -37,6 +37,7 @@ import org.keycloak.migration.migrators.MigrateTo1_9_2; import org.keycloak.migration.migrators.MigrateTo21_0_0; import org.keycloak.migration.migrators.MigrateTo22_0_0; import org.keycloak.migration.migrators.MigrateTo23_0_0; +import org.keycloak.migration.migrators.MigrateTo24_0_0; import org.keycloak.migration.migrators.MigrateTo2_0_0; import org.keycloak.migration.migrators.MigrateTo2_1_0; import org.keycloak.migration.migrators.MigrateTo2_2_0; @@ -112,7 +113,8 @@ public class LegacyMigrationManager implements MigrationManager { new MigrateTo20_0_0(), new MigrateTo21_0_0(), new MigrateTo22_0_0(), - new MigrateTo23_0_0() + new MigrateTo23_0_0(), + new MigrateTo24_0_0() }; private final KeycloakSession session; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java index 2a8e9726a8..da2ff80c80 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java @@ -63,6 +63,8 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.representations.userprofile.config.UPConfig; +import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; import org.keycloak.storage.UserStorageProvider; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; @@ -110,6 +112,7 @@ import static org.keycloak.models.AccountRoles.VIEW_GROUPS; import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; import static org.keycloak.testsuite.Assert.assertNames; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; +import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED; import static org.keycloak.userprofile.DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY; /** @@ -395,6 +398,15 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest { testRegistrationProfileFormActionRemoved(migrationRealm2); } + protected void testMigrationTo24_0_0(boolean testUserProfileMigration) { + if (testUserProfileMigration) { + testUserProfileEnabledByDefault(migrationRealm); + testUnmanagedAttributePolicySet(migrationRealm, UnmanagedAttributePolicy.ENABLED); + testUserProfileEnabledByDefault(migrationRealm2); + testUnmanagedAttributePolicySet(migrationRealm2, null); + } + } + protected void testDeleteAccount(RealmResource realm) { ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0); ClientResource accountResource = realm.clients().get(accountClient.getId()); @@ -1079,6 +1091,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest { testMigrationTo23_0_0(testUserProfileMigration); } + protected void testMigrationTo24_x(boolean testUserProfileMigration) { + testMigrationTo24_0_0(testUserProfileMigration); + } + protected void testMigrationTo7_x(boolean supportedAuthzServices) { if (supportedAuthzServices) { testDecisionStrategySetOnResourceServer(); @@ -1183,4 +1199,16 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest { Map realmAttributes = migrationRealm.toRepresentation().getAttributes(); assertEquals("custom_value", realmAttributes.get("custom_attribute")); } + + private void testUserProfileEnabledByDefault(RealmResource realm) { + RealmRepresentation rep = realm.toRepresentation(); + Map attributes = rep.getAttributes(); + String userProfileEnabled = attributes.get(REALM_USER_PROFILE_ENABLED); + assertTrue(Boolean.parseBoolean(userProfileEnabled)); + } + + private void testUnmanagedAttributePolicySet(RealmResource realm, UnmanagedAttributePolicy policy) { + UPConfig upConfig = realm.users().userProfile().getConfiguration(); + assertEquals(policy, upConfig.getUnmanagedAttributePolicy()); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java index 9c9218204b..c742675a51 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport1903MigrationTest.java @@ -17,8 +17,10 @@ package org.keycloak.testsuite.migration; import org.junit.Test; +import org.keycloak.common.Profile.Feature; import org.keycloak.exportimport.util.ImportUtils; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.utils.io.IOUtil; import org.keycloak.util.JsonSerialization; @@ -29,6 +31,7 @@ import java.util.Map; /** * Tests that we can import json file from previous version. MigrationTest only tests DB. */ +@EnableFeature(Feature.DECLARATIVE_USER_PROFILE) public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigrationTest { @Override @@ -52,6 +55,7 @@ public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigra testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(true); + testMigrationTo24_x(true); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java index 2b51481821..68b2433d0c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java @@ -78,6 +78,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(false); + testMigrationTo24_x(false); } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java index 34626586fa..4a2d2fbe28 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java @@ -72,6 +72,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(false); + testMigrationTo24_x(false); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java index cd94f1a148..e38a8058cd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport343MigrationTest.java @@ -67,6 +67,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(false); + testMigrationTo24_x(false); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java index b060a65f1a..0b04026431 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport483MigrationTest.java @@ -61,6 +61,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(false); + testMigrationTo24_x(false); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java index d204e9aafc..f6a8f3fcb6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport903MigrationTest.java @@ -54,6 +54,7 @@ public class JsonFileImport903MigrationTest extends AbstractJsonFileImportMigrat testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(false); + testMigrationTo24_x(false); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java index 060fc068fc..5a03e12fda 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java @@ -19,7 +19,9 @@ package org.keycloak.testsuite.migration; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.Profile.Feature; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.migration.Migration; import jakarta.ws.rs.NotFoundException; @@ -32,6 +34,7 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; * * @author Vlastislav Ramik */ +@EnableFeature(Feature.DECLARATIVE_USER_PROFILE) public class MigrationTest extends AbstractMigrationTest { @Override @@ -69,5 +72,6 @@ public class MigrationTest extends AbstractMigrationTest { testMigrationTo21_x(); testMigrationTo22_x(); testMigrationTo23_x(true); + testMigrationTo24_x(true); } }