8000 [KEYCLOAK-10735] implement search and filter for count endpoint by unly · Pull Request #6140 · keycloak/keycloak · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[KEYCLOAK-10735] implement search and filter for count endpoint #6140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,46 @@ List<UserRepresentation> list(@QueryParam("first") Integer firstResult,
@Consumes(MediaType.APPLICATION_JSON)
Response create(UserRepresentation userRepresentation);

/**
* Returns the number of users that can be viewed.
*
* @return number of users
*/
@Path("count")
@GET
@Produces(MediaType.APPLICATION_JSON)
Integer count();

/**
* Returns the number of users that can be viewed and match the given search criteria.
* If none is specified this is equivalent to {{@link #count()}}.
*
* @param search criteria to search for
* @return number of users matching the search criteria
*/
@Path("count")
@GET
@Produces(MediaType.APPLICATION_JSON)
Integer count(@QueryParam("search") String search);

/**
* Returns the number of users that can be viewed and match the given filters.
* If none of the filters is specified this is equivalent to {{@link #count()}}.
*
* @param last last name field of a user
* @param first first name field of a user
* @param email email field of a user
* @param username username field of a user
* @return number of users matching the given filters
*/
@Path("count")
@GET
@Produces(MediaType.APPLICATION_JSON)
Integer count(@QueryParam("lastName") String last,
@QueryParam("firstName") String first,
8000 @QueryParam("email") String email,
@QueryParam("username") String username);

@Path("{id}")
UserResource get(@PathParam("id") String id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,31 @@ public int getUsersCount(RealmModel realm) {
return getUsersCount(realm, false);
}

@Override
public int getUsersCount(RealmModel realm, Set<String> groupIds) {
return getDelegate().getUsersCount(realm, groupIds);
}

@Override
public int getUsersCount(String search, RealmModel realm) {
return getDelegate().getUsersCount(search, realm);
}

@Override
public int getUsersCount(String search, RealmModel realm, Set<String> groupIds) {
return getDelegate().getUsersCount(search, realm, groupIds);
}

@Override
public int getUsersCount(Map<String, Strin 8000 g> params, RealmModel realm) {
return getDelegate().getUsersCount(params, realm);
}

@Override
public int getUsersCount(Map<String, String> params, RealmModel realm, Set<String> groupIds) {
return getDelegate().getUsersCount(params, realm, groupIds);
}

@Override
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
return getDelegate().getUsers(realm, firstResult, maxResults, includeServiceAccounts);
Expand Down
128 changes: 127 additions & 1 deletion model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
Expand All @@ -65,7 +66,6 @@
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.LockModeType;
import javax.persistence.criteria.Expression;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
Expand Down Expand Up @@ -607,6 +607,132 @@ public int getUsersCount(RealmModel realm) {
return getUsersCount(realm, false);
}

@Override
public int getUsersCount(RealmModel realm, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty()) {
return 0;
}

TypedQuery<Long> query = em.createNamedQuery("userCountInGroups", Long.class);
query.setParameter("realmId", realm.getId());
query.setParameter("groupIds", groupIds);
Long count = query.getSingleResult();

return count.intValue();
}

@Override
public int getUsersCount(String search, RealmModel realm) {
TypedQuery<Long> query = em.createNamedQuery("searchForUserCount", Long.class);
query.setParameter("realmId", realm.getId());
query.setParameter("search", "%" + search.toLowerCase() + "%");
Long count = query.getSingleResult();

return count.intValue();
}

@Override
public int getUsersCount(String search, RealmModel realm, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty()) {
return 0;
}

TypedQuery<Long> query = em.createNamedQuery("searchForUserCountInGroups", Long.class);
query.setParameter("realmId", realm.getId());
query.setParameter("search", "%" + search.toLowerCase() + "%");
query.setParameter("groupIds", groupIds);
Long count = query.getSingleResult();

return count.intValue();
}

@Override
public int getUsersCount(Map<String, String> params, RealmModel realm) {
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Long> userQuery = qb.createQuery(Long.class);
Root<UserEntity> from = userQuery.from(UserEntity.class);
Expression<Long> count = qb.count(from);

userQuery = userQuery.select(count);
List<Predicate> restrictions = new ArrayList<>();
restrictions.add(qb.equal(from.get("realmId"), realm.getId()));

for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key == null || value == null) {
continue;
}

switch (key) {
case UserModel.USERNAME:
restrictions.add(qb.like(from.get("username"), "%" + value + "%"));
break;
case UserModel.FIRST_NAME:
restrictions.add(qb.like(from.get("firstName"), "%" + value + "%"));
break;
case UserModel.LAST_NAME:
restrictions.add(qb.like(from.get("lastName"), "%" + value + "%"));
break;
case UserModel.EMAIL:
restrictions.add(qb.like(from.get("email"), "%" + value + "%"));
break;
}
}

userQuery = userQuery.where(restrictions.toArray(new Predicate[0]));
TypedQuery<Long> query = em.createQuery(userQuery);
Long result = query.getSingleResult();

return result.intValue();
}

@Override
public int getUsersCount(Map<String, String> params, RealmModel realm, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty()) {
return 0;
}

CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Long> userQuery = qb.createQuery(Long.class);
Root<UserGroupMembershipEntity> from = userQuery.from(UserGroupMembershipEntity.class);
Expression<Long> count = qb.count(from.get("user"));
userQuery = userQuery.select(count);

List<Predicate> restrictions = new ArrayList<>();
restrictions.add(qb.equal(from.get("user").get("realmId"), realm.getId()));
restrictions.add(from.get("groupId").in(groupIds));

