[KEYCLOAK-16341] Make the new stream-based methods in server-spi user interfaces default instead of the collection-based versions.

- this ensures that providing implementation for the collection-based methods is enough, which preserves
   backwards compatibility with older custom implementations.
 - alternative interfaces now allow new implementations to focus on the stream variants of the query methods.
This commit is contained in:
Stefan Guilhen 2020-11-17 02:00:39 -03:00 committed by Hynek Mlnařík
parent 43baf1bea7
commit 84df008bc2
37 changed files with 932 additions and 264 deletions

View file

@ -92,7 +92,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
CredentialAuthentication, CredentialAuthentication,
UserLookupProvider, UserLookupProvider,
UserRegistrationProvider, UserRegistrationProvider,
UserQueryProvider, UserQueryProvider.Streams,
ImportedUserValidation { ImportedUserValidation {
private static final Logger logger = Logger.getLogger(LDAPStorageProvider.class); private static final Logger logger = Logger.getLogger(LDAPStorageProvider.class);

View file

@ -32,7 +32,7 @@ import org.keycloak.storage.ReadOnlyException;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel { public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate {
public ReadonlyLDAPUserModelDelegate(UserModel delegate) { public ReadonlyLDAPUserModelDelegate(UserModel delegate) {
super(delegate); super(delegate);

View file

@ -36,7 +36,7 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class GroupAdapter implements GroupModel { public class GroupAdapter implements GroupModel.Streams {
protected final CachedGroup cached; protected final CachedGroup cached;
protected final RealmCacheSession cacheSession; protected final RealmCacheSession cacheSession;

View file

@ -42,7 +42,7 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserAdapter implements CachedUserModel { public class UserAdapter implements CachedUserModel.Streams {
private final Supplier<UserModel> modelSupplier; private final Supplier<UserModel> modelSupplier;
protected final CachedUser cached; protected final CachedUser cached;

View file

@ -71,7 +71,7 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserCacheSession implements UserCache { public class UserCacheSession implements UserCache.Streams {
protected static final Logger logger = Logger.getLogger(UserCacheSession.class); protected static final Logger logger = Logger.getLogger(UserCacheSession.class);
protected UserCacheManager cache; protected UserCacheManager cache;
protected KeycloakSession session; protected KeycloakSession session;

View file

@ -31,12 +31,10 @@ import org.keycloak.models.utils.RoleUtils;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.persistence.LockModeType; import javax.persistence.LockModeType;
@ -46,7 +44,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class GroupAdapter implements GroupModel , JpaModel<GroupEntity> { public class GroupAdapter implements GroupModel.Streams , JpaModel<GroupEntity> {
protected GroupEntity group; protected GroupEntity group;
protected EntityManager em; protected EntityManager em;

View file

@ -82,7 +82,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@SuppressWarnings("JpaQueryApiInspection") @SuppressWarnings("JpaQueryApiInspection")
public class JpaUserProvider implements UserProvider, UserCredentialStore { public class JpaUserProvider implements UserProvider.Streams, UserCredentialStore {
private static final String EMAIL = "email"; private static final String EMAIL = "email";
private static final String EMAIL_VERIFIED = "emailVerified"; private static final String EMAIL_VERIFIED = "emailVerified";

View file

@ -53,7 +53,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserAdapter implements UserModel, JpaModel<UserEntity> { public class UserAdapter implements UserModel.Streams, JpaModel<UserEntity> {
protected UserEntity user; protected UserEntity user;
protected EntityManager em; protected EntityManager em;

View file

@ -70,7 +70,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class JpaUserFederatedStorageProvider implements public class JpaUserFederatedStorageProvider implements
UserFederatedStorageProvider, UserFederatedStorageProvider.Streams,
UserCredentialStore { UserCredentialStore {
protected static final Logger logger = Logger.getLogger(JpaUserFederatedStorageProvider.class); protected static final Logger logger = Logger.getLogger(JpaUserFederatedStorageProvider.class);

View file

@ -24,7 +24,7 @@ import org.keycloak.models.map.common.AbstractEntity;
import java.util.Objects; import java.util.Objects;
public abstract class AbstractGroupModel<E extends AbstractEntity> implements GroupModel { public abstract class AbstractGroupModel<E extends AbstractEntity> implements GroupModel.Streams {
protected final KeycloakSession session; protected final KeycloakSession session;
protected final RealmModel realm; protected final RealmModel realm;

View file

@ -31,7 +31,6 @@ import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.ReadOnlyException;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -41,7 +40,7 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class InMemoryUserAdapter extends UserModelDefaultMethods { public class InMemoryUserAdapter extends UserModelDefaultMethods.Streams {
private Long createdTimestamp = Time.currentTimeMillis(); private Long createdTimestamp = Time.currentTimeMillis();
private boolean emailVerified; private boolean emailVerified;
private boolean enabled; private boolean enabled;

View file

@ -68,11 +68,12 @@ public interface GroupModel extends RoleMapperModel {
* @return list of all attribute values or empty list if there are not any values. Never return null * @return list of all attribute values or empty list if there are not any values. Never return null
*/ */
@Deprecated @Deprecated
default List<String> getAttribute(String name) { List<String> getAttribute(String name);
return getAttributeStream(name).collect(Collectors.toList());
}
Stream<String> getAttributeStream(String name); default Stream<String> getAttributeStream(String name) {
List<String> value = this.getAttribute(name);
return value != null ? value.stream() : Stream.empty();
}
Map<String, List<String>> getAttributes(); Map<String, List<String>> getAttributes();
@ -80,11 +81,12 @@ public interface GroupModel extends RoleMapperModel {
String getParentId(); String getParentId();
@Deprecated @Deprecated
default Set<GroupModel> getSubGroups() { Set<GroupModel> getSubGroups();
return getSubGroupsStream().collect(Collectors.toSet());
}
Stream<GroupModel> getSubGroupsStream(); default Stream<GroupModel> getSubGroupsStream() {
Set<GroupModel> value = this.getSubGroups();
return value != null ? value.stream() : Stream.empty();
}
/** /**
* You must also call addChild on the parent group, addChild on RealmModel if there is no parent group * You must also call addChild on the parent group, addChild on RealmModel if there is no parent group
@ -106,4 +108,29 @@ public interface GroupModel extends RoleMapperModel {
* @param subGroup * @param subGroup
*/ */
void removeChild(GroupModel subGroup); void removeChild(GroupModel subGroup);
/**
* The {@link GroupModel.Streams} interface makes all collection-based methods in {@link GroupModel} default by providing
* implementations that delegate to the {@link Stream}-based variants instead of the other way around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends GroupModel, RoleMapperModel.Streams {
@Override
default List<String> getAttribute(String name) {
return this.getAttributeStream(name).collect(Collectors.toList());
}
@Override
Stream<String> getAttributeStream(String name);
@Override
default Set<GroupModel> getSubGroups() {
return this.getSubGroupsStream().collect(Collectors.toSet());
}
@Override
Stream<GroupModel> getSubGroupsStream();
}
} }

View file

@ -32,15 +32,16 @@ public interface RoleMapperModel {
* @deprecated Use {@link #getRealmRoleMappingsStream()} getRealmRoleMappingsStream} instead. * @deprecated Use {@link #getRealmRoleMappingsStream()} getRealmRoleMappingsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<RoleModel> getRealmRoleMappings() { Set<RoleModel> getRealmRoleMappings();
return getRealmRoleMappingsStream().collect(Collectors.toSet());
}
/** /**
* Returns stream of realm roles that are directly set to this object. * Returns stream of realm roles that are directly set to this object.
* @return stream of {@link RoleModel} * @return stream of {@link RoleModel}
*/ */
Stream<RoleModel> getRealmRoleMappingsStream(); default Stream<RoleModel> getRealmRoleMappingsStream() {
Set<RoleModel> value = this.getRealmRoleMappings();
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Returns set of client roles that are directly set to this object for the given client. * Returns set of client roles that are directly set to this object for the given client.
@ -49,16 +50,17 @@ public interface RoleMapperModel {
* @deprecated Use {@link #getClientRoleMappingsStream(ClientModel)} getClientRoleMappingsStream} instead. * @deprecated Use {@link #getClientRoleMappingsStream(ClientModel)} getClientRoleMappingsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<RoleModel> getClientRoleMappings(ClientModel app) { Set<RoleModel> getClientRoleMappings(ClientModel app);
return getClientRoleMappingsStream(app).collect(Collectors.toSet());
}
/** /**
* Returns stream of client roles that are directly set to this object for the given client. * Returns stream of client roles that are directly set to this object for the given client.
* @param app Client to get the roles for * @param app Client to get the roles for
* @return stream of {@link RoleModel} * @return stream of {@link RoleModel}
*/ */
Stream<RoleModel> getClientRoleMappingsStream(ClientModel app); default Stream<RoleModel> getClientRoleMappingsStream(ClientModel app) {
Set<RoleModel> value = this.getClientRoleMappings(app);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Returns {@code true} if this object is directly or indirectly assigned the given role, {@code false} otherwise. * Returns {@code true} if this object is directly or indirectly assigned the given role, {@code false} otherwise.
@ -86,19 +88,53 @@ public interface RoleMapperModel {
* @deprecated Use {@link #getRoleMappingsStream()} getRoleMappingsStream} instead. * @deprecated Use {@link #getRoleMappingsStream()} getRoleMappingsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<RoleModel> getRoleMappings() { Set<RoleModel> getRoleMappings();
return getRoleMappingsStream().collect(Collectors.toSet());
}
/** /**
* Returns stream of all role (both realm all client) that are directly set to this object. * Returns stream of all role (both realm all client) that are directly set to this object.
* @return stream of {@link RoleModel} * @return stream of {@link RoleModel}
*/ */
Stream<RoleModel> getRoleMappingsStream(); default Stream<RoleModel> getRoleMappingsStream() {
Set<RoleModel> value = this.getRoleMappings();
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Removes the given role mapping from this object. * Removes the given role mapping from this object.
* @param role Role to remove * @param role Role to remove
*/ */
void deleteRoleMapping(RoleModel role); void deleteRoleMapping(RoleModel role);
/**
* The {@link Streams} interface makes all collection-based methods in {@link RoleMapperModel} default by providing
* implementations that delegate to the {@link Stream}-based variants instead of the other way around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends RoleMapperModel {
@Override
default Set<RoleModel> getRealmRoleMappings() {
return this.getRealmRoleMappingsStream().collect(Collectors.toSet());
}
@Override
Stream<RoleModel> getRealmRoleMappingsStream();
@Override
default Set<RoleModel> getClientRoleMappings(ClientModel app) {
return this.getClientRoleMappingsStream(app).collect(Collectors.toSet());
}
@Override
Stream<RoleModel> getClientRoleMappingsStream(ClientModel app);
@Override
default Set<RoleModel> getRoleMappings() {
return this.getRoleMappingsStream().collect(Collectors.toSet());
}
@Override
Stream<RoleModel> getRoleMappingsStream();
}
} }

View file

@ -94,17 +94,18 @@ public interface UserModel extends RoleMapperModel {
* @deprecated Use {@link #getAttributeStream(String) getAttributeStream} instead. * @deprecated Use {@link #getAttributeStream(String) getAttributeStream} instead.
*/ */
@Deprecated @Deprecated
default List<String> getAttribute(String name) { List<String> getAttribute(String name);
return this.getAttributeStream(name).collect(Collectors.toList());
}
/** /**
* Obtains all values associated with the specified attribute name. * Obtains all values associated with the specified attribute name.
* *
* @param name the name of the attribute. * @param name the name of the attribute.
* @return a non-null {@code Stream} of attribute values. * @return a non-null {@link Stream} of attribute values.
*/ */
Stream<String> getAttributeStream(final String name); default Stream<String> getAttributeStream(final String name) {
List<String> value = this.getAttribute(name);
return value != null ? value.stream() : Stream.empty();
}
Map<String, List<String>> getAttributes(); Map<String, List<String>> getAttributes();
@ -112,16 +113,17 @@ public interface UserModel extends RoleMapperModel {
* @deprecated Use {@link #getRequiredActionsStream() getRequiredActionsStream} instead. * @deprecated Use {@link #getRequiredActionsStream() getRequiredActionsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<String> getRequiredActions() { Set<String> getRequiredActions();
return this.getRequiredActionsStream().collect(Collectors.toSet());
}
/** /**
* Obtains the names of required actions associated with the user. * Obtains the names of required actions associated with the user.
* *
* @return a non-null {@code Stream} of required action names. * @return a non-null {@link Stream} of required action names.
*/ */
Stream<String> getRequiredActionsStream(); default Stream<String> getRequiredActionsStream() {
Set<String> value = this.getRequiredActions();
return value != null ? value.stream() : Stream.empty();
}
void addRequiredAction(String action); void addRequiredAction(String action);
@ -151,16 +153,17 @@ public interface UserModel extends RoleMapperModel {
* @deprecated Use {@link #getGroupsStream() getGroupsStream} instead. * @deprecated Use {@link #getGroupsStream() getGroupsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<GroupModel> getGroups() { Set<GroupModel> getGroups();
return getGroupsStream().collect(Collectors.toSet());
}
/** /**
* Obtains the groups associated with the user. * Obtains the groups associated with the user.
* *
* @return a non-null {@code Stream} of groups. * @return a non-null {@link Stream} of groups.
*/ */
Stream<GroupModel> getGroupsStream(); default Stream<GroupModel> getGroupsStream() {
Set<GroupModel> value = this.getGroups();
return value != null ? value.stream() : Stream.empty();
}
/** /**
* @deprecated Use {@link #getGroupsStream(String, Integer, Integer) getGroupsStream} instead. * @deprecated Use {@link #getGroupsStream(String, Integer, Integer) getGroupsStream} instead.
@ -230,4 +233,37 @@ public interface UserModel extends RoleMapperModel {
enum RequiredAction { enum RequiredAction {
VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD, TERMS_AND_CONDITIONS VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD, TERMS_AND_CONDITIONS
} }
/**
* The {@link UserModel.Streams} interface makes all collection-based methods in {@link UserModel} default by providing
* implementations that delegate to the {@link Stream}-based variants instead of the other way around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserModel, RoleMapperModel.Streams {
@Override
default List<String> getAttribute(String name) {
return this.getAttributeStream(name).collect(Collectors.toList());
}
@Override
Stream<String> getAttributeStream(final String name);
@Override
default Set<String> getRequiredActions() {
return this.getRequiredActionsStream().collect(Collectors.toSet());
}
@Override
Stream<String> getRequiredActionsStream();
@Override
default Set<GroupModel> getGroups() {
return this.getGroupsStream().collect(Collectors.toSet());
}
@Override
Stream<GroupModel> getGroupsStream();
}
} }

View file

@ -17,6 +17,8 @@
package org.keycloak.models; package org.keycloak.models;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:external.Martin.Idel@bosch.io">Martin Idel</a> * @author <a href="mailto:external.Martin.Idel@bosch.io">Martin Idel</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -53,4 +55,13 @@ public abstract class UserModelDefaultMethods implements UserModel {
email = email == null ? null : email.toLowerCase(); email = email == null ? null : email.toLowerCase();
setSingleAttribute(EMAIL, email); setSingleAttribute(EMAIL, email);
} }
/**
* The {@link UserModelDefaultMethods.Streams} class extends the {@link UserModelDefaultMethods} abstract class and
* implements the {@link UserModel.Streams} interface, allowing subclasses to focus on the implementation of the
* {@link Stream}-based query methods and providing default implementations for the collections-based variants that
* delegate to their {@link Stream} counterparts.
*/
public abstract static class Streams extends UserModelDefaultMethods implements UserModel.Streams {
}
} }

View file

@ -49,18 +49,19 @@ public interface UserProvider extends Provider,
* @deprecated Use {@link #getFederatedIdentitiesStream(UserModel, RealmModel) getFederatedIdentitiesStream} instead. * @deprecated Use {@link #getFederatedIdentitiesStream(UserModel, RealmModel) getFederatedIdentitiesStream} instead.
*/ */
@Deprecated @Deprecated
default Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) { Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm);
return this.getFederatedIdentitiesStream(user, realm).collect(Collectors.toSet());
}
/** /**
* Obtains the federated identities of the specified user. * Obtains the federated identities of the specified user.
* *
* @param user a reference to the user. * @param user a reference to the user.
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@code Stream} of federated identities associated with the user. * @return a non-null {@link Stream} of federated identities associated with the user.
*/ */
Stream<FederatedIdentityModel> getFederatedIdentitiesStream(UserModel user, RealmModel realm); default Stream<FederatedIdentityModel> getFederatedIdentitiesStream(UserModel user, RealmModel realm) {
Set<FederatedIdentityModel> value = this.getFederatedIdentities(user, realm);
return value != null ? value.stream() : Stream.empty();
}
FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm); FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm);
UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm); UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm);
@ -72,18 +73,19 @@ public interface UserProvider extends Provider,
* @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead. * @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserConsentModel> getConsents(RealmModel realm, String userId) { List<UserConsentModel> getConsents(RealmModel realm, String userId);
return getConsentsStream(realm, userId).collect(Collectors.toList());
}
/** /**
* Obtains the consents associated with the user identified by the specified {@code userId}. * Obtains the consents associated with the user identified by the specified {@code userId}.
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param userId the user identifier. * @param userId the user identifier.
* @return a non-null {@code Stream} of consents associated with the user. * @return a non-null {@link Stream} of consents associated with the user.
*/ */
Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId); default Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId) {
List<UserConsentModel> value = this.getConsents(realm, userId);
return value != null ? value.stream() : Stream.empty();
}
void updateConsent(RealmModel realm, String userId, UserConsentModel consent); void updateConsent(RealmModel realm, String userId, UserConsentModel consent);
boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId); boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId);
@ -97,26 +99,25 @@ public interface UserProvider extends Provider,
* @deprecated Use {@link #getUsersStream(RealmModel, boolean) getUsersStream} instead. * @deprecated Use {@link #getUsersStream(RealmModel, boolean) getUsersStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts) { List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts);
return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList());
}
/** /**
* Obtains the users associated with the specified realm. * Obtains the users associated with the specified realm.
* *
* @param realm a reference to the realm being used for the search. * @param realm a reference to the realm being used for the search.
* @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise. * @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise.
* @return a non-null {@code Stream} of users associated withe the realm. * @return a non-null {@link Stream} of users associated withe the realm.
*/ */
Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts); default Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts) {
List<UserModel> value = this.getUsers(realm, includeServiceAccounts);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* @deprecated Use {@link #getUsersStream(RealmModel, int, int, boolean) getUsersStream} instead. * @deprecated Use {@link #getUsersStream(RealmModel, int, int, boolean) getUsersStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) { List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
return this.getUsersStream(realm, firstResult, maxResults, includeServiceAccounts).collect(Collectors.toList());
}
/** /**
* Obtains the users associated with the specified realm. * Obtains the users associated with the specified realm.
@ -125,9 +126,12 @@ public interface UserProvider extends Provider,
* @param firstResult first result to return. Ignored if negative. * @param firstResult first result to return. Ignored if negative.
* @param maxResults maximum number of results to return. Ignored if negative. * @param maxResults maximum number of results to return. Ignored if negative.
* @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise. * @param includeServiceAccounts {@code true} if service accounts should be included in the result; {@code false} otherwise.
* @return a non-null {@code Stream} of users associated withe the realm. * @return a non-null {@link Stream} of users associated withe the realm.
*/ */
Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts); default Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
List<UserModel> value = this.getUsers(realm, firstResult, maxResults, includeServiceAccounts);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* only used for local storage * only used for local storage
@ -168,4 +172,38 @@ public interface UserProvider extends Provider,
void close(); void close();
void preRemove(RealmModel realm, ComponentModel component); void preRemove(RealmModel realm, ComponentModel component);
interface Streams extends UserProvider, UserQueryProvider.Streams {
@Override
default Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
return this.getFederatedIdentitiesStream(user, realm).collect(Collectors.toSet());
}
@Override
Stream<FederatedIdentityModel> getFederatedIdentitiesStream(UserModel user, RealmModel realm);
@Override
default List<UserConsentModel> getConsents(RealmModel realm, String userId) {
return this.getConsentsStream(realm, userId).collect(Collectors.toList());
}
@Override
Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId);
@Override
default List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts) {
return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList());
}
@Override
Stream<UserModel> getUsersStream(RealmModel realm, boolean includeServiceAccounts);
@Override
default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
return this.getUsersStream(realm, firstResult, maxResults, includeServiceAccounts).collect(Collectors.toList());
}
@Override
Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
}
} }

View file

@ -17,6 +17,7 @@
package org.keycloak.models.cache; package org.keycloak.models.cache;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -56,4 +57,12 @@ public interface CachedUserModel extends UserModel {
* @return * @return
*/ */
ConcurrentMap getCachedWith(); ConcurrentMap getCachedWith();
/**
* The {@link CachedUserModel.Streams} interface differs from {@link CachedUserModel} in that it extends the
* {@link UserModel.Streams} interface, allowing implementations of {@link CachedUserModel} to focus on the
* {@link java.util.stream.Stream}-based methods in the {@link UserModel} interface.
*/
interface Streams extends CachedUserModel, UserModel.Streams {
}
} }

View file

@ -47,4 +47,12 @@ public interface UserCache extends UserProvider {
* *
*/ */
void clear(); void clear();
/**
* The {@link UserCache.Streams} interface differs from {@link UserCache} in that it extends the {@link UserProvider.Streams}
* interface, allowing implementations of {@link UserCache} to focus on the {@link java.util.stream.Stream}-based methods
* in the {@link UserProvider} interface.
*/
interface Streams extends UserCache, UserProvider.Streams {
}
} }

View file

@ -33,7 +33,7 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserModelDelegate implements UserModel { public class UserModelDelegate implements UserModel.Streams {
protected UserModel delegate; protected UserModel delegate;
public UserModelDelegate(UserModel delegate) { public UserModelDelegate(UserModel delegate) {

View file

@ -30,8 +30,12 @@ import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.ReadOnlyException;
import org.keycloak.storage.StorageId; import org.keycloak.storage.StorageId;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -59,8 +63,8 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
} }
@Override @Override
public Stream<String> getRequiredActionsStream() { public Set<String> getRequiredActions() {
return Stream.empty(); return Collections.emptySet();
} }
@Override @Override
@ -91,8 +95,8 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
* *
* @return * @return
*/ */
protected Stream<GroupModel> getGroupsInternal() { protected Set<GroupModel> getGroupsInternal() {
return Stream.empty(); return Collections.emptySet();
} }
/** /**
@ -107,10 +111,11 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
} }
@Override @Override
public Stream<GroupModel> getGroupsStream() { public Set<GroupModel> getGroups() {
Stream<GroupModel> groups = getGroupsInternal(); Set<GroupModel> set = new HashSet<>();
if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream()); if (appendDefaultGroups()) set.addAll(realm.getDefaultGroupsStream().collect(Collectors.toSet()));
return groups; set.addAll(getGroupsInternal());
return set;
} }
@Override @Override
@ -127,23 +132,23 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
@Override @Override
public boolean isMemberOf(GroupModel group) { public boolean isMemberOf(GroupModel group) {
return RoleUtils.isMember(getGroupsStream(), group); return RoleUtils.isMember(getGroups().stream(), group);
} }
@Override @Override
public Stream<RoleModel> getRealmRoleMappingsStream() { public Set<RoleModel> getRealmRoleMappings() {
return getRoleMappingsStream().filter(RoleUtils::isRealmRole); return getRoleMappings().stream().filter(RoleUtils::isRealmRole).collect(Collectors.toSet());
} }
@Override @Override
public Stream<RoleModel> getClientRoleMappingsStream(ClientModel app) { public Set<RoleModel> getClientRoleMappings(ClientModel app) {
return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app)); return getRoleMappings().stream().filter(r -> RoleUtils.isClientRole(r, app)).collect(Collectors.toSet());
} }
@Override @Override
public boolean hasRole(RoleModel role) { public boolean hasRole(RoleModel role) {
return RoleUtils.hasRole(getRoleMappingsStream(), role) return RoleUtils.hasRole(getRoleMappings().stream(), role)
|| RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true); || RoleUtils.hasRoleFromGroup(getGroups().stream(), role, true);
} }
@Override @Override
@ -163,15 +168,16 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
return true; return true;
} }
protected Stream<RoleModel> getRoleMappingsInternal() { protected Set<RoleModel> getRoleMappingsInternal() {
return Stream.empty(); return Collections.emptySet();
} }
@Override @Override
public Stream<RoleModel> getRoleMappingsStream() { public Set<RoleModel> getRoleMappings() {
Stream<RoleModel> roleMappings = getRoleMappingsInternal(); Set<RoleModel> set = new HashSet<>();
if (appendDefaultRolesToRoleMappings()) return Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm)); if (appendDefaultRolesToRoleMappings()) set.addAll(DefaultRoles.getDefaultRoles(realm).collect(Collectors.toSet()));
return roleMappings; set.addAll(getRoleMappingsInternal());
return set;
} }
@ -300,11 +306,11 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
} }
@Override @Override
public Stream<String> getAttributeStream(String name) { public List<String> getAttribute(String name) {
if (name.equals(UserModel.USERNAME)) { if (name.equals(UserModel.USERNAME)) {
return Stream.of(getUsername()); return Collections.singletonList(getUsername());
} }
return Stream.empty(); return Collections.emptyList();
} }
@Override @Override
@ -365,4 +371,100 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
return getId().hashCode(); return getId().hashCode();
} }
/**
* The {@link AbstractUserAdapter.Streams} class extends the {@link AbstractUserAdapter} abstract class and implements
* the {@link UserModel.Streams} interface, allowing subclasses to focus on the implementation of the {@link Stream}-based
* query methods and providing default implementations for the collections-based variants that delegate to their
* {@link Stream} counterparts.
*/
public abstract static class Streams extends AbstractUserAdapter implements UserModel.Streams {
public Streams(final KeycloakSession session, final RealmModel realm, final ComponentModel storageProviderModel) {
super(session, realm, storageProviderModel);
}
@Override
public Set<String> getRequiredActions() {
return this.getRequiredActionsStream().collect(Collectors.toSet());
}
@Override
public Stream<String> getRequiredActionsStream() {
return Stream.empty();
}
@Override
public List<String> getAttribute(String name) {
return this.getAttributeStream(name).collect(Collectors.toList());
}
@Override
public Stream<String> getAttributeStream(String name) {
if (name.equals(UserModel.USERNAME)) {
return Stream.of(getUsername());
}
return Stream.empty();
}
// group-related methods.
@Override
public Set<GroupModel> getGroups() {
return this.getGroupsStream().collect(Collectors.toSet());
}
@Override
public Stream<GroupModel> getGroupsStream() {
Stream<GroupModel> groups = getGroupsInternal().stream();
if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream());
return groups;
}
@Override
public boolean isMemberOf(GroupModel group) {
return RoleUtils.isMember(this.getGroupsStream(), group);
}
// role-related methods.
@Override
public Set<RoleModel> getRealmRoleMappings() {
return this.getRealmRoleMappingsStream().collect(Collectors.toSet());
}
@Override
public Stream<RoleModel> getRealmRoleMappingsStream() {
return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
}
@Override
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
return this.getClientRoleMappingsStream(app).collect(Collectors.toSet());
}
@Override
public Stream<RoleModel> getClientRoleMappingsStream(ClientModel app) {
return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
}
@Override
public Set<RoleModel> getRoleMappings() {
return this.getRoleMappingsStream().collect(Collectors.toSet());
}
@Override
public Stream<RoleModel> getRoleMappingsStream() {
Stream<RoleModel> roleMappings = getRoleMappingsInternal().stream();
if (appendDefaultRolesToRoleMappings()) return Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm));
return roleMappings;
}
@Override
public boolean hasRole(RoleModel role) {
return RoleUtils.hasRole(this.getRoleMappingsStream(), role)
|| RoleUtils.hasRoleFromGroup(this.getGroupsStream(), role, true);
}
}
} }

View file

@ -30,8 +30,12 @@ import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.StorageId; import org.keycloak.storage.StorageId;
import org.keycloak.storage.federated.UserFederatedStorageProvider; import org.keycloak.storage.federated.UserFederatedStorageProvider;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -68,8 +72,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
} }
@Override @Override
public Stream<String> getRequiredActionsStream() { public Set<String> getRequiredActions() {
return getFederatedStorage().getRequiredActionsStream(realm, this.getId()); return getFederatedStorage().getRequiredActions(realm, this.getId());
} }
@Override @Override
@ -100,8 +104,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* *
* @return * @return
*/ */
protected Stream<GroupModel> getGroupsInternal() { protected Set<GroupModel> getGroupsInternal() {
return Stream.empty(); return Collections.emptySet();
} }
/** /**
@ -124,10 +128,11 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return * @return
*/ */
@Override @Override
public Stream<GroupModel> getGroupsStream() { public Set<GroupModel> getGroups() {
Stream<GroupModel> groups = getFederatedStorage().getGroupsStream(realm, this.getId()); Set<GroupModel> set = new HashSet<>(getFederatedStorage().getGroups(realm, this.getId()));
if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream()); if (appendDefaultGroups()) set.addAll(realm.getDefaultGroupsStream().collect(Collectors.toSet()));
return Stream.concat(groups, getGroupsInternal()); set.addAll(getGroupsInternal());
return set;
} }
@Override @Override
@ -144,7 +149,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
@Override @Override
public boolean isMemberOf(GroupModel group) { public boolean isMemberOf(GroupModel group) {
return RoleUtils.isMember(getGroupsStream(), group); return RoleUtils.isMember(getGroups().stream(), group);
} }
/** /**
@ -156,8 +161,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return * @return
*/ */
@Override @Override
public Stream<RoleModel> getRealmRoleMappingsStream() { public Set<RoleModel> getRealmRoleMappings() {
return getRoleMappingsStream().filter(RoleUtils::isRealmRole); return this.getRoleMappings().stream().filter(RoleUtils::isRealmRole).collect(Collectors.toSet());
} }
/** /**
@ -169,14 +174,15 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return * @return
*/ */
@Override @Override
public Stream<RoleModel> getClientRoleMappingsStream(ClientModel app) { public Set<RoleModel> getClientRoleMappings(ClientModel app) {
return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app)); return getRoleMappings().stream().filter(r -> RoleUtils.isClientRole(r, app)).collect(Collectors.toSet());
} }
@Override @Override
public boolean hasRole(RoleModel role) { public boolean hasRole(RoleModel role) {
return RoleUtils.hasRole(getRoleMappingsStream(), role) return RoleUtils.hasRole(getRoleMappings().stream(), role)
|| RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true); || RoleUtils.hasRoleFromGroup(getGroups().stream(), role, true);
} }
@Override @Override
@ -196,8 +202,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
return true; return true;
} }
protected Stream<RoleModel> getRoleMappingsInternal() { protected Set<RoleModel> getRoleMappingsInternal() {
return Stream.empty(); return Collections.emptySet();
} }
/** /**
@ -208,14 +214,15 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return * @return
*/ */
@Override @Override
public Stream<RoleModel> getRoleMappingsStream() { public Set<RoleModel> getRoleMappings() {
Stream<RoleModel> roleMappings = getFederatedRoleMappings(); Set<RoleModel> set = new HashSet<>(getFederatedRoleMappings());
if (appendDefaultRolesToRoleMappings()) roleMappings = Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm)); if (appendDefaultRolesToRoleMappings()) set.addAll(DefaultRoles.getDefaultRoles(realm).collect(Collectors.toSet()));
return Stream.concat(roleMappings, getRoleMappingsInternal()); set.addAll(getRoleMappingsInternal());
return set;
} }
protected Stream<RoleModel> getFederatedRoleMappings() { protected Set<RoleModel> getFederatedRoleMappings() {
return getFederatedStorage().getRoleMappingsStream(realm, this.getId()); return getFederatedStorage().getRoleMappings(realm, this.getId());
} }
@Override @Override
@ -357,15 +364,15 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
} }
@Override @Override
public Stream<String> getAttributeStream(String name) { public List<String> getAttribute(String name) {
if (UserModel.USERNAME.equals(name)) { if (UserModel.USERNAME.equals(name)) {
return Stream.of(getUsername()); return Collections.singletonList(getUsername());
} }
List<String> result = getFederatedStorage().getAttributes(realm, this.getId()).get(mapAttribute(name)); List<String> result = getFederatedStorage().getAttributes(realm, this.getId()).get(mapAttribute(name));
return (result == null) ? Stream.empty() : result.stream(); return (result == null) ? Collections.emptyList() : result;
} }
private String mapAttribute(String attributeName) { protected String mapAttribute(String attributeName) {
if (UserModel.FIRST_NAME.equals(attributeName)) { if (UserModel.FIRST_NAME.equals(attributeName)) {
return FIRST_NAME_ATTRIBUTE; return FIRST_NAME_ATTRIBUTE;
} else if (UserModel.LAST_NAME.equals(attributeName)) { } else if (UserModel.LAST_NAME.equals(attributeName)) {
@ -409,4 +416,101 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
return getId().hashCode(); return getId().hashCode();
} }
/**
* The {@link AbstractUserAdapterFederatedStorage.Streams} class extends the {@link AbstractUserAdapterFederatedStorage}
* abstract class and implements the {@link UserModel.Streams} interface, allowing subclasses to focus on the implementation
* of the {@link Stream}-based query methods and providing default implementations for the collections-based variants
* that delegate to their {@link Stream} counterparts.
*/
public abstract static class Streams extends AbstractUserAdapterFederatedStorage implements UserModel.Streams {
public Streams(final KeycloakSession session, final RealmModel realm, final ComponentModel storageProviderModel) {
super(session, realm, storageProviderModel);
}
// user-related methods.
@Override
public Set<String> getRequiredActions() {
return this.getRequiredActionsStream().collect(Collectors.toSet());
}
@Override
public Stream<String> getRequiredActionsStream() {
return super.getFederatedStorage().getRequiredActionsStream(super.realm, super.getId());
}
@Override
public List<String> getAttribute(String name) {
return this.getAttributeStream(name).collect(Collectors.toList());
}
@Override
public Stream<String> getAttributeStream(String name) {
if (UserModel.USERNAME.equals(name)) {
return Stream.of(getUsername());
}
List<String> result = super.getFederatedStorage().getAttributes(realm, this.getId()).get(super.mapAttribute(name));
return (result == null) ? Stream.empty() : result.stream();
}
// group-related methods.
@Override
public Set<GroupModel> getGroups() {
return this.getGroupsStream().collect(Collectors.toSet());
}
@Override
public Stream<GroupModel> getGroupsStream() {
Stream<GroupModel> groups = getFederatedStorage().getGroupsStream(realm, this.getId());
if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream());
return Stream.concat(groups, getGroupsInternal().stream());
}
@Override
public boolean isMemberOf(GroupModel group) {
return RoleUtils.isMember(this.getGroupsStream(), group);
}
// role-related methods.
@Override
public Set<RoleModel> getRealmRoleMappings() {
return this.getRealmRoleMappingsStream().collect(Collectors.toSet());
}
@Override
public Stream<RoleModel> getRealmRoleMappingsStream() {
return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
}
@Override
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
return this.getClientRoleMappingsStream(app).collect(Collectors.toSet());
}
@Override
public Stream<RoleModel> getClientRoleMappingsStream(ClientModel app) {
return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
}
@Override
public Set<RoleModel> getRoleMappings() {
return this.getRoleMappingsStream().collect(Collectors.toSet());
}
@Override
public Stream<RoleModel> getRoleMappingsStream() {
Stream<RoleModel> roleMappings = getFederatedRoleMappings().stream();
if (appendDefaultRolesToRoleMappings()) roleMappings = Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm));
return Stream.concat(roleMappings, getRoleMappingsInternal().stream());
}
@Override
public boolean hasRole(RoleModel role) {
return RoleUtils.hasRole(this.getRoleMappingsStream(), role)
|| RoleUtils.hasRoleFromGroup(this.getGroupsStream(), role, true);
}
}
} }

View file

@ -37,9 +37,7 @@ public interface UserAttributeFederatedStorage {
* @deprecated Use {@link #getUsersByUserAttributeStream(RealmModel, String, String) getUsersByUserAttributeStream} instead. * @deprecated Use {@link #getUsersByUserAttributeStream(RealmModel, String, String) getUsersByUserAttributeStream} instead.
*/ */
@Deprecated @Deprecated
default List<String> getUsersByUserAttribute(RealmModel realm, String name, String value) { List<String> getUsersByUserAttribute(RealmModel realm, String name, String value);
return this.getUsersByUserAttributeStream(realm, name, value).collect(Collectors.toList());
}
/** /**
* Searches for federated users that have an attribute with the specified {@code name} and {@code value}. * Searches for federated users that have an attribute with the specified {@code name} and {@code value}.
@ -47,7 +45,29 @@ public interface UserAttributeFederatedStorage {
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param name the attribute name. * @param name the attribute name.
* @param value the attribute value. * @param value the attribute value.
* @return a non-null {@code Stream} of users that match the search criteria. * @return a non-null {@link Stream} of users that match the search criteria.
*/ */
Stream<String> getUsersByUserAttributeStream(RealmModel realm, String name, String value); default Stream<String> getUsersByUserAttributeStream(RealmModel realm, String name, String value) {
List<String> users = this.getUsersByUserAttribute(realm, name, value);
return users != null ? users.stream() : Stream.empty();
}
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserAttributeFederatedStorage}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserAttributeFederatedStorage {
@Override
default List<String> getUsersByUserAttribute(RealmModel realm, String name, String value) {
return this.getUsersByUserAttributeStream(realm, name, value).collect(Collectors.toList());
}
@Override
Stream<String> getUsersByUserAttributeStream(RealmModel realm, String name, String value);
}
} }

View file

@ -39,18 +39,37 @@ public interface UserBrokerLinkFederatedStorage {
* @deprecated Use {@link #getFederatedIdentitiesStream(String, RealmModel) getFederatedIdentitiesStream} instead. * @deprecated Use {@link #getFederatedIdentitiesStream(String, RealmModel) getFederatedIdentitiesStream} instead.
*/ */
@Deprecated @Deprecated
default Set<FederatedIdentityModel> getFederatedIdentities(String userId, RealmModel realm) { Set<FederatedIdentityModel> getFederatedIdentities(String userId, RealmModel realm);
return this.getFederatedIdentitiesStream(userId, realm).collect(Collectors.toSet());
}
/** /**
* Obtains the identities of the federated user identified by {@code userId}. * Obtains the identities of the federated user identified by {@code userId}.
* *
* @param userId the user identifier. * @param userId the user identifier.
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@code Stream} of federated identities associated with the user. * @return a non-null {@link Stream} of federated identities associated with the user.
*/ */
Stream<FederatedIdentityModel> getFederatedIdentitiesStream(String userId, RealmModel realm); default Stream<FederatedIdentityModel> getFederatedIdentitiesStream(String userId, RealmModel realm) {
Set<FederatedIdentityModel> value = this.getFederatedIdentities(userId, realm);
return value != null ? value.stream() : Stream.empty();
}
FederatedIdentityModel getFederatedIdentity(String userId, String socialProvider, RealmModel realm); FederatedIdentityModel getFederatedIdentity(String userId, String socialProvider, RealmModel realm);
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserBrokerLinkFederatedStorage}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserBrokerLinkFederatedStorage {
@Override
default Set<FederatedIdentityModel> getFederatedIdentities(String userId, RealmModel realm) {
return this.getFederatedIdentitiesStream(userId, realm).collect(Collectors.toSet());
}
@Override
Stream<FederatedIdentityModel> getFederatedIdentitiesStream(String userId, RealmModel realm);
}
} }

View file

@ -35,19 +35,38 @@ public interface UserConsentFederatedStorage {
* @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead. * @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserConsentModel> getConsents(RealmModel realm, String userId) { List<UserConsentModel> getConsents(RealmModel realm, String userId);
return this.getConsentsStream(realm, userId).collect(Collectors.toList());
}
/** /**
* Obtains the consents associated with the federated user identified by {@code userId}. * Obtains the consents associated with the federated user identified by {@code userId}.
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param userId the user identifier. * @param userId the user identifier.
* @return a non-null {@code Stream} of consents associated with the user. * @return a non-null {@link Stream} of consents associated with the user.
*/ */
Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId); default Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId) {
List<UserConsentModel> value = this.getConsents(realm, userId);
return value != null ? value.stream() : Stream.empty();
}
void updateConsent(RealmModel realm, String userId, UserConsentModel consent); void updateConsent(RealmModel realm, String userId, UserConsentModel consent);
boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId); boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId);
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserConsentFederatedStorage}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserConsentFederatedStorage {
@Override
default List<UserConsentModel> getConsents(RealmModel realm, String userId) {
return this.getConsentsStream(realm, userId).collect(Collectors.toList());
}
@Override
Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId);
}
} }

View file

@ -49,9 +49,7 @@ public interface UserFederatedStorageProvider extends Provider,
* @deprecated Use {@link #getStoredUsersStream(RealmModel, int, int) getStoredUsersStream} instead. * @deprecated Use {@link #getStoredUsersStream(RealmModel, int, int) getStoredUsersStream} instead.
*/ */
@Deprecated @Deprecated
default List<String> getStoredUsers(RealmModel realm, int first, int max) { List<String> getStoredUsers(RealmModel realm, int first, int max);
return getStoredUsersStream(realm, first, max).collect(Collectors.toList());
}
/** /**
* Obtains the ids of all federated users in the realm. * Obtains the ids of all federated users in the realm.
@ -59,9 +57,12 @@ public interface UserFederatedStorageProvider extends Provider,
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param first first result to return. Ignored if negative. * @param first first result to return. Ignored if negative.
* @param max maximum number of results to return. Ignored if negative. * @param max maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of federated user ids. * @return a non-null {@link Stream} of federated user ids.
*/ */
Stream<String> getStoredUsersStream(RealmModel realm, int first, int max); default Stream<String> getStoredUsersStream(RealmModel realm, int first, int max) {
List<String> value = this.getStoredUsers(realm, first, max);
return value != null ? value.stream() : Stream.empty();
}
int getStoredUsersCount(RealmModel realm); int getStoredUsersCount(RealmModel realm);
@ -80,4 +81,30 @@ public interface UserFederatedStorageProvider extends Provider,
void preRemove(RealmModel realm, UserModel user); void preRemove(RealmModel realm, UserModel user);
void preRemove(RealmModel realm, ComponentModel model); void preRemove(RealmModel realm, ComponentModel model);
/**
* The {@link UserFederatedStorageProvider.Streams} interface makes all collection-based methods in {@link UserFederatedStorageProvider}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserFederatedStorageProvider,
UserAttributeFederatedStorage.Streams,
UserBrokerLinkFederatedStorage.Streams,
UserConsentFederatedStorage.Streams,
UserFederatedUserCredentialStore.Streams,
UserGroupMembershipFederatedStorage.Streams,
UserRequiredActionsFederatedStorage.Streams,
UserRoleMappingsFederatedStorage.Streams {
@Override
default List<String> getStoredUsers(RealmModel realm, int first, int max) {
return this.getStoredUsersStream(realm, first, max).collect(Collectors.toList());
}
@Override
Stream<String> getStoredUsersStream(RealmModel realm, int first, int max);
}
} }

View file

@ -38,26 +38,25 @@ public interface UserFederatedUserCredentialStore extends Provider {
* @deprecated Use {@link #getStoredCredentialsStream(RealmModel, String) getStoredCredentialsStream} instead. * @deprecated Use {@link #getStoredCredentialsStream(RealmModel, String) getStoredCredentialsStream} instead.
*/ */
@Deprecated @Deprecated
default List<CredentialModel> getStoredCredentials(RealmModel realm, String userId) { List<CredentialModel> getStoredCredentials(RealmModel realm, String userId);
return this.getStoredCredentialsStream(realm, userId).collect(Collectors.toList());
}
/** /**
* Obtains the credentials associated with the federated user identified by {@code userId}. * Obtains the credentials associated with the federated user identified by {@code userId}.
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param userId the user identifier. * @param userId the user identifier.
* @return a non-null {@code Stream} of credentials. * @return a non-null {@link Stream} of credentials.
*/ */
Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, String userId); default Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, String userId) {
List<CredentialModel> value = this.getStoredCredentials(realm, userId);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* @deprecated Use {@link #getStoredCredentialsByTypeStream(RealmModel, String, String) getStoredCredentialsByTypeStream} instead. * @deprecated Use {@link #getStoredCredentialsByTypeStream(RealmModel, String, String) getStoredCredentialsByTypeStream} instead.
*/ */
@Deprecated @Deprecated
default List<CredentialModel> getStoredCredentialsByType(RealmModel realm, String userId, String type) { List<CredentialModel> getStoredCredentialsByType(RealmModel realm, String userId, String type);
return this.getStoredCredentialsByTypeStream(realm, userId, type).collect(Collectors.toList());
}
/** /**
* Obtains the credentials of type {@code type} that are associated with the federated user identified by {@code userId}. * Obtains the credentials of type {@code type} that are associated with the federated user identified by {@code userId}.
@ -65,9 +64,38 @@ public interface UserFederatedUserCredentialStore extends Provider {
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param userId the user identifier. * @param userId the user identifier.
* @param type the credential type. * @param type the credential type.
* @return a non-null {@code Stream} of credentials. * @return a non-null {@link Stream} of credentials.
*/ */
Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type); default Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type) {
List<CredentialModel> value = this.getStoredCredentialsByType(realm, userId, type);
return value != null ? value.stream() : Stream.empty();
}
CredentialModel getStoredCredentialByNameAndType(RealmModel realm, String userId, String name, String type); CredentialModel getStoredCredentialByNameAndType(RealmModel realm, String userId, String name, String type);
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserFederatedUserCredentialStore}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserFederatedUserCredentialStore {
@Override
default List<CredentialModel> getStoredCredentials(RealmModel realm, String userId) {
return this.getStoredCredentialsStream(realm, userId).collect(Collectors.toList());
}
@Override
Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, String userId);
@Override
default List<CredentialModel> getStoredCredentialsByType(RealmModel realm, String userId, String type) {
return this.getStoredCredentialsByTypeStream(realm, userId, type).collect(Collectors.toList());
}
@Override
Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type);
}
} }

View file

@ -29,12 +29,24 @@ import java.util.stream.Stream;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface UserGroupMembershipFederatedStorage { public interface UserGroupMembershipFederatedStorage {
@Deprecated
default Set<GroupModel> getGroups(RealmModel realm, String userId) {
return getGroupsStream(realm, userId).collect(Collectors.toSet());
}
Stream<GroupModel> getGroupsStream(RealmModel realm, String userId); /**
* @deprecated Use {@link #getGroupsStream(RealmModel, String) getGroupsStream} instead.
*/
@Deprecated
Set<GroupModel> getGroups(RealmModel realm, String userId);
/**
* Obtains the groups associated with the federated user.
*
* @param realm a reference to the realm.
* @param userId the user identifier.
* @return a non-null {@code Stream} of groups.
*/
default Stream<GroupModel> getGroupsStream(RealmModel realm, String userId) {
Set<GroupModel> value = this.getGroups(realm, userId);
return value != null ? value.stream() : Stream.empty();
}
void joinGroup(RealmModel realm, String userId, GroupModel group); void joinGroup(RealmModel realm, String userId, GroupModel group);
void leaveGroup(RealmModel realm, String userId, GroupModel group); void leaveGroup(RealmModel realm, String userId, GroupModel group);
@ -43,9 +55,7 @@ public interface UserGroupMembershipFederatedStorage {
* @deprecated Use {@link #getMembershipStream(RealmModel, GroupModel, int, int) getMembershipStream} instead. * @deprecated Use {@link #getMembershipStream(RealmModel, GroupModel, int, int) getMembershipStream} instead.
*/ */
@Deprecated @Deprecated
default List<String> getMembership(RealmModel realm, GroupModel group, int firstResult, int max) { List<String> getMembership(RealmModel realm, GroupModel group, int firstResult, int max);
return this.getMembershipStream(realm, group, firstResult, max).collect(Collectors.toList());
}
/** /**
* Obtains the federated users that are members of the given {@code group} in the specified {@code realm}. * Obtains the federated users that are members of the given {@code group} in the specified {@code realm}.
@ -56,6 +66,34 @@ public interface UserGroupMembershipFederatedStorage {
* @param max maximum number of results to return. Ignored if negative. * @param max maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of federated user ids that are members of the group in the realm. * @return a non-null {@code Stream} of federated user ids that are members of the group in the realm.
*/ */
Stream<String> getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max); default Stream<String> getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max) {
List<String> value = this.getMembership(realm, group, firstResult, max);
return value != null ? value.stream() : Stream.empty();
}
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserGroupMembershipFederatedStorage}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserGroupMembershipFederatedStorage {
@Override
default Set<GroupModel> getGroups(RealmModel realm, String userId) {
return getGroupsStream(realm, userId).collect(Collectors.toSet());
}
@Override
Stream<GroupModel> getGroupsStream(RealmModel realm, String userId);
@Override
default List<String> getMembership(RealmModel realm, GroupModel group, int firstResult, int max) {
return this.getMembershipStream(realm, group, firstResult, max).collect(Collectors.toList());
}
@Override
Stream<String> getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max);
}
} }

View file

@ -32,18 +32,38 @@ public interface UserRequiredActionsFederatedStorage {
* @deprecated Use {@link #getRequiredActionsStream(RealmModel, String) getRequiredActionsStream} instead. * @deprecated Use {@link #getRequiredActionsStream(RealmModel, String) getRequiredActionsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<String> getRequiredActions(RealmModel realm, String userId) { Set<String> getRequiredActions(RealmModel realm, String userId);
return this.getRequiredActionsStream(realm, userId).collect(Collectors.toSet());
}
/** /**
* Obtains the names of required actions associated with the federated user identified by {@code userId}. * Obtains the names of required actions associated with the federated user identified by {@code userId}.
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param userId the user identifier. * @param userId the user identifier.
* @return a non-null {@code Stream} of required action names. * @return a non-null {@link Stream} of required action names.
*/ */
Stream<String> getRequiredActionsStream(RealmModel realm, String userId); default Stream<String> getRequiredActionsStream(RealmModel realm, String userId) {
Set<String> value = this.getRequiredActions(realm, userId);
return value != null ? value.stream() : Stream.empty();
}
void addRequiredAction(RealmModel realm, String userId, String action); void addRequiredAction(RealmModel realm, String userId, String action);
void removeRequiredAction(RealmModel realm, String userId, String action); void removeRequiredAction(RealmModel realm, String userId, String action);
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserRequiredActionsFederatedStorage}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserRequiredActionsFederatedStorage {
@Override
default Set<String> getRequiredActions(RealmModel realm, String userId) {
return this.getRequiredActionsStream(realm, userId).collect(Collectors.toSet());
}
@Override
Stream<String> getRequiredActionsStream(RealmModel realm, String userId);
}
} }

View file

@ -29,15 +29,13 @@ import java.util.stream.Stream;
*/ */
public interface UserRoleMappingsFederatedStorage { public interface UserRoleMappingsFederatedStorage {
void grantRole(RealmModel realm, String userId, RoleModel role);
/** /**
* @deprecated Use {@link #getRoleMappingsStream(RealmModel, String) getRoleMappingsStream} instead. * @deprecated Use {@link #getRoleMappingsStream(RealmModel, String) getRoleMappingsStream} instead.
*/ */
@Deprecated @Deprecated
default Set<RoleModel> getRoleMappings(RealmModel realm,String userId) { Set<RoleModel> getRoleMappings(RealmModel realm,String userId);
return getRoleMappingsStream(realm, userId).collect(Collectors.toSet());
} void grantRole(RealmModel realm, String userId, RoleModel role);
/** /**
* Obtains the roles associated with the federated user identified by {@code userId}. * Obtains the roles associated with the federated user identified by {@code userId}.
@ -46,7 +44,28 @@ public interface UserRoleMappingsFederatedStorage {
* @param userId the user identifier. * @param userId the user identifier.
* @return a non-null {@code Stream} of roles. * @return a non-null {@code Stream} of roles.
*/ */
Stream<RoleModel> getRoleMappingsStream(RealmModel realm, String userId); default Stream<RoleModel> getRoleMappingsStream(RealmModel realm, String userId) {
Set<RoleModel> value = this.getRoleMappings(realm, userId);
return value != null ? value.stream() : Stream.empty();
}
void deleteRoleMapping(RealmModel realm, String userId, RoleModel role); void deleteRoleMapping(RealmModel realm, String userId, RoleModel role);
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserRoleMappingsFederatedStorage}
* default by providing implementations that delegate to the {@link Stream}-based variants instead of the other way
* around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserRoleMappingsFederatedStorage {
@Override
default Set<RoleModel> getRoleMappings(RealmModel realm, String userId) {
return getRoleMappingsStream(realm, userId).collect(Collectors.toSet());
}
@Override
Stream<RoleModel> getRoleMappingsStream(RealmModel realm, String userId);
}
} }

View file

@ -141,25 +141,23 @@ public interface UserQueryProvider {
* @deprecated Use {@link #getUsersStream(RealmModel) getUsersStream} instead. * @deprecated Use {@link #getUsersStream(RealmModel) getUsersStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> getUsers(RealmModel realm) { List<UserModel> getUsers(RealmModel realm);
return this.getUsersStream(realm).collect(Collectors.toList());
}
/** /**
* Searches all users in the realm. * Searches all users in the realm.
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@code Stream} of users. * @return a non-null {@link Stream} of users.
*/ */
Stream<UserModel> getUsersStream(RealmModel realm); default Stream<UserModel> getUsersStream(RealmModel realm) {
List<UserModel> value = this.getUsers(realm);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* @deprecated Use {@link #getUsersStream(RealmModel, int, int) getUsersStream} instead. * @deprecated Use {@link #getUsersStream(RealmModel, int, int) getUsersStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) { List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults);
return this.getUsersStream(realm, firstResult, maxResults).collect(Collectors.toList());
}
/** /**
* Searches all users in the realm, starting from the {@code firstResult} and containing at most {@code maxResults}. * Searches all users in the realm, starting from the {@code firstResult} and containing at most {@code maxResults}.
@ -167,9 +165,12 @@ public interface UserQueryProvider {
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param firstResult first result to return. Ignored if negative. * @param firstResult first result to return. Ignored if negative.
* @param maxResults maximum number of results to return. Ignored if negative. * @param maxResults maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of users. * @return a non-null {@link Stream} of users.
*/ */
Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults); default Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults) {
List<UserModel> value = this.getUsers(realm, firstResult, maxResults);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Search for users with username, email or first + last name that is like search string. * Search for users with username, email or first + last name that is like search string.
@ -184,9 +185,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #searchForUserStream(String, RealmModel) searchForUserStream} instead. * @deprecated Use {@link #searchForUserStream(String, RealmModel) searchForUserStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> searchForUser(String search, RealmModel realm) { List<UserModel> searchForUser(String search, RealmModel realm);
return this.searchForUserStream(search, realm).collect(Collectors.toList());
}
/** /**
* Searches for users with username, email or first + last name that is like search string. If possible, implementations * Searches for users with username, email or first + last name that is like search string. If possible, implementations
@ -196,9 +195,12 @@ public interface UserQueryProvider {
* *
* @param search case sensitive search string. * @param search case sensitive search string.
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@code Stream} of users that match the search string. * @return a non-null {@link Stream} of users that match the search string.
*/ */
Stream<UserModel> searchForUserStream(String search, RealmModel realm); default Stream<UserModel> searchForUserStream(String search, RealmModel realm) {
List<UserModel> value = this.searchForUser(search, realm);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Search for users with username, email or first + last name that is like search string. * Search for users with username, email or first + last name that is like search string.
@ -215,9 +217,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #searchForUserStream(String, RealmModel, int, int) searchForUserStream} instead. * @deprecated Use {@link #searchForUserStream(String, RealmModel, int, int) searchForUserStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
return this.searchForUserStream(search, realm, firstResult, maxResults).collect(Collectors.toList());
}
/** /**
* Searches for users with username, email or first + last name that is like search string. If possible, implementations * Searches for users with username, email or first + last name that is like search string. If possible, implementations
@ -229,9 +229,12 @@ public interface UserQueryProvider {
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param firstResult first result to return. Ignored if negative. * @param firstResult first result to return. Ignored if negative.
* @param maxResults maximum number of results to return. Ignored if negative. * @param maxResults maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of users that match the search criteria. * @return a non-null {@link Stream} of users that match the search criteria.
*/ */
Stream<UserModel> searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults); default Stream<UserModel> searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) {
List<UserModel> value = this.searchForUser(search, realm, firstResult, maxResults);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Search for user by parameter. Valid parameters are: * Search for user by parameter. Valid parameters are:
@ -251,9 +254,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #searchForUserStream(Map, RealmModel) searchForUserStream} instead. * @deprecated Use {@link #searchForUserStream(Map, RealmModel) searchForUserStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) { List<UserModel> searchForUser(Map<String, String> params, RealmModel realm);
return this.searchForUserStream(params, realm).collect(Collectors.toList());
}
/** /**
* Searches for user by parameter. If possible, implementations should treat the parameter values as partial match patterns * Searches for user by parameter. If possible, implementations should treat the parameter values as partial match patterns
@ -269,9 +270,12 @@ public interface UserQueryProvider {
* *
* @param params a map containing the search parameters. * @param params a map containing the search parameters.
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@code Stream} of users that match the search parameters. * @return a non-null {@link Stream} of users that match the search parameters.
*/ */
Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm); default Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm) {
List<UserModel> value = this.searchForUser(params, realm);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Search for user by parameter. Valid parameters are: * Search for user by parameter. Valid parameters are:
@ -293,9 +297,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #searchForUserStream(Map, RealmModel, int, int) searchForUserStream} instead. * @deprecated Use {@link #searchForUserStream(Map, RealmModel, int, int) searchForUserStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) { List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults);
return this.searchForUserStream(params, realm, firstResult, maxResults).collect(Collectors.toList());
}
/** /**
* Searches for user by parameter. If possible, implementations should treat the parameter values as partial match patterns * Searches for user by parameter. If possible, implementations should treat the parameter values as partial match patterns
@ -313,9 +315,12 @@ public interface UserQueryProvider {
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param firstResult first result to return. Ignored if negative. * @param firstResult first result to return. Ignored if negative.
* @param maxResults maximum number of results to return. Ignored if negative. * @param maxResults maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of users that match the search criteria. * @return a non-null {@link Stream} of users that match the search criteria.
*/ */
Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm, int firstResult, int maxResults); default Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
List<UserModel> value = this.searchForUser(params, realm, firstResult, maxResults);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider * Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider
@ -329,9 +334,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #getGroupMembersStream(RealmModel, GroupModel) getGroupMembersStream} instead. * @deprecated Use {@link #getGroupMembersStream(RealmModel, GroupModel) getGroupMembersStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) { List<UserModel> getGroupMembers(RealmModel realm, GroupModel group);
return this.getGroupMembersStream(realm, group).collect(Collectors.toList());
}
/** /**
* Obtains users that belong to a specific group. Implementations do not have to search in {@code UserFederatedStorageProvider} * Obtains users that belong to a specific group. Implementations do not have to search in {@code UserFederatedStorageProvider}
@ -341,9 +344,12 @@ public interface UserQueryProvider {
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param group a reference to the group. * @param group a reference to the group.
* @return a non-null {@code Stream} of users that belong to the group. * @return a non-null {@link Stream} of users that belong to the group.
*/ */
Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group); default Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group) {
List<UserModel> value = this.getGroupMembers(realm, group);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider * Get users that belong to a specific group. Implementations do not have to search in UserFederatedStorageProvider
@ -359,9 +365,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #getGroupMembersStream(RealmModel, GroupModel, int, int) getGroupMembersStream} instead. * @deprecated Use {@link #getGroupMembersStream(RealmModel, GroupModel, int, int) getGroupMembersStream} instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults);
return this.getGroupMembersStream(realm, group, firstResult, maxResults).collect(Collectors.toList());
}
/** /**
* Obtains users that belong to a specific group. Implementations do not have to search in {@code UserFederatedStorageProvider} * Obtains users that belong to a specific group. Implementations do not have to search in {@code UserFederatedStorageProvider}
@ -373,9 +377,12 @@ public interface UserQueryProvider {
* @param group a reference to the group. * @param group a reference to the group.
* @param firstResult first result to return. Ignored if negative. * @param firstResult first result to return. Ignored if negative.
* @param maxResults maximum number of results to return. Ignored if negative. * @param maxResults maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of users that belong to the group. * @return a non-null {@link Stream} of users that belong to the group.
*/ */
Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults); default Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
List<UserModel> value = this.getGroupMembers(realm, group, firstResult, maxResults);
return value != null ? value.stream() : Stream.empty();
}
/** /**
* Get users that belong to a specific role. * Get users that belong to a specific role.
@ -395,7 +402,7 @@ public interface UserQueryProvider {
* *
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @param role a reference to the role. * @param role a reference to the role.
* @return a non-null {@code Stream} of users that have the specified role. * @return a non-null {@link Stream} of users that have the specified role.
*/ */
default Stream<UserModel> getRoleMembersStream(RealmModel realm, RoleModel role) { default Stream<UserModel> getRoleMembersStream(RealmModel realm, RoleModel role) {
return Stream.empty(); return Stream.empty();
@ -422,7 +429,7 @@ public interface UserQueryProvider {
* @param role a reference to the role. * @param role a reference to the role.
* @param firstResult first result to return. Ignored if negative. * @param firstResult first result to return. Ignored if negative.
* @param maxResults maximum number of results to return. Ignored if negative. * @param maxResults maximum number of results to return. Ignored if negative.
* @return a non-null {@code Stream} of users that have the specified role. * @return a non-null {@link Stream} of users that have the specified role.
*/ */
default Stream<UserModel> getRoleMembersStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) { default Stream<UserModel> getRoleMembersStream(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
return Stream.empty(); return Stream.empty();
@ -443,9 +450,7 @@ public interface UserQueryProvider {
* instead. * instead.
*/ */
@Deprecated @Deprecated
default List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm);
return this.searchForUserByUserAttributeStream(attrName, attrValue, realm).collect(Collectors.toList());
}
/** /**
* Searches for users that have a specific attribute with a specific value. Implementations do not have to search in * Searches for users that have a specific attribute with a specific value. Implementations do not have to search in
@ -456,7 +461,91 @@ public interface UserQueryProvider {
* @param attrName the attribute name. * @param attrName the attribute name.
* @param attrValue the attribute value. * @param attrValue the attribute value.
* @param realm a reference to the realm. * @param realm a reference to the realm.
* @return a non-null {@code Stream} of users that match the search criteria. * @return a non-null {@link Stream} of users that match the search criteria.
*/ */
Stream<UserModel> searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm); default Stream<UserModel> searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) {
List<UserModel> value = this.searchForUserByUserAttribute(attrName, attrValue, realm);
return value != null ? value.stream() : Stream.empty();
}
/**
* The {@link Streams} interface makes all collection-based methods in {@link UserQueryProvider} default by
* providing implementations that delegate to the {@link Stream}-based variants instead of the other way around.
* <p/>
* It allows for implementations to focus on the {@link Stream}-based approach for processing sets of data and benefit
* from the potential memory and performance optimizations of that approach.
*/
interface Streams extends UserQueryProvider {
@Override
default List<UserModel> getUsers(RealmModel realm) {
return this.getUsersStream(realm).collect(Collectors.toList());
}
@Override
Stream<UserModel> getUsersStream(RealmModel realm);
@Override
default List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
return this.getUsersStream(realm, firstResult, maxResults).collect(Collectors.toList());
}
@Override
Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults);
@Override
default List<UserModel> searchForUser(String search, RealmModel realm) {
return this.searchForUserStream(search, realm).collect(Collectors.toList());
}
@Override
Stream<UserModel> searchForUserStream(String search, RealmModel realm);
@Override
default List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
return this.searchForUserStream(search, realm, firstResult, maxResults).collect(Collectors.toList());
}
@Override
Stream<UserModel> searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults);
@Override
default List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) {
return this.searchForUserStream(params, realm).collect(Collectors.toList());
}
@Override
Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm);
@Override
default List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
return this.searchForUserStream(params, realm, firstResult, maxResults).collect(Collectors.toList());
}
@Override
Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm, int firstResult, int maxResults);
@Override
default List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
return this.getGroupMembersStream(realm, group).collect(Collectors.toList());
}
@Override
Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group);
@Override
default List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
return this.getGroupMembersStream(realm, group, firstResult, maxResults).collect(Collectors.toList());
}
@Override
Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults);
@Override
default List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
return this.searchForUserByUserAttributeStream(attrName, attrValue, realm).collect(Collectors.toList());
}
@Override
Stream<UserModel> searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm);
}
} }

View file

@ -17,7 +17,6 @@
package org.keycloak.storage; package org.keycloak.storage;
import com.google.common.collect.Streams;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.component.ComponentFactory; import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
@ -27,7 +26,6 @@ import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel; import org.keycloak.models.GroupModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.ModelException; import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -65,7 +63,8 @@ import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserStorageManager extends AbstractStorageManager<UserStorageProvider, UserStorageProviderModel> implements UserProvider, OnUserCache, OnCreateComponent, OnUpdateComponent { public class UserStorageManager extends AbstractStorageManager<UserStorageProvider, UserStorageProviderModel>
implements UserProvider.Streams, OnUserCache, OnCreateComponent, OnUpdateComponent {
private static final Logger logger = Logger.getLogger(UserStorageManager.class); private static final Logger logger = Logger.getLogger(UserStorageManager.class);
@ -605,7 +604,7 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
Stream<FederatedIdentityModel> stream = StorageId.isLocalStorage(user) ? Stream<FederatedIdentityModel> stream = StorageId.isLocalStorage(user) ?
localStorage().getFederatedIdentitiesStream(user, realm) : Stream.empty(); localStorage().getFederatedIdentitiesStream(user, realm) : Stream.empty();
if (getFederatedStorage() != null) if (getFederatedStorage() != null)
stream = Streams.concat(stream, getFederatedStorage().getFederatedIdentitiesStream(user.getId(), realm)); stream = Stream.concat(stream, getFederatedStorage().getFederatedIdentitiesStream(user.getId(), realm));
return stream.distinct(); return stream.distinct();
} }

View file

@ -57,7 +57,7 @@ import org.keycloak.storage.user.UserRegistrationProvider;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class BackwardsCompatibilityUserStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, public class BackwardsCompatibilityUserStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider,
CredentialInputUpdater, CredentialInputValidator, UserQueryProvider { CredentialInputUpdater, CredentialInputValidator, UserQueryProvider.Streams {
private static final Logger log = Logger.getLogger(BackwardsCompatibilityUserStorage.class); private static final Logger log = Logger.getLogger(BackwardsCompatibilityUserStorage.class);
@ -82,7 +82,7 @@ public class BackwardsCompatibilityUserStorage implements UserLookupProvider, Us
} }
private UserModel createUser(RealmModel realm, String username) { private UserModel createUser(RealmModel realm, String username) {
return new AbstractUserAdapterFederatedStorage(session, realm, model) { return new AbstractUserAdapterFederatedStorage.Streams(session, realm, model) {
@Override @Override
public String getUsername() { public String getUsername() {
return username; return username;

View file

@ -43,7 +43,8 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class FailableHardcodedStorageProvider implements UserStorageProvider, UserLookupProvider, UserQueryProvider, ImportedUserValidation, CredentialInputUpdater, CredentialInputValidator { public class FailableHardcodedStorageProvider implements UserStorageProvider, UserLookupProvider, UserQueryProvider.Streams,
ImportedUserValidation, CredentialInputUpdater, CredentialInputValidator {
public static String username = "billb"; public static String username = "billb";
public static String password = "password"; public static String password = "password";

View file

@ -68,7 +68,7 @@ public class HardcodedGroupStorageProvider implements GroupStorageProvider {
} }
public class HardcodedGroupAdapter implements GroupModel { public class HardcodedGroupAdapter implements GroupModel.Streams {
private final RealmModel realm; private final RealmModel realm;
private StorageId storageId; private StorageId storageId;

View file

@ -154,7 +154,7 @@ public class PassThroughFederatedUserStorageProvider implements
} }
private UserModel getUserModel(final RealmModel realm) { private UserModel getUserModel(final RealmModel realm) {
return new AbstractUserAdapterFederatedStorage(session, realm, component) { return new AbstractUserAdapterFederatedStorage.Streams(session, realm, component) {
@Override @Override
public String getUsername() { public String getUsername() {
return PASSTHROUGH_USERNAME; return PASSTHROUGH_USERNAME;

View file

@ -54,8 +54,8 @@ import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater, CredentialInputValidator, public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater,
UserGroupMembershipFederatedStorage, UserQueryProvider, ImportedUserValidation { CredentialInputValidator, UserGroupMembershipFederatedStorage.Streams, UserQueryProvider.Streams, ImportedUserValidation {
private static final Logger log = Logger.getLogger(UserMapStorage.class); private static final Logger log = Logger.getLogger(UserMapStorage.class);
@ -113,7 +113,7 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
user.setEnabled(true); user.setEnabled(true);
user.setFederationLink(model.getId()); user.setFederationLink(model.getId());
} else { } else {
user = new AbstractUserAdapterFederatedStorage(session, realm, model) { user = new AbstractUserAdapterFederatedStorage.Streams(session, realm, model) {
@Override @Override
public String getUsername() { public String getUsername() {
return username; return username;

View file

@ -33,11 +33,13 @@ import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
import org.keycloak.storage.user.UserLookupProvider; import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryProvider; import org.keycloak.storage.user.UserQueryProvider;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -69,7 +71,7 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
private UserModel createUser(RealmModel realm, String username) { private UserModel createUser(RealmModel realm, String username) {
if (federatedStorageEnabled) { if (federatedStorageEnabled) {
return new AbstractUserAdapterFederatedStorage(session, realm, model) { return new AbstractUserAdapterFederatedStorage.Streams(session, realm, model) {
@Override @Override
public String getUsername() { public String getUsername() {
return username; return username;
@ -81,7 +83,7 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
}; };
} else { } else {
return new AbstractUserAdapter(session, realm, model) { return new AbstractUserAdapter.Streams(session, realm, model) {
@Override @Override
public String getUsername() { public String getUsername() {
return username; return username;
@ -90,7 +92,6 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
} }
@Override
public UserModel getUserByUsername(String username, RealmModel realm) { public UserModel getUserByUsername(String username, RealmModel realm) {
if (!userPasswords.containsKey(username)) return null; if (!userPasswords.containsKey(username)) return null;
@ -144,59 +145,67 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
@Override @Override
public Stream<UserModel> getUsersStream(RealmModel realm) { public List<UserModel> getUsers(RealmModel realm) {
return userPasswords.keySet().stream().map(obj -> createUser(realm, (String) obj)); List<UserModel> users = new LinkedList<>();
for (Object username : userPasswords.keySet()) {
users.add(createUser(realm, (String)username));
}
return users;
} }
@Override @Override
public Stream<UserModel> searchForUserStream(Map<String, String> attributes, RealmModel realm) { public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm) {
return searchForUserStream(attributes, realm, 0, Integer.MAX_VALUE - 1); return searchForUser(attributes, realm, 0, Integer.MAX_VALUE - 1);
} }
@Override @Override
public Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults) { public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
Stream<Object> stream = userPasswords.keySet().stream(); if (maxResults == 0) return Collections.EMPTY_LIST;
if (firstResult > 0) List<UserModel> users = new LinkedList<>();
stream = stream.skip(firstResult); int count = 0;
if (maxResults >= 0) for (Object un : userPasswords.keySet()) {
stream = stream.limit(maxResults); if (count++ < firstResult) continue;
return stream.map(obj -> createUser(realm, (String) obj)); String username = (String)un;
users.add(createUser(realm, username));
if (users.size() + 1 > maxResults) break;
}
return users;
} }
@Override @Override
public Stream<UserModel> searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) { public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
return searchForUserStream(search, realm, firstResult, maxResults, username -> username.contains(search)); return searchForUser(search, realm, firstResult, maxResults, username -> username.contains(search));
} }
@Override @Override
public Stream<UserModel> searchForUserStream(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) { public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
String search = Optional.ofNullable(attributes.get(UserModel.USERNAME)) String search = Optional.ofNullable(attributes.get(UserModel.USERNAME))
.orElseGet(()-> attributes.get(UserModel.SEARCH)); .orElseGet(()-> attributes.get(UserModel.SEARCH));
if (search == null) return Stream.empty(); if (search == null) return Collections.EMPTY_LIST;
Predicate<String> p = Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString())) Predicate<String> p = Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))
? username -> username.equals(search) ? username -> username.equals(search)
: username -> username.contains(search); : username -> username.contains(search);
return searchForUserStream(search, realm, firstResult, maxResults, p); return searchForUser(search, realm, firstResult, maxResults, p);
} }
@Override @Override
public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, int firstResult, int maxResults) { public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
return Stream.empty(); return Collections.EMPTY_LIST;
} }
@Override @Override
public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group) { public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
return Stream.empty(); return Collections.EMPTY_LIST;
} }
@Override @Override
public Stream<UserModel> searchForUserStream(String search, RealmModel realm) { public List<UserModel> searchForUser(String search, RealmModel realm) {
return searchForUserStream(search, realm, 0, Integer.MAX_VALUE - 1); return searchForUser(search, realm, 0, Integer.MAX_VALUE - 1);
} }
@Override @Override
public Stream<UserModel> searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) { public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
return Stream.empty(); return Collections.EMPTY_LIST;
} }
@Override @Override
@ -204,8 +213,20 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
} }
private Stream<UserModel> searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults, Predicate<String> matcher) { private List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults, Predicate<String> matcher) {
return userPasswords.keySet().stream().filter(obj -> matcher.test((String) obj)).skip(firstResult < 0 ? 0 : firstResult) if (maxResults == 0) return Collections.EMPTY_LIST;
.limit(maxResults < 0 ? 0 : maxResults).map(obj -> createUser(realm, (String) obj)); List<UserModel> users = new LinkedList<>();
int count = 0;
for (Object un : userPasswords.keySet()) {
String username = (String)un;
if (matcher.test(username)) {
if (count++ < firstResult) {
continue;
}
users.add(createUser(realm, username));
if (users.size() + 1 > maxResults) break;
}
}
return users;
} }
} }