The username in account is required and don't change when email as username is enabled

Closes #23976
This commit is contained in:
Pedro Igor 2023-10-16 08:34:07 -03:00
parent dfe64f6271
commit e91a0afca2
6 changed files with 64 additions and 26 deletions

View file

@ -183,9 +183,8 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
RealmModel realm = session.getContext().getRealm(); RealmModel realm = session.getContext().getRealm();
if (UserModel.USERNAME.equals(name) if (UserModel.USERNAME.equals(name)
&& UserProfileContext.USER_API.equals(context)
&& realm.isRegistrationEmailAsUsername()) { && realm.isRegistrationEmailAsUsername()) {
continue; continue;
} }
if (metadata == null || !metadata.canEdit(createAttributeContext(metadata))) { if (metadata == null || !metadata.canEdit(createAttributeContext(metadata))) {

View file

@ -82,15 +82,8 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
return !realm.isRegistrationEmailAsUsername(); return !realm.isRegistrationEmailAsUsername();
} }
if (USER_API.equals(c.getContext())) { if (realm.isRegistrationEmailAsUsername()) {
if (realm.isRegistrationEmailAsUsername()) { return false;
return false;
}
if (isNewUser(c)) {
// when creating a user the username is always editable
return true;
}
} }
return realm.isEditUsernameAllowed(); return realm.isEditUsernameAllowed();

View file

@ -58,10 +58,8 @@ public class LegacyAttributes extends DefaultAttributes {
if (UserProfileContext.IDP_REVIEW.equals(context)) { if (UserProfileContext.IDP_REVIEW.equals(context)) {
return false; return false;
} }
if (UserProfileContext.USER_API.equals(context)) { if (realm.isRegistrationEmailAsUsername()) {
if (realm.isRegistrationEmailAsUsername()) { return true;
return false;
}
} }
return !realm.isEditUsernameAllowed(); return !realm.isEditUsernameAllowed();
} }
@ -94,9 +92,14 @@ public class LegacyAttributes extends DefaultAttributes {
@Override @Override
public Map<String, List<String>> getWritable() { public Map<String, List<String>> getWritable() {
Map<String, List<String>> attributes = new HashMap<>(this); Map<String, List<String>> attributes = new HashMap<>(this);
RealmModel realm = session.getContext().getRealm();
for (String name : nameSet()) { for (String name : nameSet()) {
if (isReadOnly(name)) { if (isReadOnly(name)) {
if (UserModel.USERNAME.equals(name)
&& realm.isRegistrationEmailAsUsername()) {
continue;
}
attributes.remove(name); attributes.remove(name);
} }
} }

View file

@ -147,8 +147,9 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
user = getUser(); user = getUser();
if (isDeclarativeUserProfile()) { if (isDeclarativeUserProfile()) {
assertNotNull(user.getUserProfileMetadata()); assertNotNull(user.getUserProfileMetadata());
// username is read-only and is the same as email, but email is writable // username is read-only, not required, and is the same as email
assertUserProfileAttributeMetadata(user, "username", "${username}", true, true); // but email is writable
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false); assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
} }
user.setUsername("should-be-the-email"); user.setUsername("should-be-the-email");
@ -164,7 +165,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
if (isDeclarativeUserProfile()) { if (isDeclarativeUserProfile()) {
assertNotNull(user.getUserProfileMetadata()); assertNotNull(user.getUserProfileMetadata());
// username is read-only and is the same as email, but email is read-only // username is read-only and is the same as email, but email is read-only
assertUserProfileAttributeMetadata(user, "username", "${username}", true, true); assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
assertUserProfileAttributeMetadata(user, "email", "${email}", true, true); assertUserProfileAttributeMetadata(user, "email", "${email}", true, true);
} }
user.setUsername("should-be-the-email"); user.setUsername("should-be-the-email");
@ -189,8 +190,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
user = getUser(); user = getUser();
user.setEmail("should-not-change@keycloak.org"); user.setEmail("should-not-change@keycloak.org");
user = updateAndGet(user); user = updateAndGet(user);
assertEquals("different-than-email", user.getUsername());
assertEquals("user@keycloak.org", user.getEmail()); assertEquals("user@keycloak.org", user.getEmail());
assertEquals(user.getEmail(), user.getUsername());
} finally { } finally {
realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername); realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
realmRep.setEditUsernameAllowed(editUsernameAllowed); realmRep.setEditUsernameAllowed(editUsernameAllowed);

View file

@ -102,7 +102,7 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
@Test @Test
@Override @Override
public void testEditUsernameAllowed() throws IOException { public void testEditUsernameAllowed() throws IOException {
super.testEditUsernameAllowed();
setUserProfileConfiguration(UP_CONFIG_FOR_METADATA); setUserProfileConfiguration(UP_CONFIG_FOR_METADATA);
UserRepresentation user = getUser(); UserRepresentation user = getUser();

View file

@ -596,14 +596,59 @@ public class UserTest extends AbstractAdminTest {
@Test @Test
public void createUserWithEmailAsUsername() { public void createUserWithEmailAsUsername() {
switchRegistrationEmailAsUsername(true); RealmRepresentation realmRep = realm.toRepresentation();
Boolean registrationEmailAsUsername = realmRep.isRegistrationEmailAsUsername();
Boolean editUsernameAllowed = realmRep.isEditUsernameAllowed();
getCleanup().addCleanup(() -> {
realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
realm.update(realmRep);
});
getCleanup().addCleanup(() -> {
realmRep.setEditUsernameAllowed(editUsernameAllowed);
realm.update(realmRep);
});
switchRegistrationEmailAsUsername(true);
switchEditUsernameAllowedOn(false);
String id = createUser(); String id = createUser();
UserResource user = realm.users().get(id); UserResource user = realm.users().get(id);
UserRepresentation userRep = user.toRepresentation(); UserRepresentation userRep = user.toRepresentation();
assertEquals("user1@localhost", userRep.getUsername()); assertEquals("user1@localhost", userRep.getEmail());
assertEquals(userRep.getEmail(), userRep.getUsername());
deleteUser(id);
switchRegistrationEmailAsUsername(true);
switchEditUsernameAllowedOn(true);
id = createUser();
user = realm.users().get(id);
userRep = user.toRepresentation();
assertEquals("user1@localhost", userRep.getEmail());
assertEquals(userRep.getEmail(), userRep.getUsername());
deleteUser(id);
switchRegistrationEmailAsUsername(false); switchRegistrationEmailAsUsername(false);
switchEditUsernameAllowedOn(true);
id = createUser();
user = realm.users().get(id);
userRep = user.toRepresentation();
assertEquals("user1", userRep.getUsername());
assertEquals("user1@localhost", userRep.getEmail());
deleteUser(id);
switchRegistrationEmailAsUsername(false);
switchEditUsernameAllowedOn(false);
id = createUser();
user = realm.users().get(id);
userRep = user.toRepresentation();
assertEquals("user1", userRep.getUsername());
assertEquals("user1@localhost", userRep.getEmail());
}
private void deleteUser(String id) {
try (Response response = realm.users().delete(id)) {
assertEquals(204, response.getStatus());
}
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userResourcePath(id), ResourceType.USER);
} }
@Test @Test
@ -1343,10 +1388,7 @@ public class UserTest extends AbstractAdminTest {
@Test @Test
public void delete() { public void delete() {
String userId = createUser(); String userId = createUser();
try (Response response = realm.users().delete(userId)) { deleteUser(userId);
assertEquals(204, response.getStatus());
}
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userResourcePath(userId), ResourceType.USER);
} }
@Test @Test