for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key == null || value == null) {
continu 6D40 e;
}

switch (key) {
case UserModel.USERNAME:
restrictions.add(qb.like(from.get("user").get("username"), "%" + value + "%"));
break;
case UserModel.FIRST_NAME:
restrictions.add(qb.like(from.get("user").get("firstName"), "%" + value + "%"));
break;
case UserModel.LAST_NAME:
restrictions.add(qb.like(from.get("user").get("lastName"), "%" + value + "%"));
break;
case UserModel.EMAIL:
restrictions.add(qb.like(from.get("user").get("email"), "%" + value + "%"));
break;
}
}

userQuery = userQuery.where(restrictions.toArray(new Predicate[0]));
TypedQuery<Long> query = em.createQuery(userQuery);
Long result = query.getSingleResult();

return result.intValue();
}

@Override
public List<UserModel> getUsers(RealmModel realm) {
return getUsers(realm, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
@NamedQuery(name="getAllUsersByRealmExcludeServiceAccount", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) order by u.username"),
@NamedQuery(name="searchForUser", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) and " +
"( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search ) order by u.username"),
@NamedQuery(name="searchForUserCount", query="select count(u) from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) and " +
"( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search )"),
@NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"),
@NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realmId = :realmId"),
@NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realmId = :realmId"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
@NamedQuery(name="deleteUserGroupMembershipByRealm", query="delete from UserGroupMembershipEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId)"),
@NamedQuery(name="deleteUserGroupMembershipsByRealmAndLink", query="delete from UserGroupMembershipEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
@NamedQuery(name="deleteUserGroupMembershipsByGroup", query="delete from UserGroupMembershipEntity m where m.groupId = :groupId"),
@NamedQuery(name="deleteUserGroupMembershipsByUser", query="delete from UserGroupMembershipEntity m where m.user = :user")
@NamedQuery(name="deleteUserGroupMembershipsByUser", query="delete from UserGroupMembershipEntity m where m.user = :user"),
@NamedQuery(name="searchForUserCountInGroups", query="select count(m.user) from UserGroupMembershipEntity m where m.user.realmId = :realmId and (m.user.serviceAccountClientLink is null) and " +
"( lower(m.user.username) like :search or lower(concat(m.user.firstName, ' ', m.user.lastName)) like :search or m.user.email like :search ) and m.group.id in :groupIds"),
@NamedQuery(name="userCountInGroups", query="select count(m.user) from UserGroupMembershipEntity m where m.user.realmId = :realmId and m.group.id in :groupIds")
})
@Table(name="USER_GROUP_MEMBERSHIP")
@Entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Optional capability interface implemented by UserStorageProviders.
Expand All @@ -43,6 +45,99 @@ public interface UserQueryProvider {
*/
int getUsersCount(RealmModel realm);

/**
* Returns the number of users that are in at least one of the groups
* given.
*
* @param realm the realm
* @param groupIds set of groups id to check for
* @return the number of users that are in at least one of the groups
*/
default int getUsersCount(RealmModel realm, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty()) {
return 0;
}

return countUsersInGroups(getUsers(realm), groupIds);
}

/**
* Returns the number of users that match the given criteria.
*
* @param search search criteria
* @param realm the realm
* @return number of users that match the search
*/
default int getUsersCount(String search, RealmModel realm) {
return searchForUser(search, realm).size();
}

/**
* Returns the number of users that match the given criteria and are in
* at least one of the groups given.
*
* @param search search criteria
* @param realm the realm
* @param groupIds set of groups to check for
* @return number of users that match the search and given groups
*/
default int getUsersCount(String search, RealmModel realm, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty()) {
return 0;
}

List<UserModel> users = searchForUser(search, realm);
return countUsersInGroups(users, groupIds);
}

/**
* Returns the number of users that match the given filter parameters.
*
* @param params filter parameters
* @param realm the realm
* @return number of users that match the given filters
*/
default int getUsersCount(Map<String, String> params, RealmModel realm) {
return searchForUser(params, realm).size();
}

/**
* Returns the number of users that match the given filter parameters and is in
* at least one of the given groups.
*
* @param params filter parameters
* @param realm the realm
* @param groupIds set if groups to check for
* @return number of users that match the given filters and groups
*/
default int getUsersCount(Map<String, String> params, RealmModel realm, Set<String> groupIds) {
if (groupIds == null || groupIds.isEmpty()) {
return 0;
}

List<UserModel> users = searchForUser(params, realm);
return countUsersInGroups(users, groupIds);
}

/**
* Returns the number of users from the given list of users that are in at
* least one of the groups given in the groups set.
*
* @param users list of users to check
* @param groupIds id of groups that should be checked for
* @return number of users that are in at least one of the groups
*/
static int countUsersInGroups(List<UserModel> users, Set<String> groupIds) {
return (int) users.stream().filter(u -> {
for (GroupModel group : u.getGroups()) {
if (groupIds.contains(group.getId())) {
return true;
}
}
return false;
}).count();
}

/**
* Returns the number of users.
*
Expand Down Expand Up @@ -138,7 +233,7 @@ default int getUsersCount(RealmModel realm, boolean includeServiceAccount) {

/**
* Get users that belong to a specific role.
*
*
*
*
* @param realm
Expand All @@ -152,7 +247,7 @@ default List<UserModel> getRoleMembers(RealmModel realm, RoleModel role)

/**
* Search for users that have a specific role with a specific roleId.
*
*
*
*
* @param firstResult
Expand Down
Loading
0