enhance existing group search functionality allow exact name search keycloak/keycloak#13973
Co-authored-by: Abhijeet Gandhewar <agandhew@redhat.com>
This commit is contained in:
parent
a20d6e2f1f
commit
1eb7e95b97
16 changed files with 238 additions and 80 deletions
|
@ -79,6 +79,25 @@ public interface GroupsResource {
|
||||||
@QueryParam("first") Integer first,
|
@QueryParam("first") Integer first,
|
||||||
@QueryParam("max") Integer max,
|
@QueryParam("max") Integer max,
|
||||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get groups by pagination params.
|
||||||
|
* @param search search string for group
|
||||||
|
* @param exact exact match for search
|
||||||
|
* @param first index of the first element
|
||||||
|
* @param max max number of occurrences
|
||||||
|
* @param briefRepresentation if false, return groups with their attributes
|
||||||
|
* @return A list containing the slice of all groups.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
List<GroupRepresentation> groups(@QueryParam("search") String search,
|
||||||
|
@QueryParam("exact") Boolean exact,
|
||||||
|
@QueryParam("first") Integer first,
|
||||||
|
@QueryParam("max") Integer max,
|
||||||
|
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts all groups.
|
* Counts all groups.
|
||||||
* @return A map containing key "count" with number of groups as value.
|
* @return A map containing key "count" with number of groups as value.
|
||||||
|
|
|
@ -1478,8 +1478,9 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
|
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
|
||||||
return cacheSession.searchForGroupByNameStream(this, search, first, max);
|
return cacheSession.searchForGroupByNameStream( this, search, false, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1025,7 +1025,12 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
|
||||||
return getGroupDelegate().searchForGroupByNameStream(realm, search, first, max);
|
return getGroupDelegate().searchForGroupByNameStream(realm, search, false, first, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults) {
|
||||||
|
return getGroupDelegate().searchForGroupByNameStream(realm, search, exact, firstResult, maxResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -40,6 +40,7 @@ import javax.persistence.criteria.Join;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||||
|
@ -937,10 +938,19 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
|
||||||
TypedQuery<String> query = em.createNamedQuery("getGroupIdsByNameContaining", String.class)
|
return searchForGroupByNameStream(realm, search, false, first, max);
|
||||||
.setParameter("realm", realm.getId())
|
}
|
||||||
.setParameter("search", search);
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer first, Integer max) {
|
||||||
|
TypedQuery<String> query;
|
||||||
|
if (Boolean.TRUE.equals(exact)) {
|
||||||
|
query = em.createNamedQuery("getGroupIdsByName", String.class);
|
||||||
|
} else {
|
||||||
|
query = em.createNamedQuery("getGroupIdsByNameContaining", String.class);
|
||||||
|
}
|
||||||
|
query.setParameter("realm", realm.getId())
|
||||||
|
.setParameter("search", search);
|
||||||
Stream<String> groups = paginateQuery(query, first, max).getResultStream();
|
Stream<String> groups = paginateQuery(query, first, max).getResultStream();
|
||||||
|
|
||||||
return closing(groups.map(id -> {
|
return closing(groups.map(id -> {
|
||||||
|
@ -951,7 +961,6 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
|
||||||
return groupById;
|
return groupById;
|
||||||
}).sorted(GroupModel.COMPARE_BY_NAME).distinct());
|
}).sorted(GroupModel.COMPARE_BY_NAME).distinct());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<GroupModel> searchGroupsByAttributes(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
|
public Stream<GroupModel> searchGroupsByAttributes(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
|
||||||
Map<String, String> filteredAttributes = groupSearchableAttributes == null || groupSearchableAttributes.isEmpty()
|
Map<String, String> filteredAttributes = groupSearchableAttributes == null || groupSearchableAttributes.isEmpty()
|
||||||
|
|
|
@ -1938,8 +1938,9 @@ public class RealmAdapter implements LegacyRealmModel, JpaModel<RealmEntity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
|
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
|
||||||
return session.groups().searchForGroupByNameStream(this, search, first, max);
|
return session.groups().searchForGroupByNameStream(this, search, false, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.LinkedList;
|
||||||
@NamedQuery(name="getGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm = :realm order by u.name ASC"),
|
@NamedQuery(name="getGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm = :realm order by u.name ASC"),
|
||||||
@NamedQuery(name="getGroupIdsByNameContaining", query="select u.id from GroupEntity u where u.realm = :realm and u.name like concat('%',:search,'%') order by u.name ASC"),
|
@NamedQuery(name="getGroupIdsByNameContaining", query="select u.id from GroupEntity u where u.realm = :realm and u.name like concat('%',:search,'%') order by u.name ASC"),
|
||||||
@NamedQuery(name="getGroupIdsByNameContainingFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids order by u.name ASC"),
|
@NamedQuery(name="getGroupIdsByNameContainingFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids order by u.name ASC"),
|
||||||
|
@NamedQuery(name="getGroupIdsByName", query="select u.id from GroupEntity u where u.realm = :realm and u.name = :search order by u.name ASC"),
|
||||||
@NamedQuery(name="getGroupIdsFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and u.id in :ids order by u.name ASC"),
|
@NamedQuery(name="getGroupIdsFromIdList", query="select u.id from GroupEntity u where u.realm = :realm and u.id in :ids order by u.name ASC"),
|
||||||
@NamedQuery(name="getGroupCountByNameContainingFromIdList", query="select count(u) from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids"),
|
@NamedQuery(name="getGroupCountByNameContainingFromIdList", query="select count(u) from GroupEntity u where u.realm = :realm and lower(u.name) like lower(concat('%',:search,'%')) and u.id in :ids"),
|
||||||
@NamedQuery(name="getTopLevelGroupIds", query="select u.id from GroupEntity u where u.parentId = :parent and u.realm = :realm order by u.name ASC"),
|
@NamedQuery(name="getTopLevelGroupIds", query="select u.id from GroupEntity u where u.parentId = :parent and u.realm = :realm order by u.name ASC"),
|
||||||
|
|
|
@ -64,12 +64,9 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||||
Stream<GroupModel> local = localStorage().searchForGroupByNameStream(realm, search, firstResult, maxResults);
|
return searchForGroupByNameStream(realm, search, false, firstResult, maxResults);
|
||||||
Stream<GroupModel> ext = flatMapEnabledStorageProvidersWithTimeout(realm, GroupLookupProvider.class,
|
|
||||||
p -> p.searchForGroupByNameStream(realm, search, firstResult, maxResults));
|
|
||||||
|
|
||||||
return Stream.concat(local, ext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -81,6 +78,22 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
|
||||||
return Stream.concat(local, ext);
|
return Stream.concat(local, ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtaining groups from an external client storage is time-bounded. In case the external group storage
|
||||||
|
* isn't available at least groups from a local storage are returned. For this purpose
|
||||||
|
* the {@link org.keycloak.services.DefaultKeycloakSessionFactory#getClientStorageProviderTimeout()} property is used.
|
||||||
|
* Default value is 3000 milliseconds and it's configurable.
|
||||||
|
* See {@link org.keycloak.services.DefaultKeycloakSessionFactory} for details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults) {
|
||||||
|
Stream<GroupModel> local = localStorage().searchForGroupByNameStream(realm, search, exact, firstResult, maxResults);
|
||||||
|
Stream<GroupModel> ext = flatMapEnabledStorageProvidersWithTimeout(realm, GroupLookupProvider.class,
|
||||||
|
p -> p.searchForGroupByNameStream(realm, search, exact, firstResult, maxResults));
|
||||||
|
|
||||||
|
return Stream.concat(local, ext);
|
||||||
|
}
|
||||||
/* GROUP PROVIDER METHODS - provided only by local storage (e.g. not supported by storage providers) */
|
/* GROUP PROVIDER METHODS - provided only by local storage (e.g. not supported by storage providers) */
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.models.map.group;
|
package org.keycloak.models.map.group;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.GroupModel.SearchableFields;
|
import org.keycloak.models.GroupModel.SearchableFields;
|
||||||
|
@ -167,24 +168,34 @@ public class MapGroupProvider implements GroupProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||||
LOG.tracef("searchForGroupByNameStream(%s, %s, %d, %d)%s", realm, search, firstResult, maxResults, getShortStackTrace());
|
return searchForGroupByNameStream(realm, search, false, firstResult, maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults) {
|
||||||
|
LOG.tracef("searchForGroupByNameStream(%s, %s, %s, %b, %d, %d)%s", realm, session, search, exact, firstResult, maxResults, getShortStackTrace());
|
||||||
|
|
||||||
|
|
||||||
DefaultModelCriteria<GroupModel> mcb = criteria();
|
DefaultModelCriteria<GroupModel> mcb = criteria();
|
||||||
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
if (exact != null && exact.equals(Boolean.TRUE)) {
|
||||||
.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
|
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||||
|
.compare(SearchableFields.NAME, Operator.EQ, search);
|
||||||
|
} else {
|
||||||
|
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||||
|
.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
|
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
|
||||||
.map(MapGroupEntity::getId)
|
.map(MapGroupEntity::getId)
|
||||||
.map(id -> {
|
.map(id -> {
|
||||||
GroupModel groupById = session.groups().getGroupById(realm, id);
|
GroupModel groupById = session.groups().getGroupById(realm, id);
|
||||||
while (Objects.nonNull(groupById.getParentId())) {
|
while (Objects.nonNull(groupById.getParentId())) {
|
||||||
groupById = session.groups().getGroupById(realm, groupById.getParentId());
|
groupById = session.groups().getGroupById(realm, groupById.getParentId());
|
||||||
}
|
}
|
||||||
return groupById;
|
return groupById;
|
||||||
}).sorted(GroupModel.COMPARE_BY_NAME).distinct();
|
}).sorted(GroupModel.COMPARE_BY_NAME).distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1425,8 +1425,9 @@ public class MapRealmAdapter extends AbstractRealmModel<MapRealmEntity> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
|
public Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max) {
|
||||||
return session.groups().searchForGroupByNameStream(this, search, first, max);
|
return session.groups().searchForGroupByNameStream(this, search, false, first, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -393,8 +393,8 @@ public class MapRealmProvider implements RealmProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults) {
|
||||||
return session.groups().searchForGroupByNameStream(realm, search, firstResult, maxResults);
|
return session.groups().searchForGroupByNameStream(realm, search, exact, firstResult, maxResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -177,9 +177,15 @@ public class ModelToRepresentation {
|
||||||
.map(g -> toGroupHierarchy(g, full, attributes));
|
.map(g -> toGroupHierarchy(g, full, attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static Stream<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, String search, Integer first, Integer max) {
|
public static Stream<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, String search, Integer first, Integer max) {
|
||||||
return realm.searchForGroupByNameStream(search, first, max)
|
return realm.searchForGroupByNameStream(search, first, max)
|
||||||
.map(g -> toGroupHierarchy(g, full, search));
|
.map(g -> toGroupHierarchy(g, full, search));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream<GroupRepresentation> searchForGroupByName(KeycloakSession session, RealmModel realm, boolean full, String search, Boolean exact, Integer first, Integer max) {
|
||||||
|
return session.groups().searchForGroupByNameStream(realm, search, exact, first, max)
|
||||||
|
.map(g -> toGroupHierarchy(g, full, search, exact));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<GroupRepresentation> searchForGroupByName(UserModel user, boolean full, String search, Integer first, Integer max) {
|
public static Stream<GroupRepresentation> searchForGroupByName(UserModel user, boolean full, String search, Integer first, Integer max) {
|
||||||
|
@ -211,11 +217,16 @@ public class ModelToRepresentation {
|
||||||
return toGroupHierarchy(group, full, (String) null);
|
return toGroupHierarchy(group, full, (String) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full, String search) {
|
public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full, String search) {
|
||||||
|
return toGroupHierarchy(group, full, search, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupRepresentation toGroupHierarchy(GroupModel group, boolean full, String search, Boolean exact) {
|
||||||
GroupRepresentation rep = toRepresentation(group, full);
|
GroupRepresentation rep = toRepresentation(group, full);
|
||||||
List<GroupRepresentation> subGroups = group.getSubGroupsStream()
|
List<GroupRepresentation> subGroups = group.getSubGroupsStream()
|
||||||
.filter(g -> groupMatchesSearchOrIsPathElement(g, search))
|
.filter(g -> groupMatchesSearchOrIsPathElement(g, search, exact))
|
||||||
.map(subGroup -> toGroupHierarchy(subGroup, full, search)).collect(Collectors.toList());
|
.map(subGroup -> toGroupHierarchy(subGroup, full, search, exact)).collect(Collectors.toList());
|
||||||
rep.setSubGroups(subGroups);
|
rep.setSubGroups(subGroups);
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
@ -228,16 +239,24 @@ public class ModelToRepresentation {
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean groupMatchesSearchOrIsPathElement(GroupModel group, String search) {
|
private static boolean groupMatchesSearchOrIsPathElement(GroupModel group, String search, Boolean exact) {
|
||||||
if (StringUtil.isBlank(search)) {
|
if (StringUtil.isBlank(search)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (group.getName().contains(search)) {
|
if(exact !=null && exact.equals(true)){
|
||||||
return true;
|
if (group.getName().equals(search)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (group.getName().contains(search)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return group.getSubGroupsStream().findAny().isPresent();
|
return group.getSubGroupsStream().findAny().isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel user) {
|
public static UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
UserRepresentation rep = new UserRepresentation();
|
UserRepresentation rep = new UserRepresentation();
|
||||||
rep.setId(user.getId());
|
rep.setId(user.getId());
|
||||||
|
|
|
@ -846,7 +846,7 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
Stream<GroupModel> getTopLevelGroupsStream(Integer first, Integer max);
|
Stream<GroupModel> getTopLevelGroupsStream(Integer first, Integer max);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #searchForGroupByNameStream(String, Integer, Integer) searchForGroupByName} instead.
|
* @deprecated Use {@link GroupProvider#searchForGroupByNameStream(RealmModel, String, Boolean, Integer, Integer)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
default List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
|
default List<GroupModel> searchForGroupByName(String search, Integer first, Integer max) {
|
||||||
|
@ -859,7 +859,9 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
* @param first {@code Integer} Index of the first desired group. Ignored if negative or {@code null}.
|
* @param first {@code Integer} Index of the first desired group. Ignored if negative or {@code null}.
|
||||||
* @param max {@code Integer} Maximum number of returned groups. Ignored if negative or {@code null}.
|
* @param max {@code Integer} Maximum number of returned groups. Ignored if negative or {@code null}.
|
||||||
* @return Stream of {@link GroupModel}. Never returns {@code null}.
|
* @return Stream of {@link GroupModel}. Never returns {@code null}.
|
||||||
|
* @deprecated Use {@link GroupProvider#searchForGroupByNameStream(RealmModel, String, Boolean, Integer, Integer)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max);
|
Stream<GroupModel> searchForGroupByNameStream(String search, Integer first, Integer max);
|
||||||
|
|
||||||
boolean removeGroup(GroupModel group);
|
boolean removeGroup(GroupModel group);
|
||||||
|
|
|
@ -62,8 +62,12 @@ public interface GroupLookupProvider {
|
||||||
* @param maxResults Maximum number of results to return. Ignored if negative or {@code null}.
|
* @param maxResults Maximum number of results to return. Ignored if negative or {@code null}.
|
||||||
* @return Stream of root groups that have the given string in their name themself or a group in their child-collection has.
|
* @return Stream of root groups that have the given string in their name themself or a group in their child-collection has.
|
||||||
* The returned hierarchy contains siblings that do not necessarily have a matching name. Never returns {@code null}.
|
* The returned hierarchy contains siblings that do not necessarily have a matching name. Never returns {@code null}.
|
||||||
|
* @deprecated Use {@link #searchForGroupByNameStream(RealmModel, String, Boolean, Integer, Integer)} instead.
|
||||||
*/
|
*/
|
||||||
Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults);
|
@Deprecated
|
||||||
|
default Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||||
|
return searchForGroupByNameStream(realm, search, false, firstResult, maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the groups filtered by attribute names and attribute values for the given realm.
|
* Returns the groups filtered by attribute names and attribute values for the given realm.
|
||||||
|
@ -76,4 +80,20 @@ public interface GroupLookupProvider {
|
||||||
*/
|
*/
|
||||||
Stream<GroupModel> searchGroupsByAttributes(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults);
|
Stream<GroupModel> searchGroupsByAttributes(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the group hierarchy with the given string in name for the given realm.
|
||||||
|
*
|
||||||
|
* For a matching group node the parent group is fetched by id (with all children) and added to the result stream.
|
||||||
|
* This is done until the group node does not have a parent (root group)
|
||||||
|
*
|
||||||
|
* @param realm Realm.
|
||||||
|
* @param search Case sensitive searched string.
|
||||||
|
* @param exact Boolean which defines wheather search param should be matched exactly.
|
||||||
|
* @param firstResult First result to return. Ignored if negative or {@code null}.
|
||||||
|
* @param maxResults Maximum number of results to return. Ignored if negative or {@code null}.
|
||||||
|
* @return Stream of root groups that have the given string in their name themself or a group in their child-collection has.
|
||||||
|
* The returned hierarchy contains siblings that do not necessarily have a matching name. Never returns {@code null}.
|
||||||
|
*/
|
||||||
|
Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ public class GroupsResource {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Stream<GroupRepresentation> getGroups(@QueryParam("search") String search,
|
public Stream<GroupRepresentation> getGroups(@QueryParam("search") String search,
|
||||||
@QueryParam("q") String searchQuery,
|
@QueryParam("q") String searchQuery,
|
||||||
|
@QueryParam("exact") @DefaultValue("false") Boolean exact,
|
||||||
@QueryParam("first") Integer firstResult,
|
@QueryParam("first") Integer firstResult,
|
||||||
@QueryParam("max") Integer maxResults,
|
@QueryParam("max") Integer maxResults,
|
||||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
||||||
|
@ -86,7 +87,7 @@ public class GroupsResource {
|
||||||
Map<String, String> attributes = SearchQueryUtils.getFields(searchQuery);
|
Map<String, String> attributes = SearchQueryUtils.getFields(searchQuery);
|
||||||
return ModelToRepresentation.searchGroupsByAttributes(session, realm, !briefRepresentation, attributes, firstResult, maxResults);
|
return ModelToRepresentation.searchGroupsByAttributes(session, realm, !briefRepresentation, attributes, firstResult, maxResults);
|
||||||
} else if (Objects.nonNull(search)) {
|
} else if (Objects.nonNull(search)) {
|
||||||
return ModelToRepresentation.searchForGroupByName(realm, !briefRepresentation, search.trim(), firstResult, maxResults);
|
return ModelToRepresentation.searchForGroupByName(session, realm, !briefRepresentation, search.trim(), exact, firstResult, maxResults);
|
||||||
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
|
} else if(Objects.nonNull(firstResult) && Objects.nonNull(maxResults)) {
|
||||||
return ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults);
|
return ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.federation;
|
package org.keycloak.testsuite.federation;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
|
@ -53,15 +54,27 @@ public class HardcodedGroupStorageProvider implements GroupStorageProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||||
|
return searchForGroupByNameStream(realm, search, false, firstResult, maxResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults) {
|
||||||
if (Boolean.parseBoolean(component.getConfig().getFirst(HardcodedGroupStorageProviderFactory.DELAYED_SEARCH))) try {
|
if (Boolean.parseBoolean(component.getConfig().getFirst(HardcodedGroupStorageProviderFactory.DELAYED_SEARCH))) try {
|
||||||
Thread.sleep(5000l);
|
Thread.sleep(5000l);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
Logger.getLogger(HardcodedGroupStorageProvider.class).warn(ex.getCause());
|
Logger.getLogger(HardcodedGroupStorageProvider.class).warn(ex.getCause());
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
if (search != null && this.groupName.toLowerCase().contains(search.toLowerCase())) {
|
if(BooleanUtils.isTrue(exact)){
|
||||||
return Stream.of(new HardcodedGroupAdapter(realm));
|
if (search != null && this.groupName.equals(search)) {
|
||||||
|
return Stream.of(new HardcodedGroupAdapter(realm));
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
if (search != null && this.groupName.toLowerCase().contains(search.toLowerCase())) {
|
||||||
|
return Stream.of(new HardcodedGroupAdapter(realm));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
|
import org.keycloak.testsuite.updaters.Creator;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
import org.keycloak.testsuite.util.RoleBuilder;
|
import org.keycloak.testsuite.util.RoleBuilder;
|
||||||
|
@ -67,14 +68,22 @@ import javax.ws.rs.ClientErrorException;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.keycloak.testsuite.Assert.assertNames;
|
import static org.keycloak.testsuite.Assert.assertNames;
|
||||||
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
|
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
|
||||||
|
|
||||||
|
@ -182,7 +191,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
response.close();
|
response.close();
|
||||||
assertEquals(409, response.getStatus()); // conflict status 409 - same name not allowed
|
assertEquals(409, response.getStatus()); // conflict status 409 - same name not allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
// KEYCLOAK-11412 Unintended Groups with same names
|
// KEYCLOAK-11412 Unintended Groups with same names
|
||||||
public void doNotAllowSameGroupNameAtSameLevelWhenUpdatingName() throws Exception {
|
public void doNotAllowSameGroupNameAtSameLevelWhenUpdatingName() throws Exception {
|
||||||
|
@ -195,9 +204,9 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
GroupRepresentation anotherTopGroup = new GroupRepresentation();
|
GroupRepresentation anotherTopGroup = new GroupRepresentation();
|
||||||
anotherTopGroup.setName("top2");
|
anotherTopGroup.setName("top2");
|
||||||
anotherTopGroup = createGroup(realm, anotherTopGroup);
|
anotherTopGroup = createGroup(realm, anotherTopGroup);
|
||||||
|
|
||||||
anotherTopGroup.setName("top1");
|
anotherTopGroup.setName("top1");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
realm.groups().group(anotherTopGroup.getId()).update(anotherTopGroup);
|
realm.groups().group(anotherTopGroup.getId()).update(anotherTopGroup);
|
||||||
Assert.fail("Expected ClientErrorException");
|
Assert.fail("Expected ClientErrorException");
|
||||||
|
@ -215,14 +224,14 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
addSubGroup(realm, topGroup, anotherlevel2Group);
|
addSubGroup(realm, topGroup, anotherlevel2Group);
|
||||||
|
|
||||||
anotherlevel2Group.setName("level2-1");
|
anotherlevel2Group.setName("level2-1");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
realm.groups().group(anotherlevel2Group.getId()).update(anotherlevel2Group);
|
realm.groups().group(anotherlevel2Group.getId()).update(anotherlevel2Group);
|
||||||
Assert.fail("Expected ClientErrorException");
|
Assert.fail("Expected ClientErrorException");
|
||||||
} catch (ClientErrorException e) {
|
} catch (ClientErrorException e) {
|
||||||
// conflict status 409 - same name not allowed
|
// conflict status 409 - same name not allowed
|
||||||
assertEquals("HTTP 409 Conflict", e.getMessage());
|
assertEquals("HTTP 409 Conflict", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -295,7 +304,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
Assert.fail("Creating a group with empty name should fail");
|
Assert.fail("Creating a group with empty name should fail");
|
||||||
}
|
}
|
||||||
} catch (Exception expected) {
|
} catch (Exception expected) {
|
||||||
Assert.assertNotNull(expected);
|
assertNotNull(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
group.setName(null);
|
group.setName(null);
|
||||||
|
@ -304,7 +313,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
Assert.fail("Creating a group with null name should fail");
|
Assert.fail("Creating a group with null name should fail");
|
||||||
}
|
}
|
||||||
} catch (Exception expected) {
|
} catch (Exception expected) {
|
||||||
Assert.assertNotNull(expected);
|
assertNotNull(expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +336,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
realm.groups().group(groupId).update(group);
|
realm.groups().group(groupId).update(group);
|
||||||
Assert.fail("Updating a group with empty name should fail");
|
Assert.fail("Updating a group with empty name should fail");
|
||||||
} catch(Exception expected) {
|
} catch(Exception expected) {
|
||||||
Assert.assertNotNull(expected);
|
assertNotNull(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -335,7 +344,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
realm.groups().group(groupId).update(group);
|
realm.groups().group(groupId).update(group);
|
||||||
Assert.fail("Updating a group with null name should fail");
|
Assert.fail("Updating a group with null name should fail");
|
||||||
} catch(Exception expected) {
|
} catch(Exception expected) {
|
||||||
Assert.assertNotNull(expected);
|
assertNotNull(expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +388,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
});
|
});
|
||||||
|
|
||||||
level2Group = realm.getGroupByPath("/top/level2");
|
level2Group = realm.getGroupByPath("/top/level2");
|
||||||
Assert.assertNotNull(level2Group);
|
assertNotNull(level2Group);
|
||||||
roles.clear();
|
roles.clear();
|
||||||
roles.add(level2Role);
|
roles.add(level2Role);
|
||||||
realm.groups().group(level2Group.getId()).roles().realmLevel().add(roles);
|
realm.groups().group(level2Group.getId()).roles().realmLevel().add(roles);
|
||||||
|
@ -392,7 +401,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
assertAdminEvents.assertEvent(testRealmId, OperationType.CREATE, AdminEventPaths.groupSubgroupsPath(level2Group.getId()), level3Group, ResourceType.GROUP);
|
assertAdminEvents.assertEvent(testRealmId, OperationType.CREATE, AdminEventPaths.groupSubgroupsPath(level2Group.getId()), level3Group, ResourceType.GROUP);
|
||||||
|
|
||||||
level3Group = realm.getGroupByPath("/top/level2/level3");
|
level3Group = realm.getGroupByPath("/top/level2/level3");
|
||||||
Assert.assertNotNull(level3Group);
|
assertNotNull(level3Group);
|
||||||
roles.clear();
|
roles.clear();
|
||||||
roles.add(level3Role);
|
roles.add(level3Role);
|
||||||
realm.groups().group(level3Group.getId()).roles().realmLevel().add(roles);
|
realm.groups().group(level3Group.getId()).roles().realmLevel().add(roles);
|
||||||
|
@ -473,7 +482,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
}
|
}
|
||||||
catch (NotFoundException e) {}
|
catch (NotFoundException e) {}
|
||||||
|
|
||||||
Assert.assertNull(login("direct-login", "resource-owner", "secret", user.getId()).getRealmAccess());
|
assertNull(login("direct-login", "resource-owner", "secret", user.getId()).getRealmAccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -489,7 +498,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
createGroup(realm, group);
|
createGroup(realm, group);
|
||||||
group = realm.getGroupByPath("/" + groupName);
|
group = realm.getGroupByPath("/" + groupName);
|
||||||
|
|
||||||
Assert.assertNotNull(group);
|
assertNotNull(group);
|
||||||
assertThat(group.getName(), is(groupName));
|
assertThat(group.getName(), is(groupName));
|
||||||
assertThat(group.getAttributes().keySet(), containsInAnyOrder("attr1", "attr2"));
|
assertThat(group.getAttributes().keySet(), containsInAnyOrder("attr1", "attr2"));
|
||||||
assertThat(group.getAttributes(), hasEntry(is("attr1"), contains("attrval1")));
|
assertThat(group.getAttributes(), hasEntry(is("attr1"), contains("attrval1")));
|
||||||
|
@ -530,14 +539,14 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
|
|
||||||
// Move "mygroup2" as child of "mygroup1" . Assert it was moved
|
// Move "mygroup2" as child of "mygroup1" . Assert it was moved
|
||||||
Response response = realm.groups().group(group1.getId()).subGroup(group2);
|
Response response = realm.groups().group(group1.getId()).subGroup(group2);
|
||||||
Assert.assertEquals(204, response.getStatus());
|
assertEquals(204, response.getStatus());
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
// Assert "mygroup2" was moved
|
// Assert "mygroup2" was moved
|
||||||
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
||||||
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
||||||
assertNames(group1.getSubGroups(), "mygroup2");
|
assertNames(group1.getSubGroups(), "mygroup2");
|
||||||
Assert.assertEquals("/mygroup1/mygroup2", group2.getPath());
|
assertEquals("/mygroup1/mygroup2", group2.getPath());
|
||||||
|
|
||||||
assertAdminEvents.clear();
|
assertAdminEvents.clear();
|
||||||
|
|
||||||
|
@ -549,19 +558,19 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
// Try to move top level "mygroup2" as child of "mygroup1". It should fail as there is already a child group
|
// Try to move top level "mygroup2" as child of "mygroup1". It should fail as there is already a child group
|
||||||
// of "mygroup1" with name "mygroup2"
|
// of "mygroup1" with name "mygroup2"
|
||||||
response = realm.groups().group(group1.getId()).subGroup(group3);
|
response = realm.groups().group(group1.getId()).subGroup(group3);
|
||||||
Assert.assertEquals(409, response.getStatus());
|
assertEquals(409, response.getStatus());
|
||||||
realm.groups().group(group3.getId()).remove();
|
realm.groups().group(group3.getId()).remove();
|
||||||
|
|
||||||
// Move "mygroup2" back under parent
|
// Move "mygroup2" back under parent
|
||||||
response = realm.groups().add(group2);
|
response = realm.groups().add(group2);
|
||||||
Assert.assertEquals(204, response.getStatus());
|
assertEquals(204, response.getStatus());
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
// Assert "mygroup2" was moved
|
// Assert "mygroup2" was moved
|
||||||
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
||||||
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
||||||
assertTrue(group1.getSubGroups().isEmpty());
|
assertTrue(group1.getSubGroups().isEmpty());
|
||||||
Assert.assertEquals("/mygroup2", group2.getPath());
|
assertEquals("/mygroup2", group2.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -601,7 +610,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
assertNames(members, "user-b");
|
assertNames(members, "user-b");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
//KEYCLOAK-6300
|
//KEYCLOAK-6300
|
||||||
public void groupMembershipUsersOrder() {
|
public void groupMembershipUsersOrder() {
|
||||||
|
@ -615,20 +624,20 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
UserRepresentation user = UserBuilder.create().username("user" + i).build();
|
UserRepresentation user = UserBuilder.create().username("user" + i).build();
|
||||||
usernames.add(user.getUsername());
|
usernames.add(user.getUsername());
|
||||||
|
|
||||||
try (Response create = realm.users().create(user)) {
|
try (Response create = realm.users().create(user)) {
|
||||||
assertEquals(Status.CREATED, create.getStatusInfo());
|
assertEquals(Status.CREATED, create.getStatusInfo());
|
||||||
|
|
||||||
String userAId = ApiUtil.getCreatedId(create);
|
String userAId = ApiUtil.getCreatedId(create);
|
||||||
realm.users().get(userAId).joinGroup(groupId);
|
realm.users().get(userAId).joinGroup(groupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> memberUsernames = new ArrayList<>();
|
List<String> memberUsernames = new ArrayList<>();
|
||||||
for (UserRepresentation member : realm.groups().group(groupId).members(0, 10)) {
|
for (UserRepresentation member : realm.groups().group(groupId).members(0, 10)) {
|
||||||
memberUsernames.add(member.getUsername());
|
memberUsernames.add(member.getUsername());
|
||||||
}
|
}
|
||||||
assertArrayEquals("Expected: " + usernames + ", was: " + memberUsernames,
|
assertArrayEquals("Expected: " + usernames + ", was: " + memberUsernames,
|
||||||
usernames.toArray(), memberUsernames.toArray());
|
usernames.toArray(), memberUsernames.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,7 +874,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
GroupRepresentation group = GroupBuilder.create().name(groupName).build();
|
GroupRepresentation group = GroupBuilder.create().name(groupName).build();
|
||||||
try (Response response = realm.groups().add(group)) {
|
try (Response response = realm.groups().add(group)) {
|
||||||
String groupId = ApiUtil.getCreatedId(response);
|
String groupId = ApiUtil.getCreatedId(response);
|
||||||
|
|
||||||
RoleMappingResource mappings = realm.groups().group(groupId).roles();
|
RoleMappingResource mappings = realm.groups().group(groupId).roles();
|
||||||
mappings.realmLevel().add(Collections.singletonList(adminRole));
|
mappings.realmLevel().add(Collections.singletonList(adminRole));
|
||||||
|
|
||||||
|
@ -996,41 +1005,74 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
assertEquals(110, group.members(-1, -2).size());
|
assertEquals(110, group.members(-1, -2).size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getGroupsWithFullRepresentation() {
|
public void getGroupsWithFullRepresentation() {
|
||||||
RealmResource realm = adminClient.realms().realm("test");
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
||||||
|
|
||||||
GroupRepresentation group = new GroupRepresentation();
|
GroupRepresentation group = new GroupRepresentation();
|
||||||
group.setName("groupWithAttribute");
|
group.setName("groupWithAttribute");
|
||||||
|
|
||||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||||
attributes.put("attribute1", Arrays.asList("attribute1","attribute2"));
|
attributes.put("attribute1", Arrays.asList("attribute1","attribute2"));
|
||||||
group.setAttributes(attributes);
|
group.setAttributes(attributes);
|
||||||
group = createGroup(realm, group);
|
group = createGroup(realm, group);
|
||||||
|
|
||||||
List<GroupRepresentation> groups = groupsResource.groups("groupWithAttribute", 0, 20, false);
|
List<GroupRepresentation> groups = groupsResource.groups("groupWithAttribute", 0, 20, false);
|
||||||
|
|
||||||
assertFalse(groups.isEmpty());
|
assertFalse(groups.isEmpty());
|
||||||
assertTrue(groups.get(0).getAttributes().containsKey("attribute1"));
|
assertTrue(groups.get(0).getAttributes().containsKey("attribute1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchGroupsByNameContaining() {
|
||||||
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
|
try(Creator<GroupResource> g = Creator.create(realm, GroupBuilder.create().name("group-name-1").build());
|
||||||
|
Creator<GroupResource> g1 = Creator.create(realm, GroupBuilder.create().name("group-name-2").build())) {
|
||||||
|
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
||||||
|
List<GroupRepresentation> groups = groupsResource.groups("group-name", false, 0, 20, false);
|
||||||
|
assertThat(groups, hasSize(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchGroupsByNameExactSuccess() {
|
||||||
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
|
try(Creator<GroupResource> g = Creator.create(realm, GroupBuilder.create().name("group-name-1").build());
|
||||||
|
Creator<GroupResource> g1 = Creator.create(realm, GroupBuilder.create().name("group-name-2").build())) {
|
||||||
|
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
||||||
|
List<GroupRepresentation> groups = groupsResource.groups("group-name-1", true, 0, 20, false);
|
||||||
|
assertThat(groups, hasSize(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchGroupsByNameExactFailure() {
|
||||||
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
|
try(Creator<GroupResource> g = Creator.create(realm, GroupBuilder.create().name("group-name-1").build());
|
||||||
|
Creator<GroupResource> g1 = Creator.create(realm, GroupBuilder.create().name("group-name-2").build())) {
|
||||||
|
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
||||||
|
List<GroupRepresentation> groups = groupsResource.groups("group-name", true, 0, 20, false);
|
||||||
|
assertThat(groups, empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getGroupsWithBriefRepresentation() {
|
public void getGroupsWithBriefRepresentation() {
|
||||||
RealmResource realm = adminClient.realms().realm("test");
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
GroupsResource groupsResource = adminClient.realms().realm("test").groups();
|
||||||
|
|
||||||
GroupRepresentation group = new GroupRepresentation();
|
GroupRepresentation group = new GroupRepresentation();
|
||||||
group.setName("groupWithAttribute");
|
group.setName("groupWithAttribute");
|
||||||
|
|
||||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||||
attributes.put("attribute1", Arrays.asList("attribute1","attribute2"));
|
attributes.put("attribute1", Arrays.asList("attribute1","attribute2"));
|
||||||
group.setAttributes(attributes);
|
group.setAttributes(attributes);
|
||||||
group = createGroup(realm, group);
|
group = createGroup(realm, group);
|
||||||
|
|
||||||
List<GroupRepresentation> groups = groupsResource.groups("groupWithAttribute", 0, 20);
|
List<GroupRepresentation> groups = groupsResource.groups("groupWithAttribute", 0, 20);
|
||||||
|
|
||||||
assertFalse(groups.isEmpty());
|
assertFalse(groups.isEmpty());
|
||||||
assertNull(groups.get(0).getAttributes());
|
assertNull(groups.get(0).getAttributes());
|
||||||
}
|
}
|
||||||
|
@ -1145,7 +1187,7 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
assertTrue(Comparators.isInStrictOrder(secondPage, compareByName));
|
assertTrue(Comparators.isInStrictOrder(secondPage, compareByName));
|
||||||
|
|
||||||
// Check that the ordering of groups across multiple pages is correct
|
// Check that the ordering of groups across multiple pages is correct
|
||||||
// Since the individual pages are ordered it is sufficient to compare
|
// Since the individual pages are ordered it is sufficient to compare
|
||||||
// every group from the first page to the first group of the second page
|
// every group from the first page to the first group of the second page
|
||||||
GroupRepresentation firstGroupOnSecondPage = secondPage.get(0);
|
GroupRepresentation firstGroupOnSecondPage = secondPage.get(0);
|
||||||
for (GroupRepresentation firstPageGroup : firstPage) {
|
for (GroupRepresentation firstPageGroup : firstPage) {
|
||||||
|
@ -1213,17 +1255,17 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
|
|
||||||
final List<GroupRepresentation> searchResultGroups = realm.groups().groups(searchFor, 0, 10);
|
final List<GroupRepresentation> searchResultGroups = realm.groups().groups(searchFor, 0, 10);
|
||||||
|
|
||||||
Assert.assertFalse(searchResultGroups.isEmpty());
|
assertFalse(searchResultGroups.isEmpty());
|
||||||
Assert.assertEquals(expectedRootGroup.getId(), searchResultGroups.get(0).getId());
|
assertEquals(expectedRootGroup.getId(), searchResultGroups.get(0).getId());
|
||||||
Assert.assertEquals(expectedRootGroup.getName(), searchResultGroups.get(0).getName());
|
assertEquals(expectedRootGroup.getName(), searchResultGroups.get(0).getName());
|
||||||
|
|
||||||
List<GroupRepresentation> searchResultSubGroups = searchResultGroups.get(0).getSubGroups();
|
List<GroupRepresentation> searchResultSubGroups = searchResultGroups.get(0).getSubGroups();
|
||||||
Assert.assertEquals(expectedChildGroup.getId(), searchResultSubGroups.get(0).getId());
|
assertEquals(expectedChildGroup.getId(), searchResultSubGroups.get(0).getId());
|
||||||
Assert.assertEquals(expectedChildGroup.getName(), searchResultSubGroups.get(0).getName());
|
assertEquals(expectedChildGroup.getName(), searchResultSubGroups.get(0).getName());
|
||||||
|
|
||||||
searchResultSubGroups.remove(0);
|
searchResultSubGroups.remove(0);
|
||||||
Assert.assertTrue(searchResultSubGroups.isEmpty());
|
assertTrue(searchResultSubGroups.isEmpty());
|
||||||
searchResultGroups.remove(0);
|
searchResultGroups.remove(0);
|
||||||
Assert.assertTrue(searchResultGroups.isEmpty());
|
assertTrue(searchResultGroups.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue