Added new searchByAttributes function to UsersResource with the exact parameter

Closes #39609

Signed-off-by: Michael-AT-Corporation <michael-hu@ooutlook.de>
This commit is contained in:
Michael-AT-Corporation
2025-06-02 19:12:28 +02:00
committed by Pedro Igor
parent c315762883
commit ff9e7c2371
4 changed files with 76 additions and 2 deletions
@@ -0,0 +1,36 @@
== Breaking changes
Breaking changes are identified as requiring changes from existing users to their configurations.
In minor or patch releases we will only do breaking changes to fix bugs.
=== <TODO>
== Notable changes
Notable changes where an internal behavior changed to prevent common misconfigurations, fix bugs or simplify running {project_name}.
=== Usage of the `exact` request parameter when searching users by attributes
If you are querying users by attributes through the User API where you want to fetch users that match a specific attribute key (regardless the value),
you should consider setting the `exact` request parameter to `false` when invoking the `/admin/realms/{realm}/users` using
the `GET` method.
For instance, searching for all users with the attribute `myattribute` set should be done as follows:
```
GET /admin/realms/{realm}/users?exact=false&q=myattribute:
```
The {project_name} Admin Client is also updated with a new method to search users by attribute using the `exact` request parameter.
== Deprecated features
The following sections provide details on deprecated features.
=== <TODO>
== Removed features
The following features have been removed from this release.
=== <TODO>
@@ -1,9 +1,9 @@
[[migration-changes]]
== Migration Changes
=== Migrating to 26.3.1
=== Migrating to 26.4.0
include::changes-26_3_1.adoc[leveloffset=2]
include::changes-26_4_0.adoc[leveloffset=2]
=== Migrating to 26.3.0
@@ -129,6 +129,16 @@ public interface UsersResource {
@QueryParam("briefRepresentation") Boolean briefRepresentation,
@QueryParam("q") String searchQuery);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
List<UserRepresentation> searchByAttributes(@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("enabled") Boolean enabled,
@QueryParam("exact") Boolean exact,
@QueryParam("briefRepresentation") Boolean briefRepresentation,
@QueryParam("q") String searchQuery);
@GET
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> search(@QueryParam("username") String username, @QueryParam("exact") Boolean exact);
@@ -127,6 +127,7 @@ import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -961,6 +962,33 @@ public class UserTest extends AbstractAdminTest {
}
}
@Test
public void searchByAttributesForAnyValue() {
createUser(UserBuilder.create().username("user-0").addAttribute("attr", "common").build());
createUser(UserBuilder.create().username("user-1").addAttribute("test", "common").build());
createUser(UserBuilder.create().username("user-2").addAttribute("test", "common").addAttribute("attr", "common").build());
Map<String, String> attributes = new HashMap<>();
attributes.put("attr", "");
// exact needs to be set to false to match for any users with the attribute attr
List<UserRepresentation> users = realm.users().searchByAttributes(-1, -1, null, false, false, mapToSearchQuery(attributes));
assertEquals(2, users.size());
assertTrue(users.stream().allMatch(r -> Set.of("user-0", "user-2").contains(r.getUsername())));
attributes = new HashMap<>();
attributes.put("test", "");
users = realm.users().searchByAttributes(-1, -1, null, false, false, mapToSearchQuery(attributes));
assertEquals(2, users.size());
assertTrue(users.stream().allMatch(r -> Set.of("user-1", "user-2").contains(r.getUsername())));
attributes = new HashMap<>();
attributes.put("test", "");
attributes.put("attr", "");
users = realm.users().searchByAttributes(-1, -1, null, false, false, mapToSearchQuery(attributes));
assertEquals(1, users.size());
assertTrue(users.stream().allMatch(r -> "user-2".equals(r.getUsername())));
}
@Test
public void storeAndReadUserWithLongAttributeValue() {
String longValue = RandomStringUtils.random(Integer.parseInt(DefaultAttributes.DEFAULT_MAX_LENGTH_ATTRIBUTES), true, true);