diff --git a/docs/documentation/release_notes/topics/25_0_0.adoc b/docs/documentation/release_notes/topics/25_0_0.adoc index a2f306da4cc..6f9226dc93a 100644 --- a/docs/documentation/release_notes/topics/25_0_0.adoc +++ b/docs/documentation/release_notes/topics/25_0_0.adoc @@ -4,4 +4,8 @@ The following methods for setting custom cookies have been removed: * `LocaleSelectorProvider.KEYCLOAK_LOCALE` - replaced by `CookieType.LOCALE` * `HttpCookie` - replaced by `NewCookie.Builder` -* `HttpResponse.setCookieIfAbsent(HttpCookie cookie)` - replaced by `HttpResponse.setCookieIfAbsent(NewCookie cookie)` \ No newline at end of file +* `HttpResponse.setCookieIfAbsent(HttpCookie cookie)` - replaced by `HttpResponse.setCookieIfAbsent(NewCookie cookie)` + += Searching by user attribute no longer case insensitive + +When searching for users by user attribute, Keycloak no longer searches for user attribute names forcing lower case comparisons. The goal of this change was to speed up searches by using Keycloak's native index on the user attribute table. If your database collation is case-insensitive, your search results will stay the same. If your database collation is case-sensitive, you might see less search results than before. diff --git a/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc index 6d9e4d8ebf5..0d80e09de32 100644 --- a/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-25_0_0.adoc @@ -30,3 +30,7 @@ The module `org.keycloak:keycloak-model-legacy` module was deprecated in a previ = Removed offline session preloading The old behavior to preload offline sessions at startup is now removed after it has been deprecated in the previous release. + += Removing custom user attribute indexes + +When searching for users by user attribute, Keycloak no longer searches for user attribute names forcing lower case comparisons. This means Keycloak's native index on the user attribute table will now be used when searching. If you have created your own index based on `lower(name)`to speed up searches, you can now remove it. diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java index 61380493751..907d6cf35f9 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java @@ -1001,15 +1001,14 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { // All unknown attributes will be assumed as custom attributes default: Join attributesJoin = root.join("attributes", JoinType.LEFT); - if (value.length() > 255) { customLongValueSearchAttributes.put(key, value); attributePredicates.add(builder.and( - builder.equal(builder.lower(attributesJoin.get("name")), key.toLowerCase()), + builder.equal(attributesJoin.get("name"), key), builder.equal(attributesJoin.get("longValueHashLowerCase"), JpaHashUtils.hashForAttributeValueLowerCase(value)))); } else { attributePredicates.add(builder.and( - builder.equal(builder.lower(attributesJoin.get("name")), key.toLowerCase()), + builder.equal(attributesJoin.get("name"), key), builder.equal(builder.lower(attributesJoin.get("value")), value.toLowerCase()))); } break; 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 7b7f56dcf93..78f533ed8a3 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 @@ -927,7 +927,7 @@ public class UserTest extends AbstractAdminTest { createUsers(); Map attributes = new HashMap<>(); - attributes.put("attr", "common"); + attributes.put("attr", "Common"); for (int i = 1; i < 10; i++) { List users = realm.users().searchByAttributes(i - 1, 1, null, false, mapToSearchQuery(attributes)); assertEquals(1, users.size());