diff --git a/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java b/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java index 87a5140ded..fffa8294df 100644 --- a/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java +++ b/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java @@ -180,6 +180,13 @@ public class DefaultAttributes extends HashMap> implements for (String name : nameSet()) { AttributeMetadata metadata = getMetadata(name); + RealmModel realm = session.getContext().getRealm(); + + if (UserModel.USERNAME.equals(name) + && UserProfileContext.USER_API.equals(context) + && realm.isRegistrationEmailAsUsername()) { + continue; + } if (metadata == null || !metadata.canEdit(createAttributeContext(metadata))) { attributes.remove(name); diff --git a/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java b/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java index 3c33cecc1c..940049a3e7 100644 --- a/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java +++ b/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java @@ -83,6 +83,12 @@ public abstract class AbstractUserProfileProvider return !realm.isRegistrationEmailAsUsername(); } + if (USER_API.equals(c.getContext())) { + if (realm.isRegistrationEmailAsUsername()) { + return false; + } + } + return realm.isEditUsernameAllowed(); } @@ -114,6 +120,12 @@ public abstract class AbstractUserProfileProvider return true; } + if (USER_API.equals(c.getContext())) { + if (realm.isRegistrationEmailAsUsername()) { + return true; + } + } + if (Profile.isFeatureEnabled(Feature.UPDATE_EMAIL)) { return !(UPDATE_PROFILE.equals(c.getContext()) || ACCOUNT.equals(c.getContext())); } diff --git a/services/src/main/java/org/keycloak/userprofile/LegacyAttributes.java b/services/src/main/java/org/keycloak/userprofile/LegacyAttributes.java index b2ce22adfe..192ae41254 100644 --- a/services/src/main/java/org/keycloak/userprofile/LegacyAttributes.java +++ b/services/src/main/java/org/keycloak/userprofile/LegacyAttributes.java @@ -58,6 +58,11 @@ public class LegacyAttributes extends DefaultAttributes { if (UserProfileContext.IDP_REVIEW.equals(context)) { return false; } + if (UserProfileContext.USER_API.equals(context)) { + if (realm.isRegistrationEmailAsUsername()) { + return false; + } + } return !realm.isEditUsernameAllowed(); } @@ -65,7 +70,8 @@ public class LegacyAttributes extends DefaultAttributes { if (isServiceAccountUser()) { return false; } - if (UserProfileContext.IDP_REVIEW.equals(context)) { + if (UserProfileContext.IDP_REVIEW.equals(context) + || UserProfileContext.USER_API.equals(context)) { return false; } if (realm.isRegistrationEmailAsUsername() && !realm.isEditUsernameAllowed()) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java index 8c688de9f0..a81205f496 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java @@ -2376,7 +2376,8 @@ public class UserTest extends AbstractAdminTest { updateUser(user, userRep); userRep = realm.users().get(id).toRepresentation(); - assertEquals("user1@localhost", userRep.getUsername()); + assertEquals("user11@localhost", userRep.getUsername()); + assertEquals("user11@localhost", userRep.getEmail()); } @Test @@ -2396,6 +2397,7 @@ public class UserTest extends AbstractAdminTest { userRep = realm.users().get(id).toRepresentation(); assertEquals("user11@localhost", userRep.getUsername()); + assertEquals("user11@localhost", userRep.getEmail()); } @Test @@ -2914,7 +2916,7 @@ public class UserTest extends AbstractAdminTest { assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM); } - private void switchRegistrationEmailAsUsername(boolean enable) { + protected void switchRegistrationEmailAsUsername(boolean enable) { RealmRepresentation rep = realm.toRepresentation(); rep.setRegistrationEmailAsUsername(enable); realm.update(rep); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java index e8ca24a9ac..333b198bfe 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTestWithUserProfile.java @@ -17,17 +17,18 @@ package org.keycloak.testsuite.admin; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.io.IOException; -import java.util.HashSet; import java.util.Set; import org.jetbrains.annotations.Nullable; import org.junit.Before; import org.junit.Test; import org.keycloak.common.Profile.Feature; -import org.keycloak.models.LDAPConstants; +import org.keycloak.models.UserModel; import org.keycloak.representations.idm.UserProfileAttributeMetadata; import org.keycloak.representations.idm.UserProfileMetadata; import org.keycloak.representations.idm.RealmRepresentation; @@ -74,6 +75,40 @@ public class UserTestWithUserProfile extends UserTest { } } + @Test + public void testUsernameReadOnlyIfEmailAsUsernameEnabled() { + switchRegistrationEmailAsUsername(true); + getCleanup().addCleanup(() -> switchRegistrationEmailAsUsername(false)); + String userId = createUser("user-metadata", "user-metadata@keycloak.org"); + UserRepresentation user = realm.users().get(userId).toRepresentation(true); + UserProfileMetadata metadata = user.getUserProfileMetadata(); + assertNotNull(metadata); + UserProfileAttributeMetadata username = getAttributeMetadata(metadata, UserModel.USERNAME); + assertNotNull(username); + assertTrue(username.isReadOnly()); + UserProfileAttributeMetadata email = getAttributeMetadata(metadata, UserModel.EMAIL); + assertNotNull(email); + assertFalse(email.isReadOnly()); + } + + @Test + public void testEmailNotReadOnlyIfEmailAsUsernameEnabledAndEditUsernameDisabled() { + switchRegistrationEmailAsUsername(true); + getCleanup().addCleanup(() -> switchRegistrationEmailAsUsername(false)); + RealmRepresentation rep = realm.toRepresentation(); + assertFalse(rep.isEditUsernameAllowed()); + String userId = createUser("user-metadata", "user-metadata@keycloak.org"); + UserRepresentation user = realm.users().get(userId).toRepresentation(true); + UserProfileMetadata metadata = user.getUserProfileMetadata(); + assertNotNull(metadata); + UserProfileAttributeMetadata username = getAttributeMetadata(metadata, UserModel.USERNAME); + assertNotNull(username); + assertTrue(username.isReadOnly()); + UserProfileAttributeMetadata email = getAttributeMetadata(metadata, UserModel.EMAIL); + assertNotNull(email); + assertFalse(email.isReadOnly()); + } + @Nullable private static UserProfileAttributeMetadata getAttributeMetadata(UserProfileMetadata metadata, String name) { UserProfileAttributeMetadata attrMetadata = null;