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("max") Integer max,
|
||||
@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.
|
||||
* @return A map containing key "count" with number of groups as value.
|
||||
|
|
|
@ -1478,8 +1478,9 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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
|
||||
|
|
|
@ -1025,7 +1025,12 @@ public class RealmCacheSession implements CacheRealmProvider {
|
|||
|
||||
@Override
|
||||
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
|
||||
|
|
|
@ -40,6 +40,7 @@ import javax.persistence.criteria.Join;
|
|||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
|
@ -937,10 +938,19 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
|
|||
|
||||
@Override
|
||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer first, Integer max) {
|
||||
TypedQuery<String> query = em.createNamedQuery("getGroupIdsByNameContaining", String.class)
|
||||
.setParameter("realm", realm.getId())
|
||||
.setParameter("search", search);
|
||||
return searchForGroupByNameStream(realm, search, false, first, max);
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
return closing(groups.map(id -> {
|
||||
|
@ -951,7 +961,6 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
|
|||
return groupById;
|
||||
}).sorted(GroupModel.COMPARE_BY_NAME).distinct());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<GroupModel> searchGroupsByAttributes(RealmModel realm, Map<String, String> attributes, Integer firstResult, Integer maxResults) {
|
||||
Map<String, String> filteredAttributes = groupSearchableAttributes == null || groupSearchableAttributes.isEmpty()
|
||||
|
|
|
@ -1938,8 +1938,9 @@ public class RealmAdapter implements LegacyRealmModel, JpaModel<RealmEntity> {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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
|
||||
|
|
|
@ -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="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="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="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"),
|
||||
|
|
|
@ -64,12 +64,9 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
|
|||
*
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||
Stream<GroupModel> local = localStorage().searchForGroupByNameStream(realm, search, firstResult, maxResults);
|
||||
Stream<GroupModel> ext = flatMapEnabledStorageProvidersWithTimeout(realm, GroupLookupProvider.class,
|
||||
p -> p.searchForGroupByNameStream(realm, search, firstResult, maxResults));
|
||||
|
||||
return Stream.concat(local, ext);
|
||||
return searchForGroupByNameStream(realm, search, false, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,6 +78,22 @@ public class GroupStorageManager extends AbstractStorageManager<GroupStorageProv
|
|||
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) */
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.models.map.group;
|
||||
|
||||
import java.security.Key;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.GroupModel.SearchableFields;
|
||||
|
@ -167,24 +168,34 @@ public class MapGroupProvider implements GroupProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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();
|
||||
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
|
||||
if (exact != null && exact.equals(Boolean.TRUE)) {
|
||||
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))
|
||||
.map(MapGroupEntity::getId)
|
||||
.map(id -> {
|
||||
GroupModel groupById = session.groups().getGroupById(realm, id);
|
||||
while (Objects.nonNull(groupById.getParentId())) {
|
||||
groupById = session.groups().getGroupById(realm, groupById.getParentId());
|
||||
}
|
||||
return groupById;
|
||||
}).sorted(GroupModel.COMPARE_BY_NAME).distinct();
|
||||
.map(MapGroupEntity::getId)
|
||||
.map(id -> {
|
||||
GroupModel groupById = session.groups().getGroupById(realm, id);
|
||||
while (Objects.nonNull(groupById.getParentId())) {
|
||||
groupById = session.groups().getGroupById(realm, groupById.getParentId());
|
||||
}
|
||||
return groupById;
|
||||
}).sorted(GroupModel.COMPARE_BY_NAME).distinct();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1425,8 +1425,9 @@ public class MapRealmAdapter extends AbstractRealmModel<MapRealmEntity> implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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
|
||||
|
|
|
@ -393,8 +393,8 @@ public class MapRealmProvider implements RealmProvider {
|
|||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
|
||||
return session.groups().searchForGroupByNameStream(realm, search, firstResult, maxResults);
|
||||
public Stream<GroupModel> searchForGroupByNameStream(RealmModel realm, String search, Boolean exact, Integer firstResult, Integer maxResults) {
|
||||
return session.groups().searchForGroupByNameStream(realm, search, exact, firstResult, maxResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -177,9 +177,15 @@ public class ModelToRepresentation {
|
|||
.map(g -> toGroupHierarchy(g, full, attributes));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Stream<GroupRepresentation> searchForGroupByName(RealmModel realm, boolean full, String search, Integer first, Integer 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) {
|
||||
|
@ -211,11 +217,16 @@ public class ModelToRepresentation {
|
|||
return toGroupHierarchy(group, full, (String) null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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);
|
||||
List<GroupRepresentation> subGroups = group.getSubGroupsStream()
|
||||
.filter(g -> groupMatchesSearchOrIsPathElement(g, search))
|
||||
.map(subGroup -> toGroupHierarchy(subGroup, full, search)).collect(Collectors.toList());
|
||||
.filter(g -> groupMatchesSearchOrIsPathElement(g, search, exact))
|
||||
.map(subGroup -> toGroupHierarchy(subGroup, full, search, exact)).collect(Collectors.toList());
|
||||
rep.setSubGroups(subGroups);
|
||||
return rep;
|
||||
}
|
||||
|
@ -228,16 +239,24 @@ public class ModelToRepresentation {
|
|||
return rep;
|
||||
}
|
||||
|
||||
private static boolean groupMatchesSearchOrIsPathElement(GroupModel group, String search) {
|
||||
private static boolean groupMatchesSearchOrIsPathElement(GroupModel group, String search, Boolean exact) {
|
||||
if (StringUtil.isBlank(search)) {
|
||||
return true;
|
||||
}
|
||||
if (group.getName().contains(search)) {
|
||||
return true;
|
||||
if(exact !=null && exact.equals(true)){
|
||||
if (group.getName().equals(search)){
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (group.getName().contains(search)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return group.getSubGroupsStream().findAny().isPresent();
|
||||
}
|
||||
|
||||
|
||||
public static UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
UserRepresentation rep = new UserRepresentation();
|
||||
rep.setId(user.getId());
|
||||
|
|
|
@ -846,7 +846,7 @@ public interface RealmModel extends RoleContainerModel {
|
|||
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
|
||||
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 max {@code Integer} Maximum number of returned groups. Ignored if negative or {@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);
|
||||
|
||||
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}.
|
||||
* @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}.
|
||||
* @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.
|
||||
|
@ -76,4 +80,20 @@ public interface GroupLookupProvider {
|
|||
*/
|
||||
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)
|
||||
public Stream<GroupRepresentation> getGroups(@QueryParam("search") String search,
|
||||
@QueryParam("q") String searchQuery,
|
||||
@QueryParam("exact") @DefaultValue("false") Boolean exact,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
||||
|
@ -86,7 +87,7 @@ public class GroupsResource {
|
|||
Map<String, String> attributes = SearchQueryUtils.getFields(searchQuery);
|
||||
return ModelToRepresentation.searchGroupsByAttributes(session, realm, !briefRepresentation, attributes, firstResult, maxResults);
|
||||
} 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)) {
|
||||
return ModelToRepresentation.toGroupHierarchy(realm, !briefRepresentation, firstResult, maxResults);
|
||||
} else {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
|
@ -53,15 +54,27 @@ public class HardcodedGroupStorageProvider implements GroupStorageProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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 {
|
||||
Thread.sleep(5000l);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(HardcodedGroupStorageProvider.class).warn(ex.getCause());
|
||||
return Stream.empty();
|
||||
}
|
||||
if (search != null && this.groupName.toLowerCase().contains(search.toLowerCase())) {
|
||||
return Stream.of(new HardcodedGroupAdapter(realm));
|
||||
if(BooleanUtils.isTrue(exact)){
|
||||
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();
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
|||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.updaters.Creator;
|
||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
|
@ -67,14 +68,22 @@ import javax.ws.rs.ClientErrorException;
|
|||
import javax.ws.rs.core.Response.Status;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Rule;
|
||||
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.models.AdminRoles;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.keycloak.testsuite.Assert.assertNames;
|
||||
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
|
||||
|
||||
|
@ -295,7 +304,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
Assert.fail("Creating a group with empty name should fail");
|
||||
}
|
||||
} catch (Exception expected) {
|
||||
Assert.assertNotNull(expected);
|
||||
assertNotNull(expected);
|
||||
}
|
||||
|
||||
group.setName(null);
|
||||
|
@ -304,7 +313,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
Assert.fail("Creating a group with null name should fail");
|
||||
}
|
||||
} catch (Exception expected) {
|
||||
Assert.assertNotNull(expected);
|
||||
assertNotNull(expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +336,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
realm.groups().group(groupId).update(group);
|
||||
Assert.fail("Updating a group with empty name should fail");
|
||||
} catch(Exception expected) {
|
||||
Assert.assertNotNull(expected);
|
||||
assertNotNull(expected);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -335,7 +344,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
realm.groups().group(groupId).update(group);
|
||||
Assert.fail("Updating a group with null name should fail");
|
||||
} catch(Exception expected) {
|
||||
Assert.assertNotNull(expected);
|
||||
assertNotNull(expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,7 +388,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
});
|
||||
|
||||
level2Group = realm.getGroupByPath("/top/level2");
|
||||
Assert.assertNotNull(level2Group);
|
||||
assertNotNull(level2Group);
|
||||
roles.clear();
|
||||
roles.add(level2Role);
|
||||
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);
|
||||
|
||||
level3Group = realm.getGroupByPath("/top/level2/level3");
|
||||
Assert.assertNotNull(level3Group);
|
||||
assertNotNull(level3Group);
|
||||
roles.clear();
|
||||
roles.add(level3Role);
|
||||
realm.groups().group(level3Group.getId()).roles().realmLevel().add(roles);
|
||||
|
@ -473,7 +482,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
}
|
||||
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
|
||||
|
@ -489,7 +498,7 @@ public class GroupTest extends AbstractGroupTest {
|
|||
createGroup(realm, group);
|
||||
group = realm.getGroupByPath("/" + groupName);
|
||||
|
||||
Assert.assertNotNull(group);
|
||||
assertNotNull(group);
|
||||
assertThat(group.getName(), is(groupName));
|
||||
assertThat(group.getAttributes().keySet(), containsInAnyOrder("attr1", "attr2"));
|
||||
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
|
||||
Response response = realm.groups().group(group1.getId()).subGroup(group2);
|
||||
Assert.assertEquals(204, response.getStatus());
|
||||
assertEquals(204, response.getStatus());
|
||||
response.close();
|
||||
|
||||
// Assert "mygroup2" was moved
|
||||
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
||||
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
||||
assertNames(group1.getSubGroups(), "mygroup2");
|
||||
Assert.assertEquals("/mygroup1/mygroup2", group2.getPath());
|
||||
assertEquals("/mygroup1/mygroup2", group2.getPath());
|
||||
|
||||
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
|
||||
// of "mygroup1" with name "mygroup2"
|
||||
response = realm.groups().group(group1.getId()).subGroup(group3);
|
||||
Assert.assertEquals(409, response.getStatus());
|
||||
assertEquals(409, response.getStatus());
|
||||
realm.groups().group(group3.getId()).remove();
|
||||
|
||||
// Move "mygroup2" back under parent
|
||||
response = realm.groups().add(group2);
|
||||
Assert.assertEquals(204, response.getStatus());
|
||||
assertEquals(204, response.getStatus());
|
||||
response.close();
|
||||
|
||||
// Assert "mygroup2" was moved
|
||||
group1 = realm.groups().group(group1.getId()).toRepresentation();
|
||||
group2 = realm.groups().group(group2.getId()).toRepresentation();
|
||||
assertTrue(group1.getSubGroups().isEmpty());
|
||||
Assert.assertEquals("/mygroup2", group2.getPath());
|
||||
assertEquals("/mygroup2", group2.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1016,6 +1025,39 @@ public class GroupTest extends AbstractGroupTest {
|
|||
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
|
||||
public void getGroupsWithBriefRepresentation() {
|
||||
RealmResource realm = adminClient.realms().realm("test");
|
||||
|
@ -1213,17 +1255,17 @@ public class GroupTest extends AbstractGroupTest {
|
|||
|
||||
final List<GroupRepresentation> searchResultGroups = realm.groups().groups(searchFor, 0, 10);
|
||||
|
||||
Assert.assertFalse(searchResultGroups.isEmpty());
|
||||
Assert.assertEquals(expectedRootGroup.getId(), searchResultGroups.get(0).getId());
|
||||
Assert.assertEquals(expectedRootGroup.getName(), searchResultGroups.get(0).getName());
|
||||
assertFalse(searchResultGroups.isEmpty());
|
||||
assertEquals(expectedRootGroup.getId(), searchResultGroups.get(0).getId());
|
||||
assertEquals(expectedRootGroup.getName(), searchResultGroups.get(0).getName());
|
||||
|
||||
List<GroupRepresentation> searchResultSubGroups = searchResultGroups.get(0).getSubGroups();
|
||||
Assert.assertEquals(expectedChildGroup.getId(), searchResultSubGroups.get(0).getId());
|
||||
Assert.assertEquals(expectedChildGroup.getName(), searchResultSubGroups.get(0).getName());
|
||||
assertEquals(expectedChildGroup.getId(), searchResultSubGroups.get(0).getId());
|
||||
assertEquals(expectedChildGroup.getName(), searchResultSubGroups.get(0).getName());
|
||||
|
||||
searchResultSubGroups.remove(0);
|
||||
Assert.assertTrue(searchResultSubGroups.isEmpty());
|
||||
assertTrue(searchResultSubGroups.isEmpty());
|
||||
searchResultGroups.remove(0);
|
||||
Assert.assertTrue(searchResultGroups.isEmpty());
|
||||
assertTrue(searchResultGroups.isEmpty());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue