Improve user search performance

Removes bulder.lower() from user search queries on email and username.

Closes #8893
This commit is contained in:
Michael Parlee 2022-02-17 10:55:04 -08:00 committed by Hynek Mlnařík
parent 201277b897
commit 722ce950bf
2 changed files with 86 additions and 6 deletions

View file

@ -812,16 +812,22 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
predicates.add(builder.or(getSearchOptionPredicateArray(stringToSearch, builder, root)));
}
break;
case USERNAME:
case FIRST_NAME:
case LAST_NAME:
case EMAIL:
if (Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) {
predicates.add(builder.equal(builder.lower(root.get(key)), value.toLowerCase()));
} else {
predicates.add(builder.like(builder.lower(root.get(key)), "%" + value.toLowerCase() + "%"));
}
break;
case USERNAME:
case EMAIL:
if (Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) {
predicates.add(builder.equal(root.get(key), value.toLowerCase()));
} else {
predicates.add(builder.like(root.get(key), "%" + value.toLowerCase() + "%"));
}
break;
case EMAIL_VERIFIED:
predicates.add(builder.equal(root.get(key), Boolean.parseBoolean(value.toLowerCase())));
break;
@ -1050,8 +1056,8 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
// exact search
value = value.substring(1, value.length() - 1);
orPredicates.add(builder.equal(builder.lower(from.get(USERNAME)), value));
orPredicates.add(builder.equal(builder.lower(from.get(EMAIL)), value));
orPredicates.add(builder.equal(from.get(USERNAME), value));
orPredicates.add(builder.equal(from.get(EMAIL), value));
orPredicates.add(builder.equal(builder.lower(from.get(FIRST_NAME)), value));
orPredicates.add(builder.equal(builder.lower(from.get(LAST_NAME)), value));
} else {
@ -1066,8 +1072,8 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
value += "%";
}
orPredicates.add(builder.like(builder.lower(from.get(USERNAME)), value));
orPredicates.add(builder.like(builder.lower(from.get(EMAIL)), value));
orPredicates.add(builder.like(from.get(USERNAME), value));
orPredicates.add(builder.like(from.get(EMAIL), value));
orPredicates.add(builder.like(builder.lower(from.get(FIRST_NAME)), value));
orPredicates.add(builder.like(builder.lower(from.get(LAST_NAME)), value));
}

View file

@ -862,6 +862,80 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
setEditUsernameAllowed(false);
}
// KEYCLOAK-8893
@Test
public void caseInsensitiveSearchWorksWithoutForcingLowercaseOnEmailAttribute() throws Exception {
setEditUsernameAllowed(true);
setRegistrationEmailAsUsername(true);
profilePage.open();
loginPage.login("test-user@localhost", "password");
assertFalse(driver.findElements(By.id("username")).size() > 0);
profilePage.updateProfile("New First", "New Last", "New-Email@email");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
Assert.assertEquals("New First", profilePage.getFirstName());
Assert.assertEquals("New Last", profilePage.getLastName());
Assert.assertEquals("new-email@email", profilePage.getEmail()); // verify attribute is lower case after save
List<UserRepresentation> list = adminClient.realm("test").users().search(null, null, null, "nEw-emAil@eMail", null, null);
assertEquals(1, list.size());
UserRepresentation user = list.get(0);
assertEquals("new-email@email", user.getUsername());
list = adminClient.realm("test").users().search("nEw-emAil@eMail", null, null, null, null, null);
assertEquals(1, list.size());
user = list.get(0);
assertEquals("new-email@email", user.getUsername());
list = adminClient.realm("test").users().search(null, "new fIrSt", null, null, null, null);
assertEquals(1, list.size());
user = list.get(0);
assertEquals("new-email@email", user.getUsername());
list = adminClient.realm("test").users().search(null, null, "NEw LaST", null, null, null);
assertEquals(1, list.size());
user = list.get(0);
assertEquals("new-email@email", user.getUsername());
assertEquals("New First", user.getFirstName());
assertEquals("New Last", user.getLastName());
list = adminClient.realm("test").users().search("nEw-emAil@eMail", 0, 1);
assertEquals(1, list.size());
user = list.get(0);
assertEquals("new-email@email", user.getUsername());
list = adminClient.realm("test").users().search("nEw", 0, 1);
assertEquals(1, list.size());
user = list.get(0);
assertEquals("new-email@email", user.getUsername());
// Revert
user.setUsername("test-user@localhost");
user.setFirstName("Tom");
user.setLastName("Brady");
user.setEmail("test-user@localhost");
adminClient.realm("test").users().get(user.getId()).update(user);
setRegistrationEmailAsUsername(false);
setEditUsernameAllowed(false);
}
private void setEditUsernameAllowed(boolean allowed) {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setEditUsernameAllowed(allowed);