[KEYCLOAK-18891] Add support for searching users by custom user attributes
Users can now be searched by custom attributes using 'q' in the query parameters. The implementation is roughly the same as search clients by custom attributes.
This commit is contained in:
parent
ce0070508f
commit
5b0986e490
6 changed files with 133 additions and 30 deletions
|
@ -109,6 +109,20 @@ public interface UsersResource {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<UserRepresentation> search(@QueryParam("username") String username);
|
List<UserRepresentation> search(@QueryParam("username") String username);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
List<UserRepresentation> searchByAttributes(@QueryParam("q") String searchQuery);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
List<UserRepresentation> searchByAttributes(@QueryParam("first") Integer firstResult,
|
||||||
|
@QueryParam("max") Integer maxResults,
|
||||||
|
@QueryParam("enabled") Boolean enabled,
|
||||||
|
@QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||||
|
@QueryParam("q") String searchQuery);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<UserRepresentation> search(@QueryParam("username") String username, @QueryParam("exact") Boolean exact);
|
List<UserRepresentation> search(@QueryParam("username") String username, @QueryParam("exact") Boolean exact);
|
||||||
|
@ -246,7 +260,7 @@ public interface UsersResource {
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@DELETE
|
@DELETE
|
||||||
Response delete(@PathParam("id") String id);
|
Response delete(@PathParam("id") String id);
|
||||||
|
|
||||||
@Path("profile")
|
@Path("profile")
|
||||||
UserProfileResource userProfile();
|
UserProfileResource userProfile();
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.jpa.entities.CredentialEntity;
|
import org.keycloak.models.jpa.entities.CredentialEntity;
|
||||||
import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
|
import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserConsentClientScopeEntity;
|
import org.keycloak.models.jpa.entities.UserConsentClientScopeEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserConsentEntity;
|
import org.keycloak.models.jpa.entities.UserConsentEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserEntity;
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
|
@ -49,15 +50,16 @@ import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.client.ClientStorageProvider;
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.LockModeType;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
import javax.persistence.criteria.Join;
|
import javax.persistence.criteria.Join;
|
||||||
|
import javax.persistence.criteria.JoinType;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
import javax.persistence.criteria.Subquery;
|
import javax.persistence.criteria.Subquery;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -68,8 +70,6 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.persistence.LockModeType;
|
|
||||||
|
|
||||||
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
|
import static org.keycloak.models.jpa.PaginationUtils.paginateQuery;
|
||||||
import static org.keycloak.utils.StreamsUtil.closing;
|
import static org.keycloak.utils.StreamsUtil.closing;
|
||||||
|
|
||||||
|
@ -768,7 +768,8 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
CriteriaQuery<UserEntity> queryBuilder = builder.createQuery(UserEntity.class);
|
CriteriaQuery<UserEntity> queryBuilder = builder.createQuery(UserEntity.class);
|
||||||
Root<UserEntity> root = queryBuilder.from(UserEntity.class);
|
Root<UserEntity> root = queryBuilder.from(UserEntity.class);
|
||||||
|
|
||||||
List<Predicate> predicates = new ArrayList();
|
List<Predicate> predicates = new ArrayList<>();
|
||||||
|
List<Predicate> attributePredicates = new ArrayList<>();
|
||||||
|
|
||||||
predicates.add(builder.equal(root.get("realmId"), realm.getId()));
|
predicates.add(builder.equal(root.get("realmId"), realm.getId()));
|
||||||
|
|
||||||
|
@ -788,7 +789,7 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case UserModel.SEARCH:
|
case UserModel.SEARCH:
|
||||||
List<Predicate> orPredicates = new ArrayList();
|
List<Predicate> orPredicates = new ArrayList<>();
|
||||||
|
|
||||||
orPredicates
|
orPredicates
|
||||||
.add(builder.like(builder.lower(root.get(USERNAME)), "%" + value.toLowerCase() + "%"));
|
.add(builder.like(builder.lower(root.get(USERNAME)), "%" + value.toLowerCase() + "%"));
|
||||||
|
@ -799,7 +800,7 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
builder.coalesce(root.get(LAST_NAME), builder.literal("")))),
|
builder.coalesce(root.get(LAST_NAME), builder.literal("")))),
|
||||||
"%" + value.toLowerCase() + "%"));
|
"%" + value.toLowerCase() + "%"));
|
||||||
|
|
||||||
predicates.add(builder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
|
predicates.add(builder.or(orPredicates.toArray(new Predicate[0])));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -831,9 +832,24 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
}
|
}
|
||||||
predicates.add(builder.equal(federatedIdentitiesJoin.get("userId"), value));
|
predicates.add(builder.equal(federatedIdentitiesJoin.get("userId"), value));
|
||||||
break;
|
break;
|
||||||
|
case UserModel.EXACT:
|
||||||
|
break;
|
||||||
|
// All unknown attributes will be assumed as custom attributes
|
||||||
|
default:
|
||||||
|
Join<UserEntity, UserAttributeEntity> attributesJoin = root.join("attributes", JoinType.LEFT);
|
||||||
|
|
||||||
|
attributePredicates.add(builder.and(
|
||||||
|
builder.equal(builder.lower(attributesJoin.get("name")), key.toLowerCase()),
|
||||||
|
builder.equal(builder.lower(attributesJoin.get("value")), value.toLowerCase())));
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!attributePredicates.isEmpty()) {
|
||||||
|
predicates.add(builder.and(attributePredicates.toArray(new Predicate[0])));
|
||||||
|
}
|
||||||
|
|
||||||
Set<String> userGroups = (Set<String>) session.getAttribute(UserModel.GROUPS);
|
Set<String> userGroups = (Set<String>) session.getAttribute(UserModel.GROUPS);
|
||||||
|
|
||||||
if (userGroups != null) {
|
if (userGroups != null) {
|
||||||
|
|
|
@ -637,6 +637,11 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
mcb = mcb.compare(SearchableFields.IDP_AND_USER, Operator.EQ, attributes.get(UserModel.IDP_ALIAS), value);
|
mcb = mcb.compare(SearchableFields.IDP_AND_USER, Operator.EQ, attributes.get(UserModel.IDP_ALIAS), value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case UserModel.EXACT:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mcb = mcb.compare(SearchableFields.ATTRIBUTE, Operator.EQ, key, value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -389,6 +389,8 @@ public interface UserQueryProvider {
|
||||||
* the given userId (case sensitive string)</li>
|
* the given userId (case sensitive string)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
* Any other parameters will be treated as custom user attributes.
|
||||||
|
*
|
||||||
* This method is used by the REST API when querying users.
|
* This method is used by the REST API when querying users.
|
||||||
*
|
*
|
||||||
* @param realm a reference to the realm.
|
* @param realm a reference to the realm.
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources.admin;
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
|
@ -43,6 +41,7 @@ import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluato
|
||||||
import org.keycloak.services.resources.admin.permissions.UserPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.UserPermissionEvaluator;
|
||||||
import org.keycloak.userprofile.UserProfile;
|
import org.keycloak.userprofile.UserProfile;
|
||||||
import org.keycloak.userprofile.UserProfileProvider;
|
import org.keycloak.userprofile.UserProfileProvider;
|
||||||
|
import org.keycloak.utils.SearchQueryUtils;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -56,11 +55,14 @@ import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base resource for managing users
|
* Base resource for managing users
|
||||||
*
|
*
|
||||||
|
@ -215,7 +217,7 @@ public class UsersResource {
|
||||||
/**
|
/**
|
||||||
* Get users
|
* Get users
|
||||||
*
|
*
|
||||||
* Returns a stream of users, filtered according to query parameters
|
* Returns a stream of users, filtered according to query parameters.
|
||||||
*
|
*
|
||||||
* @param search A String contained in username, first or last name, or email
|
* @param search A String contained in username, first or last name, or email
|
||||||
* @param last A String contained in lastName, or the complete lastName, if param "exact" is true
|
* @param last A String contained in lastName, or the complete lastName, if param "exact" is true
|
||||||
|
@ -230,6 +232,7 @@ public class UsersResource {
|
||||||
* @param enabled Boolean representing if user is enabled or not
|
* @param enabled Boolean representing if user is enabled or not
|
||||||
* @param briefRepresentation Boolean which defines whether brief representations are returned (default: false)
|
* @param briefRepresentation Boolean which defines whether brief representations are returned (default: false)
|
||||||
* @param exact Boolean which defines whether the params "last", "first", "email" and "username" must match exactly
|
* @param exact Boolean which defines whether the params "last", "first", "email" and "username" must match exactly
|
||||||
|
* @param searchQuery A query to search for custom attributes, in the format 'key1:value2 key2:value2'
|
||||||
* @return a non-null {@code Stream} of users
|
* @return a non-null {@code Stream} of users
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
|
@ -247,7 +250,8 @@ public class UsersResource {
|
||||||
@QueryParam("max") Integer maxResults,
|
@QueryParam("max") Integer maxResults,
|
||||||
@QueryParam("enabled") Boolean enabled,
|
@QueryParam("enabled") Boolean enabled,
|
||||||
@QueryParam("briefRepresentation") Boolean briefRepresentation,
|
@QueryParam("briefRepresentation") Boolean briefRepresentation,
|
||||||
@QueryParam("exact") Boolean exact) {
|
@QueryParam("exact") Boolean exact,
|
||||||
|
@QueryParam("q") String searchQuery) {
|
||||||
UserPermissionEvaluator userPermissionEvaluator = auth.users();
|
UserPermissionEvaluator userPermissionEvaluator = auth.users();
|
||||||
|
|
||||||
userPermissionEvaluator.requireQuery();
|
userPermissionEvaluator.requireQuery();
|
||||||
|
@ -255,6 +259,10 @@ public class UsersResource {
|
||||||
firstResult = firstResult != null ? firstResult : -1;
|
firstResult = firstResult != null ? firstResult : -1;
|
||||||
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
|
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
|
||||||
|
|
||||||
|
Map<String, String> searchAttributes = searchQuery == null
|
||||||
|
? Collections.emptyMap()
|
||||||
|
: SearchQueryUtils.getFields(searchQuery);
|
||||||
|
|
||||||
Stream<UserModel> userModels = Stream.empty();
|
Stream<UserModel> userModels = Stream.empty();
|
||||||
if (search != null) {
|
if (search != null) {
|
||||||
if (search.startsWith(SEARCH_ID_PARAMETER)) {
|
if (search.startsWith(SEARCH_ID_PARAMETER)) {
|
||||||
|
@ -273,7 +281,7 @@ public class UsersResource {
|
||||||
maxResults, false);
|
maxResults, false);
|
||||||
}
|
}
|
||||||
} else if (last != null || first != null || email != null || username != null || emailVerified != null
|
} else if (last != null || first != null || email != null || username != null || emailVerified != null
|
||||||
|| idpAlias != null || idpUserId != null || enabled != null || exact != null) {
|
|| idpAlias != null || idpUserId != null || enabled != null || exact != null || !searchAttributes.isEmpty()) {
|
||||||
Map<String, String> attributes = new HashMap<>();
|
Map<String, String> attributes = new HashMap<>();
|
||||||
if (last != null) {
|
if (last != null) {
|
||||||
attributes.put(UserModel.LAST_NAME, last);
|
attributes.put(UserModel.LAST_NAME, last);
|
||||||
|
@ -302,6 +310,9 @@ public class UsersResource {
|
||||||
if (exact != null) {
|
if (exact != null) {
|
||||||
attributes.put(UserModel.EXACT, exact.toString());
|
attributes.put(UserModel.EXACT, exact.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes.putAll(searchAttributes);
|
||||||
|
|
||||||
return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult,
|
return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult,
|
||||||
maxResults, true);
|
maxResults, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -61,6 +61,8 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
|
||||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||||
import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
|
import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
|
||||||
|
@ -101,6 +103,7 @@ import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
@ -116,9 +119,6 @@ import static org.junit.Assert.fail;
|
||||||
import static org.keycloak.testsuite.Assert.assertNames;
|
import static org.keycloak.testsuite.Assert.assertNames;
|
||||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||||
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -595,6 +595,12 @@ public class UserTest extends AbstractAdminTest {
|
||||||
user.setFirstName("First" + i);
|
user.setFirstName("First" + i);
|
||||||
user.setLastName("Last" + i);
|
user.setLastName("Last" + i);
|
||||||
|
|
||||||
|
HashMap<String, List<String>> attributes = new HashMap<>();
|
||||||
|
attributes.put("test", Collections.singletonList("test" + i));
|
||||||
|
attributes.put("test" + i, Collections.singletonList("test" + i));
|
||||||
|
attributes.put("attr", Collections.singletonList("common"));
|
||||||
|
user.setAttributes(attributes);
|
||||||
|
|
||||||
ids.add(createUser(user));
|
ids.add(createUser(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,15 +629,64 @@ public class UserTest extends AbstractAdminTest {
|
||||||
assertEquals(9, users.size());
|
assertEquals(9, users.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String mapToSearchQuery(Map<String, String> search) {
|
||||||
|
return search.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(e -> String.format("%s:%s", e.getKey(), e.getValue()))
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchByAttribute() {
|
||||||
|
createUsers();
|
||||||
|
|
||||||
|
Map<String, String> attributes = new HashMap<>();
|
||||||
|
attributes.put("test", "test1");
|
||||||
|
List<UserRepresentation> users = realm.users().searchByAttributes(mapToSearchQuery(attributes));
|
||||||
|
assertEquals(1, users.size());
|
||||||
|
|
||||||
|
attributes.clear();
|
||||||
|
attributes.put("attr", "common");
|
||||||
|
|
||||||
|
users = realm.users().searchByAttributes(mapToSearchQuery(attributes));
|
||||||
|
assertEquals(9, users.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchByMultipleAttributes() {
|
||||||
|
createUsers();
|
||||||
|
|
||||||
|
Map<String, String> attributes = new HashMap<>();
|
||||||
|
attributes.put("test", "test1");
|
||||||
|
attributes.put("attr", "common");
|
||||||
|
attributes.put("test1", "test1");
|
||||||
|
|
||||||
|
List<UserRepresentation> users = realm.users().searchByAttributes(mapToSearchQuery(attributes));
|
||||||
|
assertEquals(1, users.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchByAttributesWithPagination() {
|
||||||
|
createUsers();
|
||||||
|
|
||||||
|
Map<String, String> attributes = new HashMap<>();
|
||||||
|
attributes.put("attr", "common");
|
||||||
|
for (int i = 1; i < 10; i++) {
|
||||||
|
List<UserRepresentation> users = realm.users().searchByAttributes(i - 1, 1, null, false, mapToSearchQuery(attributes));
|
||||||
|
assertEquals(1, users.size());
|
||||||
|
assertTrue(users.get(0).getAttributes().keySet().stream().anyMatch(attributes::containsKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void searchByUsernameExactMatch() {
|
public void searchByUsernameExactMatch() {
|
||||||
createUsers();
|
createUsers();
|
||||||
|
|
||||||
UserRepresentation user = new UserRepresentation();
|
UserRepresentation user = new UserRepresentation();
|
||||||
user.setUsername("username11");
|
user.setUsername("username11");
|
||||||
|
|
||||||
createUser(user);
|
createUser(user);
|
||||||
|
|
||||||
List<UserRepresentation> users = realm.users().search("username1", true);
|
List<UserRepresentation> users = realm.users().search("username1", true);
|
||||||
assertEquals(1, users.size());
|
assertEquals(1, users.size());
|
||||||
|
|
||||||
|
@ -2022,14 +2077,14 @@ public class UserTest extends AbstractAdminTest {
|
||||||
realm.flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePasswordReqAction);
|
realm.flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePasswordReqAction);
|
||||||
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.authRequiredActionPath(UserModel.RequiredAction.UPDATE_PASSWORD.toString()), updatePasswordReqAction, ResourceType.REQUIRED_ACTION);
|
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.authRequiredActionPath(UserModel.RequiredAction.UPDATE_PASSWORD.toString()), updatePasswordReqAction, ResourceType.REQUIRED_ACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RoleRepresentation getRoleByName(String name, List<RoleRepresentation> roles) {
|
private RoleRepresentation getRoleByName(String name, List<RoleRepresentation> roles) {
|
||||||
for(RoleRepresentation role : roles) {
|
for(RoleRepresentation role : roles) {
|
||||||
if(role.getName().equalsIgnoreCase(name)) {
|
if(role.getName().equalsIgnoreCase(name)) {
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2042,7 +2097,7 @@ public class UserTest extends AbstractAdminTest {
|
||||||
realm.update(realmRep);
|
realm.update(realmRep);
|
||||||
|
|
||||||
RoleRepresentation realmCompositeRole = RoleBuilder.create().name("realm-composite").singleAttribute("attribute1", "value1").build();
|
RoleRepresentation realmCompositeRole = RoleBuilder.create().name("realm-composite").singleAttribute("attribute1", "value1").build();
|
||||||
|
|
||||||
realm.roles().create(RoleBuilder.create().name("realm-role").build());
|
realm.roles().create(RoleBuilder.create().name("realm-role").build());
|
||||||
realm.roles().create(realmCompositeRole);
|
realm.roles().create(realmCompositeRole);
|
||||||
realm.roles().create(RoleBuilder.create().name("realm-child").build());
|
realm.roles().create(RoleBuilder.create().name("realm-child").build());
|
||||||
|
@ -2054,8 +2109,8 @@ public class UserTest extends AbstractAdminTest {
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
RoleRepresentation clientCompositeRole = RoleBuilder.create().name("client-composite").singleAttribute("attribute1", "value1").build();
|
RoleRepresentation clientCompositeRole = RoleBuilder.create().name("client-composite").singleAttribute("attribute1", "value1").build();
|
||||||
|
|
||||||
|
|
||||||
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role").build());
|
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role").build());
|
||||||
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role2").build());
|
realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role2").build());
|
||||||
realm.clients().get(clientUuid).roles().create(clientCompositeRole);
|
realm.clients().get(clientUuid).roles().create(clientCompositeRole);
|
||||||
|
@ -2099,12 +2154,12 @@ public class UserTest extends AbstractAdminTest {
|
||||||
RoleRepresentation realmCompositeRoleFromList = getRoleByName("realm-composite", realmRolesFullRepresentations);
|
RoleRepresentation realmCompositeRoleFromList = getRoleByName("realm-composite", realmRolesFullRepresentations);
|
||||||
assertNotNull(realmCompositeRoleFromList);
|
assertNotNull(realmCompositeRoleFromList);
|
||||||
assertTrue(realmCompositeRoleFromList.getAttributes().containsKey("attribute1"));
|
assertTrue(realmCompositeRoleFromList.getAttributes().containsKey("attribute1"));
|
||||||
|
|
||||||
// List client roles
|
// List client roles
|
||||||
assertNames(roles.clientLevel(clientUuid).listAll(), "client-role", "client-composite");
|
assertNames(roles.clientLevel(clientUuid).listAll(), "client-role", "client-composite");
|
||||||
assertNames(roles.clientLevel(clientUuid).listAvailable(), "client-role2", "client-child");
|
assertNames(roles.clientLevel(clientUuid).listAvailable(), "client-role2", "client-child");
|
||||||
assertNames(roles.clientLevel(clientUuid).listEffective(), "client-role", "client-composite", "client-child");
|
assertNames(roles.clientLevel(clientUuid).listEffective(), "client-role", "client-composite", "client-child");
|
||||||
|
|
||||||
// List client effective role with full representation
|
// List client effective role with full representation
|
||||||
List<RoleRepresentation> rolesFullRepresentations = roles.clientLevel(clientUuid).listEffective(false);
|
List<RoleRepresentation> rolesFullRepresentations = roles.clientLevel(clientUuid).listEffective(false);
|
||||||
RoleRepresentation clientCompositeRoleFromList = getRoleByName("client-composite", rolesFullRepresentations);
|
RoleRepresentation clientCompositeRoleFromList = getRoleByName("client-composite", rolesFullRepresentations);
|
||||||
|
@ -2455,11 +2510,11 @@ public class UserTest extends AbstractAdminTest {
|
||||||
Assert.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getUserLabel(), otpCredentialLoaded.getUserLabel()));
|
Assert.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getUserLabel(), otpCredentialLoaded.getUserLabel()));
|
||||||
Assert.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getPriority(), otpCredentialLoaded.getPriority()));
|
Assert.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getPriority(), otpCredentialLoaded.getPriority()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetGroupsForUserFullRepresentation() {
|
public void testGetGroupsForUserFullRepresentation() {
|
||||||
RealmResource realm = adminClient.realms().realm("test");
|
RealmResource realm = adminClient.realms().realm("test");
|
||||||
|
|
||||||
String userName = "averagejoe";
|
String userName = "averagejoe";
|
||||||
String groupName = "groupWithAttribute";
|
String groupName = "groupWithAttribute";
|
||||||
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
Map<String, List<String>> attributes = new HashMap<String, List<String>>();
|
||||||
|
@ -2469,16 +2524,16 @@ public class UserTest extends AbstractAdminTest {
|
||||||
.edit(createUserRepresentation(userName, "joe@average.com", "average", "joe", true))
|
.edit(createUserRepresentation(userName, "joe@average.com", "average", "joe", true))
|
||||||
.addPassword("password")
|
.addPassword("password")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Creator<UserResource> u = Creator.create(realm, userRepresentation);
|
try (Creator<UserResource> u = Creator.create(realm, userRepresentation);
|
||||||
Creator<GroupResource> g = Creator.create(realm, GroupBuilder.create().name(groupName).attributes(attributes).build())) {
|
Creator<GroupResource> g = Creator.create(realm, GroupBuilder.create().name(groupName).attributes(attributes).build())) {
|
||||||
|
|
||||||
String groupId = g.id();
|
String groupId = g.id();
|
||||||
UserResource user = u.resource();
|
UserResource user = u.resource();
|
||||||
user.joinGroup(groupId);
|
user.joinGroup(groupId);
|
||||||
|
|
||||||
List<GroupRepresentation> userGroups = user.groups(0, 100, false);
|
List<GroupRepresentation> userGroups = user.groups(0, 100, false);
|
||||||
|
|
||||||
assertFalse(userGroups.isEmpty());
|
assertFalse(userGroups.isEmpty());
|
||||||
assertTrue(userGroups.get(0).getAttributes().containsKey("attribute1"));
|
assertTrue(userGroups.get(0).getAttributes().containsKey("attribute1"));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue