parent
dc3b037e3a
commit
2493f11331
5 changed files with 79 additions and 22 deletions
|
@ -95,3 +95,15 @@ In version 21.1.0 of Keycloak the new Account Console (version 3) was introduced
|
|||
Two of the variables exposed to the Account Console V2 and V3 templates (`isEventsEnabled` and `isTotpConfigured`) were left unused, and have been removed in this release.
|
||||
|
||||
It is possible that if a developer extended the Account Console theme, he or she could make use of these variables. So make sure that these variables are no longer used if you are extending the base theme.
|
||||
|
||||
= Support for count users based on custom attributes
|
||||
|
||||
The User API now supports querying the number of users based on custom attributes. For that, a new `q` parameter was added to the `/{realm}/users/count` endpoint.
|
||||
|
||||
The `q` parameter expects the following format:
|
||||
|
||||
```
|
||||
q=<name>:<value> <name>:<value> ...
|
||||
```
|
||||
|
||||
Where `<name>` and `<value>` represent the attribute name and value, respectively.
|
||||
|
|
|
@ -308,7 +308,8 @@ public interface UsersResource {
|
|||
@QueryParam("email") String email,
|
||||
@QueryParam("emailVerified") Boolean emailVerified,
|
||||
@QueryParam("username") String username,
|
||||
@QueryParam("enabled") Boolean enabled);
|
||||
@QueryParam("enabled") Boolean enabled,
|
||||
@QueryParam("q") String searchQuery);
|
||||
|
||||
/**
|
||||
* Returns the number of users with the given status for emailVerified.
|
||||
|
|
|
@ -559,20 +559,7 @@ public class MapUserProvider implements UserProvider {
|
|||
return (int) storeWithRealm(realm).getCount(withCriteria(mcb));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||
LOG.tracef("searchForUserStream(%s, %s, %d, %d)%s", realm, search, firstResult, maxResults, getShortStackTrace());
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put(UserModel.SEARCH, search);
|
||||
attributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString());
|
||||
return searchForUserStream(realm, attributes, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
|
||||
LOG.tracef("searchForUserStream(%s, %s, %d, %d)%s", realm, attributes, firstResult, maxResults, getShortStackTrace());
|
||||
|
||||
final DefaultModelCriteria<UserModel> mcb = criteria();
|
||||
private DefaultModelCriteria<UserModel> resolveCriteria(RealmModel realm, Map<String, String> attributes, DefaultModelCriteria<UserModel> mcb) {
|
||||
DefaultModelCriteria<UserModel> criteria = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
|
||||
|
||||
final boolean exactSearch = Boolean.parseBoolean(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()));
|
||||
|
@ -649,6 +636,34 @@ public class MapUserProvider implements UserProvider {
|
|||
break;
|
||||
}
|
||||
}
|
||||
return criteria;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm, Map<String, String> attributes) {
|
||||
LOG.tracef("getUsersCount(%s, %s)%s", realm, attributes, getShortStackTrace());
|
||||
|
||||
final DefaultModelCriteria<UserModel> mcb = criteria();
|
||||
DefaultModelCriteria<UserModel> criteria = resolveCriteria(realm, attributes, mcb);
|
||||
|
||||
return (int) storeWithRealm(realm).getCount(withCriteria(criteria));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||
LOG.tracef("searchForUserStream(%s, %s, %d, %d)%s", realm, search, firstResult, maxResults, getShortStackTrace());
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put(UserModel.SEARCH, search);
|
||||
attributes.put(UserModel.INCLUDE_SERVICE_ACCOUNT, Boolean.FALSE.toString());
|
||||
return searchForUserStream(realm, attributes, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
|
||||
LOG.tracef("searchForUserStream(%s, %s, %d, %d)%s", realm, attributes, firstResult, maxResults, getShortStackTrace());
|
||||
|
||||
final DefaultModelCriteria<UserModel> mcb = criteria();
|
||||
DefaultModelCriteria<UserModel> criteria = resolveCriteria(realm, attributes, mcb);
|
||||
|
||||
// Only return those results that the current user is authorized to view,
|
||||
// i.e. there is an intersection of groups with view permission of the current
|
||||
|
|
|
@ -370,10 +370,15 @@ public class UsersResource {
|
|||
@QueryParam("email") String email,
|
||||
@QueryParam("emailVerified") Boolean emailVerified,
|
||||
@QueryParam("username") String username,
|
||||
@QueryParam("enabled") Boolean enabled) {
|
||||
@QueryParam("enabled") Boolean enabled,
|
||||
@QueryParam("q") String searchQuery) {
|
||||
UserPermissionEvaluator userPermissionEvaluator = auth.users();
|
||||
userPermissionEvaluator.requireQuery();
|
||||
|
||||
Map<String, String> searchAttributes = searchQuery == null
|
||||
? Collections.emptyMap()
|
||||
: SearchQueryUtils.getFields(searchQuery);
|
||||
|
||||
if (search != null) {
|
||||
if (search.startsWith(SEARCH_ID_PARAMETER)) {
|
||||
UserModel userModel = session.users().getUserById(realm, search.substring(SEARCH_ID_PARAMETER.length()).trim());
|
||||
|
@ -383,7 +388,7 @@ public class UsersResource {
|
|||
} else {
|
||||
return session.users().getUsersCount(realm, search.trim(), auth.groups().getGroupsWithViewPermission());
|
||||
}
|
||||
} else if (last != null || first != null || email != null || username != null || emailVerified != null || enabled != null) {
|
||||
} else if (last != null || first != null || email != null || username != null || emailVerified != null || enabled != null || !searchAttributes.isEmpty()) {
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
if (last != null) {
|
||||
parameters.put(UserModel.LAST_NAME, last);
|
||||
|
@ -403,6 +408,8 @@ public class UsersResource {
|
|||
if (enabled != null) {
|
||||
parameters.put(UserModel.ENABLED, enabled.toString());
|
||||
}
|
||||
parameters.putAll(searchAttributes);
|
||||
|
||||
if (userPermissionEvaluator.canView()) {
|
||||
return session.users().getUsersCount(realm, parameters);
|
||||
} else {
|
||||
|
|
|
@ -640,6 +640,28 @@ public class UserTest extends AbstractAdminTest {
|
|||
return ids;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countByAttribute() {
|
||||
createUsers();
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("test1", "test2");
|
||||
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(0));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("test", "test1");
|
||||
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(1));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("test", "test2");
|
||||
attributes.put("attr", "common");
|
||||
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(1));
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.put("attr", "common");
|
||||
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countUsersByEnabledFilter() {
|
||||
|
||||
|
@ -666,16 +688,16 @@ public class UserTest extends AbstractAdminTest {
|
|||
Boolean disabled = false;
|
||||
|
||||
// count all users with @enabledfilter.com
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, null, null), is(3));
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, null, null, null), is(3));
|
||||
|
||||
// count users that are enabled and have username enabled1
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, "enabled1", enabled),is(1));
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, "enabled1", enabled, null),is(1));
|
||||
|
||||
// count users that are disabled
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, null, disabled), is(1));
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, null, disabled, null), is(1));
|
||||
|
||||
// count users that are enabled
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, null, enabled), is(2));
|
||||
assertThat(realm.users().count(null, null, null, "@enabledfilter.com", null, null, enabled, null), is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue