diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java
index 45d34e572f..7e46763613 100755
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java
@@ -92,7 +92,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
CredentialAuthentication,
UserLookupProvider,
UserRegistrationProvider,
- UserQueryProvider,
+ UserQueryProvider.Streams,
ImportedUserValidation {
private static final Logger logger = Logger.getLogger(LDAPStorageProvider.class);
diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java
index b22645bf2b..23e404ed8b 100644
--- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java
+++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java
@@ -32,7 +32,7 @@ import org.keycloak.storage.ReadOnlyException;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
+public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate {
public ReadonlyLDAPUserModelDelegate(UserModel delegate) {
super(delegate);
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java
index df900dc157..71911f699c 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class GroupAdapter implements GroupModel {
+public class GroupAdapter implements GroupModel.Streams {
protected final CachedGroup cached;
protected final RealmCacheSession cacheSession;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
index 157397c875..de102c7c09 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
@@ -42,7 +42,7 @@ import java.util.stream.Stream;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class UserAdapter implements CachedUserModel {
+public class UserAdapter implements CachedUserModel.Streams {
private final Supplier modelSupplier;
protected final CachedUser cached;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index 6a0c37e105..15338b9a51 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -71,7 +71,7 @@ import java.util.stream.Stream;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class UserCacheSession implements UserCache {
+public class UserCacheSession implements UserCache.Streams {
protected static final Logger logger = Logger.getLogger(UserCacheSession.class);
protected UserCacheManager cache;
protected KeycloakSession session;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
index e69f66a88f..2ae1c828f5 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java
@@ -31,12 +31,10 @@ import org.keycloak.models.utils.RoleUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.stream.Stream;
import javax.persistence.LockModeType;
@@ -46,7 +44,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class GroupAdapter implements GroupModel , JpaModel {
+public class GroupAdapter implements GroupModel.Streams , JpaModel {
protected GroupEntity group;
protected EntityManager em;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 348009b9d9..5224568fc0 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -82,7 +82,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @version $Revision: 1 $
*/
@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_VERIFIED = "emailVerified";
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index 42d21d2d76..7d9089c2b9 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -53,7 +53,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class UserAdapter implements UserModel, JpaModel {
+public class UserAdapter implements UserModel.Streams, JpaModel {
protected UserEntity user;
protected EntityManager em;
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
index ee4c4f5ca3..f065ab62c6 100644
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
@@ -70,7 +70,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @version $Revision: 1 $
*/
public class JpaUserFederatedStorageProvider implements
- UserFederatedStorageProvider,
+ UserFederatedStorageProvider.Streams,
UserCredentialStore {
protected static final Logger logger = Logger.getLogger(JpaUserFederatedStorageProvider.class);
diff --git a/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupModel.java b/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupModel.java
index 30a345f96c..5cd77faf87 100644
--- a/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupModel.java
+++ b/model/map/src/main/java/org/keycloak/models/map/group/AbstractGroupModel.java
@@ -24,7 +24,7 @@ import org.keycloak.models.map.common.AbstractEntity;
import java.util.Objects;
-public abstract class AbstractGroupModel implements GroupModel {
+public abstract class AbstractGroupModel implements GroupModel.Streams {
protected final KeycloakSession session;
protected final RealmModel realm;
diff --git a/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java b/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java
index 0d1471e5a2..50d5bfd0c8 100644
--- a/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java
+++ b/server-spi-private/src/main/java/org/keycloak/storage/adapter/InMemoryUserAdapter.java
@@ -31,7 +31,6 @@ import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,7 +40,7 @@ import java.util.stream.Stream;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class InMemoryUserAdapter extends UserModelDefaultMethods {
+public class InMemoryUserAdapter extends UserModelDefaultMethods.Streams {
private Long createdTimestamp = Time.currentTimeMillis();
private boolean emailVerified;
private boolean enabled;
diff --git a/server-spi/src/main/java/org/keycloak/models/GroupModel.java b/server-spi/src/main/java/org/keycloak/models/GroupModel.java
index c7971a9a1d..cd2e680a3c 100755
--- a/server-spi/src/main/java/org/keycloak/models/GroupModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/GroupModel.java
@@ -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
*/
@Deprecated
- default List getAttribute(String name) {
- return getAttributeStream(name).collect(Collectors.toList());
- }
+ List getAttribute(String name);
- Stream getAttributeStream(String name);
+ default Stream getAttributeStream(String name) {
+ List value = this.getAttribute(name);
+ return value != null ? value.stream() : Stream.empty();
+ }
Map> getAttributes();
@@ -80,11 +81,12 @@ public interface GroupModel extends RoleMapperModel {
String getParentId();
@Deprecated
- default Set getSubGroups() {
- return getSubGroupsStream().collect(Collectors.toSet());
- }
+ Set getSubGroups();
- Stream getSubGroupsStream();
+ default Stream getSubGroupsStream() {
+ Set 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
@@ -106,4 +108,29 @@ public interface GroupModel extends RoleMapperModel {
* @param 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.
+ *
+ * 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 getAttribute(String name) {
+ return this.getAttributeStream(name).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getAttributeStream(String name);
+
+ @Override
+ default Set getSubGroups() {
+ return this.getSubGroupsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getSubGroupsStream();
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java b/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java
index bf049d7291..fac9709041 100755
--- a/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java
@@ -32,15 +32,16 @@ public interface RoleMapperModel {
* @deprecated Use {@link #getRealmRoleMappingsStream()} getRealmRoleMappingsStream} instead.
*/
@Deprecated
- default Set getRealmRoleMappings() {
- return getRealmRoleMappingsStream().collect(Collectors.toSet());
- }
+ Set getRealmRoleMappings();
/**
* Returns stream of realm roles that are directly set to this object.
* @return stream of {@link RoleModel}
*/
- Stream getRealmRoleMappingsStream();
+ default Stream getRealmRoleMappingsStream() {
+ Set 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.
@@ -49,16 +50,17 @@ public interface RoleMapperModel {
* @deprecated Use {@link #getClientRoleMappingsStream(ClientModel)} getClientRoleMappingsStream} instead.
*/
@Deprecated
- default Set getClientRoleMappings(ClientModel app) {
- return getClientRoleMappingsStream(app).collect(Collectors.toSet());
- }
+ Set getClientRoleMappings(ClientModel app);
/**
* Returns stream of client roles that are directly set to this object for the given client.
* @param app Client to get the roles for
* @return stream of {@link RoleModel}
*/
- Stream getClientRoleMappingsStream(ClientModel app);
+ default Stream getClientRoleMappingsStream(ClientModel app) {
+ Set 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.
@@ -86,19 +88,53 @@ public interface RoleMapperModel {
* @deprecated Use {@link #getRoleMappingsStream()} getRoleMappingsStream} instead.
*/
@Deprecated
- default Set getRoleMappings() {
- return getRoleMappingsStream().collect(Collectors.toSet());
- }
+ Set getRoleMappings();
/**
* Returns stream of all role (both realm all client) that are directly set to this object.
* @return stream of {@link RoleModel}
*/
- Stream getRoleMappingsStream();
+ default Stream getRoleMappingsStream() {
+ Set value = this.getRoleMappings();
+ return value != null ? value.stream() : Stream.empty();
+ }
/**
* Removes the given role mapping from this object.
* @param role Role to remove
*/
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.
+ *
+ * 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 getRealmRoleMappings() {
+ return this.getRealmRoleMappingsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getRealmRoleMappingsStream();
+
+ @Override
+ default Set getClientRoleMappings(ClientModel app) {
+ return this.getClientRoleMappingsStream(app).collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getClientRoleMappingsStream(ClientModel app);
+
+ @Override
+ default Set getRoleMappings() {
+ return this.getRoleMappingsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getRoleMappingsStream();
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserModel.java b/server-spi/src/main/java/org/keycloak/models/UserModel.java
index 99738d036d..35f1b15f57 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserModel.java
@@ -94,17 +94,18 @@ public interface UserModel extends RoleMapperModel {
* @deprecated Use {@link #getAttributeStream(String) getAttributeStream} instead.
*/
@Deprecated
- default List getAttribute(String name) {
- return this.getAttributeStream(name).collect(Collectors.toList());
- }
+ List getAttribute(String name);
/**
* Obtains all values associated with the specified attribute name.
*
* @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 getAttributeStream(final String name);
+ default Stream getAttributeStream(final String name) {
+ List value = this.getAttribute(name);
+ return value != null ? value.stream() : Stream.empty();
+ }
Map> getAttributes();
@@ -112,16 +113,17 @@ public interface UserModel extends RoleMapperModel {
* @deprecated Use {@link #getRequiredActionsStream() getRequiredActionsStream} instead.
*/
@Deprecated
- default Set getRequiredActions() {
- return this.getRequiredActionsStream().collect(Collectors.toSet());
- }
+ Set getRequiredActions();
/**
* 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 getRequiredActionsStream();
+ default Stream getRequiredActionsStream() {
+ Set value = this.getRequiredActions();
+ return value != null ? value.stream() : Stream.empty();
+ }
void addRequiredAction(String action);
@@ -151,16 +153,17 @@ public interface UserModel extends RoleMapperModel {
* @deprecated Use {@link #getGroupsStream() getGroupsStream} instead.
*/
@Deprecated
- default Set getGroups() {
- return getGroupsStream().collect(Collectors.toSet());
- }
+ Set getGroups();
/**
* Obtains the groups associated with the user.
*
- * @return a non-null {@code Stream} of groups.
+ * @return a non-null {@link Stream} of groups.
*/
- Stream getGroupsStream();
+ default Stream getGroupsStream() {
+ Set value = this.getGroups();
+ return value != null ? value.stream() : Stream.empty();
+ }
/**
* @deprecated Use {@link #getGroupsStream(String, Integer, Integer) getGroupsStream} instead.
@@ -230,4 +233,37 @@ public interface UserModel extends RoleMapperModel {
enum RequiredAction {
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.
+ *
+ * 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 getAttribute(String name) {
+ return this.getAttributeStream(name).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getAttributeStream(final String name);
+
+ @Override
+ default Set getRequiredActions() {
+ return this.getRequiredActionsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getRequiredActionsStream();
+
+ @Override
+ default Set getGroups() {
+ return this.getGroupsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getGroupsStream();
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java b/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java
index fc9f6b9e40..179a4030bb 100644
--- a/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserModelDefaultMethods.java
@@ -17,6 +17,8 @@
package org.keycloak.models;
+import java.util.stream.Stream;
+
/**
* @author Martin Idel
* @version $Revision: 1 $
@@ -53,4 +55,13 @@ public abstract class UserModelDefaultMethods implements UserModel {
email = email == null ? null : email.toLowerCase();
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 {
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
index a67fc4ac99..0e3685e713 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
@@ -49,18 +49,19 @@ public interface UserProvider extends Provider,
* @deprecated Use {@link #getFederatedIdentitiesStream(UserModel, RealmModel) getFederatedIdentitiesStream} instead.
*/
@Deprecated
- default Set getFederatedIdentities(UserModel user, RealmModel realm) {
- return this.getFederatedIdentitiesStream(user, realm).collect(Collectors.toSet());
- }
+ Set getFederatedIdentities(UserModel user, RealmModel realm);
/**
* Obtains the federated identities of the specified user.
*
* @param user a reference to the user.
* @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 getFederatedIdentitiesStream(UserModel user, RealmModel realm);
+ default Stream getFederatedIdentitiesStream(UserModel user, RealmModel realm) {
+ Set value = this.getFederatedIdentities(user, realm);
+ return value != null ? value.stream() : Stream.empty();
+ }
FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, 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
- default List getConsents(RealmModel realm, String userId) {
- return getConsentsStream(realm, userId).collect(Collectors.toList());
- }
+ List getConsents(RealmModel realm, String userId);
/**
* Obtains the consents associated with the user identified by the specified {@code userId}.
*
* @param realm a reference to the realm.
* @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 getConsentsStream(RealmModel realm, String userId);
+ default Stream getConsentsStream(RealmModel realm, String userId) {
+ List value = this.getConsents(realm, userId);
+ return value != null ? value.stream() : Stream.empty();
+ }
void updateConsent(RealmModel realm, String userId, UserConsentModel consent);
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
- default List getUsers(RealmModel realm, boolean includeServiceAccounts) {
- return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList());
- }
+ List getUsers(RealmModel realm, boolean includeServiceAccounts);
/**
* Obtains the users associated with the specified realm.
*
* @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.
- * @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 getUsersStream(RealmModel realm, boolean includeServiceAccounts);
+ default Stream getUsersStream(RealmModel realm, boolean includeServiceAccounts) {
+ List value = this.getUsers(realm, includeServiceAccounts);
+ return value != null ? value.stream() : Stream.empty();
+ }
/**
* @deprecated Use {@link #getUsersStream(RealmModel, int, int, boolean) getUsersStream} instead.
*/
@Deprecated
- default List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
- return this.getUsersStream(realm, firstResult, maxResults, includeServiceAccounts).collect(Collectors.toList());
- }
+ List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
/**
* 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 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.
- * @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 getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
+ default Stream getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
+ List value = this.getUsers(realm, firstResult, maxResults, includeServiceAccounts);
+ return value != null ? value.stream() : Stream.empty();
+ }
/**
* only used for local storage
@@ -168,4 +172,38 @@ public interface UserProvider extends Provider,
void close();
void preRemove(RealmModel realm, ComponentModel component);
+
+ interface Streams extends UserProvider, UserQueryProvider.Streams {
+ @Override
+ default Set getFederatedIdentities(UserModel user, RealmModel realm) {
+ return this.getFederatedIdentitiesStream(user, realm).collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getFederatedIdentitiesStream(UserModel user, RealmModel realm);
+
+ @Override
+ default List getConsents(RealmModel realm, String userId) {
+ return this.getConsentsStream(realm, userId).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getConsentsStream(RealmModel realm, String userId);
+
+ @Override
+ default List getUsers(RealmModel realm, boolean includeServiceAccounts) {
+ return this.getUsersStream(realm, includeServiceAccounts).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getUsersStream(RealmModel realm, boolean includeServiceAccounts);
+
+ @Override
+ default List getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
+ return this.getUsersStream(realm, firstResult, maxResults, includeServiceAccounts).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getUsersStream(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java b/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java
index 1d3f59f289..268c816297 100644
--- a/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java
@@ -17,6 +17,7 @@
package org.keycloak.models.cache;
import org.keycloak.models.UserModel;
+import org.keycloak.models.UserProvider;
import java.util.concurrent.ConcurrentMap;
@@ -56,4 +57,12 @@ public interface CachedUserModel extends UserModel {
* @return
*/
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 {
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/cache/UserCache.java b/server-spi/src/main/java/org/keycloak/models/cache/UserCache.java
index ab7080817f..8d1b1343f3 100755
--- a/server-spi/src/main/java/org/keycloak/models/cache/UserCache.java
+++ b/server-spi/src/main/java/org/keycloak/models/cache/UserCache.java
@@ -47,4 +47,12 @@ public interface UserCache extends UserProvider {
*
*/
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 {
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
index 19fc110b5f..3acca8400b 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
@@ -33,7 +33,7 @@ import java.util.stream.Stream;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class UserModelDelegate implements UserModel {
+public class UserModelDelegate implements UserModel.Streams {
protected UserModel delegate;
public UserModelDelegate(UserModel delegate) {
diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
index 511e99a4cc..d047904fef 100644
--- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
+++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java
@@ -30,8 +30,12 @@ import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException;
import org.keycloak.storage.StorageId;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@@ -59,8 +63,8 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
}
@Override
- public Stream getRequiredActionsStream() {
- return Stream.empty();
+ public Set getRequiredActions() {
+ return Collections.emptySet();
}
@Override
@@ -91,8 +95,8 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
*
* @return
*/
- protected Stream getGroupsInternal() {
- return Stream.empty();
+ protected Set getGroupsInternal() {
+ return Collections.emptySet();
}
/**
@@ -107,10 +111,11 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
}
@Override
- public Stream getGroupsStream() {
- Stream groups = getGroupsInternal();
- if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream());
- return groups;
+ public Set getGroups() {
+ Set set = new HashSet<>();
+ if (appendDefaultGroups()) set.addAll(realm.getDefaultGroupsStream().collect(Collectors.toSet()));
+ set.addAll(getGroupsInternal());
+ return set;
}
@Override
@@ -127,23 +132,23 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
@Override
public boolean isMemberOf(GroupModel group) {
- return RoleUtils.isMember(getGroupsStream(), group);
+ return RoleUtils.isMember(getGroups().stream(), group);
}
@Override
- public Stream getRealmRoleMappingsStream() {
- return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
+ public Set getRealmRoleMappings() {
+ return getRoleMappings().stream().filter(RoleUtils::isRealmRole).collect(Collectors.toSet());
}
@Override
- public Stream getClientRoleMappingsStream(ClientModel app) {
- return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
+ public Set getClientRoleMappings(ClientModel app) {
+ return getRoleMappings().stream().filter(r -> RoleUtils.isClientRole(r, app)).collect(Collectors.toSet());
}
@Override
public boolean hasRole(RoleModel role) {
- return RoleUtils.hasRole(getRoleMappingsStream(), role)
- || RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
+ return RoleUtils.hasRole(getRoleMappings().stream(), role)
+ || RoleUtils.hasRoleFromGroup(getGroups().stream(), role, true);
}
@Override
@@ -163,15 +168,16 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
return true;
}
- protected Stream getRoleMappingsInternal() {
- return Stream.empty();
+ protected Set getRoleMappingsInternal() {
+ return Collections.emptySet();
}
@Override
- public Stream getRoleMappingsStream() {
- Stream roleMappings = getRoleMappingsInternal();
- if (appendDefaultRolesToRoleMappings()) return Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm));
- return roleMappings;
+ public Set getRoleMappings() {
+ Set set = new HashSet<>();
+ if (appendDefaultRolesToRoleMappings()) set.addAll(DefaultRoles.getDefaultRoles(realm).collect(Collectors.toSet()));
+ set.addAll(getRoleMappingsInternal());
+ return set;
}
@@ -300,11 +306,11 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
}
@Override
- public Stream getAttributeStream(String name) {
+ public List getAttribute(String name) {
if (name.equals(UserModel.USERNAME)) {
- return Stream.of(getUsername());
+ return Collections.singletonList(getUsername());
}
- return Stream.empty();
+ return Collections.emptyList();
}
@Override
@@ -365,4 +371,100 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
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 getRequiredActions() {
+ return this.getRequiredActionsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getRequiredActionsStream() {
+ return Stream.empty();
+ }
+
+ @Override
+ public List getAttribute(String name) {
+ return this.getAttributeStream(name).collect(Collectors.toList());
+ }
+
+ @Override
+ public Stream getAttributeStream(String name) {
+ if (name.equals(UserModel.USERNAME)) {
+ return Stream.of(getUsername());
+ }
+ return Stream.empty();
+ }
+
+ // group-related methods.
+
+
+ @Override
+ public Set getGroups() {
+ return this.getGroupsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getGroupsStream() {
+ Stream 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 getRealmRoleMappings() {
+ return this.getRealmRoleMappingsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getRealmRoleMappingsStream() {
+ return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
+ }
+
+ @Override
+ public Set getClientRoleMappings(ClientModel app) {
+ return this.getClientRoleMappingsStream(app).collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getClientRoleMappingsStream(ClientModel app) {
+ return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
+ }
+
+ @Override
+ public Set getRoleMappings() {
+ return this.getRoleMappingsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getRoleMappingsStream() {
+ Stream 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);
+ }
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
index f48b94a9a7..c420b70b0f 100644
--- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java
@@ -30,8 +30,12 @@ import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@@ -68,8 +72,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
}
@Override
- public Stream getRequiredActionsStream() {
- return getFederatedStorage().getRequiredActionsStream(realm, this.getId());
+ public Set getRequiredActions() {
+ return getFederatedStorage().getRequiredActions(realm, this.getId());
}
@Override
@@ -100,8 +104,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
*
* @return
*/
- protected Stream getGroupsInternal() {
- return Stream.empty();
+ protected Set getGroupsInternal() {
+ return Collections.emptySet();
}
/**
@@ -124,10 +128,11 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return
*/
@Override
- public Stream getGroupsStream() {
- Stream groups = getFederatedStorage().getGroupsStream(realm, this.getId());
- if (appendDefaultGroups()) groups = Stream.concat(groups, realm.getDefaultGroupsStream());
- return Stream.concat(groups, getGroupsInternal());
+ public Set getGroups() {
+ Set set = new HashSet<>(getFederatedStorage().getGroups(realm, this.getId()));
+ if (appendDefaultGroups()) set.addAll(realm.getDefaultGroupsStream().collect(Collectors.toSet()));
+ set.addAll(getGroupsInternal());
+ return set;
}
@Override
@@ -144,7 +149,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
@Override
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
*/
@Override
- public Stream getRealmRoleMappingsStream() {
- return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
+ public Set getRealmRoleMappings() {
+ return this.getRoleMappings().stream().filter(RoleUtils::isRealmRole).collect(Collectors.toSet());
}
/**
@@ -169,14 +174,15 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return
*/
@Override
- public Stream getClientRoleMappingsStream(ClientModel app) {
- return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
+ public Set getClientRoleMappings(ClientModel app) {
+ return getRoleMappings().stream().filter(r -> RoleUtils.isClientRole(r, app)).collect(Collectors.toSet());
}
+
@Override
public boolean hasRole(RoleModel role) {
- return RoleUtils.hasRole(getRoleMappingsStream(), role)
- || RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true);
+ return RoleUtils.hasRole(getRoleMappings().stream(), role)
+ || RoleUtils.hasRoleFromGroup(getGroups().stream(), role, true);
}
@Override
@@ -196,8 +202,8 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
return true;
}
- protected Stream getRoleMappingsInternal() {
- return Stream.empty();
+ protected Set getRoleMappingsInternal() {
+ return Collections.emptySet();
}
/**
@@ -208,14 +214,15 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
* @return
*/
@Override
- public Stream getRoleMappingsStream() {
- Stream roleMappings = getFederatedRoleMappings();
- if (appendDefaultRolesToRoleMappings()) roleMappings = Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm));
- return Stream.concat(roleMappings, getRoleMappingsInternal());
+ public Set getRoleMappings() {
+ Set set = new HashSet<>(getFederatedRoleMappings());
+ if (appendDefaultRolesToRoleMappings()) set.addAll(DefaultRoles.getDefaultRoles(realm).collect(Collectors.toSet()));
+ set.addAll(getRoleMappingsInternal());
+ return set;
}
- protected Stream getFederatedRoleMappings() {
- return getFederatedStorage().getRoleMappingsStream(realm, this.getId());
+ protected Set getFederatedRoleMappings() {
+ return getFederatedStorage().getRoleMappings(realm, this.getId());
}
@Override
@@ -357,15 +364,15 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
}
@Override
- public Stream getAttributeStream(String name) {
+ public List getAttribute(String name) {
if (UserModel.USERNAME.equals(name)) {
- return Stream.of(getUsername());
+ return Collections.singletonList(getUsername());
}
List 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)) {
return FIRST_NAME_ATTRIBUTE;
} else if (UserModel.LAST_NAME.equals(attributeName)) {
@@ -409,4 +416,101 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
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 getRequiredActions() {
+ return this.getRequiredActionsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getRequiredActionsStream() {
+ return super.getFederatedStorage().getRequiredActionsStream(super.realm, super.getId());
+ }
+
+ @Override
+ public List getAttribute(String name) {
+ return this.getAttributeStream(name).collect(Collectors.toList());
+ }
+
+ @Override
+ public Stream getAttributeStream(String name) {
+ if (UserModel.USERNAME.equals(name)) {
+ return Stream.of(getUsername());
+ }
+ List result = super.getFederatedStorage().getAttributes(realm, this.getId()).get(super.mapAttribute(name));
+ return (result == null) ? Stream.empty() : result.stream();
+ }
+
+ // group-related methods.
+
+ @Override
+ public Set getGroups() {
+ return this.getGroupsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getGroupsStream() {
+ Stream 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 getRealmRoleMappings() {
+ return this.getRealmRoleMappingsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getRealmRoleMappingsStream() {
+ return getRoleMappingsStream().filter(RoleUtils::isRealmRole);
+ }
+
+ @Override
+ public Set getClientRoleMappings(ClientModel app) {
+ return this.getClientRoleMappingsStream(app).collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getClientRoleMappingsStream(ClientModel app) {
+ return getRoleMappingsStream().filter(r -> RoleUtils.isClientRole(r, app));
+ }
+
+ @Override
+ public Set getRoleMappings() {
+ return this.getRoleMappingsStream().collect(Collectors.toSet());
+ }
+
+ @Override
+ public Stream getRoleMappingsStream() {
+ Stream 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);
+ }
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java
index b1919bc525..c27ca677b8 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserAttributeFederatedStorage.java
@@ -37,9 +37,7 @@ public interface UserAttributeFederatedStorage {
* @deprecated Use {@link #getUsersByUserAttributeStream(RealmModel, String, String) getUsersByUserAttributeStream} instead.
*/
@Deprecated
- default List getUsersByUserAttribute(RealmModel realm, String name, String value) {
- return this.getUsersByUserAttributeStream(realm, name, value).collect(Collectors.toList());
- }
+ List getUsersByUserAttribute(RealmModel realm, String name, String 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 name the attribute name.
* @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 getUsersByUserAttributeStream(RealmModel realm, String name, String value);
+ default Stream getUsersByUserAttributeStream(RealmModel realm, String name, String value) {
+ List 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.
+ *
+ * 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 getUsersByUserAttribute(RealmModel realm, String name, String value) {
+ return this.getUsersByUserAttributeStream(realm, name, value).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getUsersByUserAttributeStream(RealmModel realm, String name, String value);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java
index 68ae813e70..e0140cd148 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserBrokerLinkFederatedStorage.java
@@ -39,18 +39,37 @@ public interface UserBrokerLinkFederatedStorage {
* @deprecated Use {@link #getFederatedIdentitiesStream(String, RealmModel) getFederatedIdentitiesStream} instead.
*/
@Deprecated
- default Set getFederatedIdentities(String userId, RealmModel realm) {
- return this.getFederatedIdentitiesStream(userId, realm).collect(Collectors.toSet());
- }
+ Set getFederatedIdentities(String userId, RealmModel realm);
/**
* Obtains the identities of the federated user identified by {@code userId}.
*
* @param userId the user identifier.
* @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 getFederatedIdentitiesStream(String userId, RealmModel realm);
+ default Stream getFederatedIdentitiesStream(String userId, RealmModel realm) {
+ Set value = this.getFederatedIdentities(userId, realm);
+ return value != null ? value.stream() : Stream.empty();
+ }
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.
+ *
+ * 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 getFederatedIdentities(String userId, RealmModel realm) {
+ return this.getFederatedIdentitiesStream(userId, realm).collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getFederatedIdentitiesStream(String userId, RealmModel realm);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java
index 86219a2a4a..f0078d5e5f 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserConsentFederatedStorage.java
@@ -35,19 +35,38 @@ public interface UserConsentFederatedStorage {
* @deprecated Use {@link #getConsentsStream(RealmModel, String) getConsentsStream} instead.
*/
@Deprecated
- default List getConsents(RealmModel realm, String userId) {
- return this.getConsentsStream(realm, userId).collect(Collectors.toList());
- }
+ List getConsents(RealmModel realm, String userId);
/**
* Obtains the consents associated with the federated user identified by {@code userId}.
*
* @param realm a reference to the realm.
* @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 getConsentsStream(RealmModel realm, String userId);
+ default Stream getConsentsStream(RealmModel realm, String userId) {
+ List value = this.getConsents(realm, userId);
+ return value != null ? value.stream() : Stream.empty();
+ }
void updateConsent(RealmModel realm, String userId, UserConsentModel consent);
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.
+ *
+ * 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 getConsents(RealmModel realm, String userId) {
+ return this.getConsentsStream(realm, userId).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getConsentsStream(RealmModel realm, String userId);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
index 0479631c9c..d91b94525d 100755
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
@@ -49,9 +49,7 @@ public interface UserFederatedStorageProvider extends Provider,
* @deprecated Use {@link #getStoredUsersStream(RealmModel, int, int) getStoredUsersStream} instead.
*/
@Deprecated
- default List getStoredUsers(RealmModel realm, int first, int max) {
- return getStoredUsersStream(realm, first, max).collect(Collectors.toList());
- }
+ List getStoredUsers(RealmModel realm, int first, int max);
/**
* 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 first first result 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 getStoredUsersStream(RealmModel realm, int first, int max);
+ default Stream getStoredUsersStream(RealmModel realm, int first, int max) {
+ List value = this.getStoredUsers(realm, first, max);
+ return value != null ? value.stream() : Stream.empty();
+ }
int getStoredUsersCount(RealmModel realm);
@@ -80,4 +81,30 @@ public interface UserFederatedStorageProvider extends Provider,
void preRemove(RealmModel realm, UserModel user);
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.
+ *
+ * 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 getStoredUsers(RealmModel realm, int first, int max) {
+ return this.getStoredUsersStream(realm, first, max).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getStoredUsersStream(RealmModel realm, int first, int max);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java
index 676cce05d4..8b943d8fed 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedUserCredentialStore.java
@@ -38,26 +38,25 @@ public interface UserFederatedUserCredentialStore extends Provider {
* @deprecated Use {@link #getStoredCredentialsStream(RealmModel, String) getStoredCredentialsStream} instead.
*/
@Deprecated
- default List getStoredCredentials(RealmModel realm, String userId) {
- return this.getStoredCredentialsStream(realm, userId).collect(Collectors.toList());
- }
+ List getStoredCredentials(RealmModel realm, String userId);
/**
* Obtains the credentials associated with the federated user identified by {@code userId}.
*
* @param realm a reference to the realm.
* @param userId the user identifier.
- * @return a non-null {@code Stream} of credentials.
+ * @return a non-null {@link Stream} of credentials.
*/
- Stream getStoredCredentialsStream(RealmModel realm, String userId);
+ default Stream getStoredCredentialsStream(RealmModel realm, String userId) {
+ List value = this.getStoredCredentials(realm, userId);
+ return value != null ? value.stream() : Stream.empty();
+ }
/**
* @deprecated Use {@link #getStoredCredentialsByTypeStream(RealmModel, String, String) getStoredCredentialsByTypeStream} instead.
*/
@Deprecated
- default List getStoredCredentialsByType(RealmModel realm, String userId, String type) {
- return this.getStoredCredentialsByTypeStream(realm, userId, type).collect(Collectors.toList());
- }
+ List getStoredCredentialsByType(RealmModel realm, String userId, String type);
/**
* 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 userId the user identifier.
* @param type the credential type.
- * @return a non-null {@code Stream} of credentials.
+ * @return a non-null {@link Stream} of credentials.
*/
- Stream getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type);
+ default Stream getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type) {
+ List value = this.getStoredCredentialsByType(realm, userId, type);
+ return value != null ? value.stream() : Stream.empty();
+ }
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.
+ *
+ * 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 getStoredCredentials(RealmModel realm, String userId) {
+ return this.getStoredCredentialsStream(realm, userId).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getStoredCredentialsStream(RealmModel realm, String userId);
+
+ @Override
+ default List getStoredCredentialsByType(RealmModel realm, String userId, String type) {
+ return this.getStoredCredentialsByTypeStream(realm, userId, type).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getStoredCredentialsByTypeStream(RealmModel realm, String userId, String type);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java
index aad956d471..0933ad2447 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserGroupMembershipFederatedStorage.java
@@ -29,12 +29,24 @@ import java.util.stream.Stream;
* @version $Revision: 1 $
*/
public interface UserGroupMembershipFederatedStorage {
- @Deprecated
- default Set getGroups(RealmModel realm, String userId) {
- return getGroupsStream(realm, userId).collect(Collectors.toSet());
- }
- Stream getGroupsStream(RealmModel realm, String userId);
+ /**
+ * @deprecated Use {@link #getGroupsStream(RealmModel, String) getGroupsStream} instead.
+ */
+ @Deprecated
+ Set 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 getGroupsStream(RealmModel realm, String userId) {
+ Set value = this.getGroups(realm, userId);
+ return value != null ? value.stream() : Stream.empty();
+ }
void joinGroup(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
- default List getMembership(RealmModel realm, GroupModel group, int firstResult, int max) {
- return this.getMembershipStream(realm, group, firstResult, max).collect(Collectors.toList());
- }
+ List getMembership(RealmModel realm, GroupModel group, int firstResult, int max);
/**
* 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.
* @return a non-null {@code Stream} of federated user ids that are members of the group in the realm.
*/
- Stream getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max);
+ default Stream getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max) {
+ List 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.
+ *
+ * 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 getGroups(RealmModel realm, String userId) {
+ return getGroupsStream(realm, userId).collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getGroupsStream(RealmModel realm, String userId);
+
+ @Override
+ default List getMembership(RealmModel realm, GroupModel group, int firstResult, int max) {
+ return this.getMembershipStream(realm, group, firstResult, max).collect(Collectors.toList());
+ }
+
+ @Override
+ Stream getMembershipStream(RealmModel realm, GroupModel group, int firstResult, int max);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java
index 23dce8e90c..165542aaf2 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserRequiredActionsFederatedStorage.java
@@ -32,18 +32,38 @@ public interface UserRequiredActionsFederatedStorage {
* @deprecated Use {@link #getRequiredActionsStream(RealmModel, String) getRequiredActionsStream} instead.
*/
@Deprecated
- default Set getRequiredActions(RealmModel realm, String userId) {
- return this.getRequiredActionsStream(realm, userId).collect(Collectors.toSet());
- }
+ Set getRequiredActions(RealmModel realm, String userId);
+
/**
* Obtains the names of required actions associated with the federated user identified by {@code userId}.
*
* @param realm a reference to the realm.
* @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 getRequiredActionsStream(RealmModel realm, String userId);
+ default Stream getRequiredActionsStream(RealmModel realm, String userId) {
+ Set value = this.getRequiredActions(realm, userId);
+ return value != null ? value.stream() : Stream.empty();
+ }
void addRequiredAction(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.
+ *
+ * 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 getRequiredActions(RealmModel realm, String userId) {
+ return this.getRequiredActionsStream(realm, userId).collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getRequiredActionsStream(RealmModel realm, String userId);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java
index 10ea27c3ec..3d8c0d0357 100644
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserRoleMappingsFederatedStorage.java
@@ -29,15 +29,13 @@ import java.util.stream.Stream;
*/
public interface UserRoleMappingsFederatedStorage {
- void grantRole(RealmModel realm, String userId, RoleModel role);
-
/**
* @deprecated Use {@link #getRoleMappingsStream(RealmModel, String) getRoleMappingsStream} instead.
*/
@Deprecated
- default Set getRoleMappings(RealmModel realm,String userId) {
- return getRoleMappingsStream(realm, userId).collect(Collectors.toSet());
- }
+ Set getRoleMappings(RealmModel realm,String userId);
+
+ void grantRole(RealmModel realm, String userId, RoleModel role);
/**
* Obtains the roles associated with the federated user identified by {@code userId}.
@@ -46,7 +44,28 @@ public interface UserRoleMappingsFederatedStorage {
* @param userId the user identifier.
* @return a non-null {@code Stream} of roles.
*/
- Stream getRoleMappingsStream(RealmModel realm, String userId);
+ default Stream getRoleMappingsStream(RealmModel realm, String userId) {
+ Set value = this.getRoleMappings(realm, userId);
+ return value != null ? value.stream() : Stream.empty();
+ }
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.
+ *
+ * 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 getRoleMappings(RealmModel realm, String userId) {
+ return getRoleMappingsStream(realm, userId).collect(Collectors.toSet());
+ }
+
+ @Override
+ Stream getRoleMappingsStream(RealmModel realm, String userId);
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java
index 8e8de6ad5b..f9d550133d 100644
--- a/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/user/UserQueryProvider.java
@@ -141,25 +141,23 @@ public interface UserQueryProvider {
* @deprecated Use {@link #getUsersStream(RealmModel) getUsersStream} instead.
*/
@Deprecated
- default List getUsers(RealmModel realm) {
- return this.getUsersStream(realm).collect(Collectors.toList());
- }
-
+ List getUsers(RealmModel realm);
/**
* Searches all users in 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 getUsersStream(RealmModel realm);
+ default Stream getUsersStream(RealmModel realm) {
+ List value = this.getUsers(realm);
+ return value != null ? value.stream() : Stream.empty();
+ }
/**
* @deprecated Use {@link #getUsersStream(RealmModel, int, int) getUsersStream} instead.
*/
@Deprecated
- default List getUsers(RealmModel realm, int firstResult, int maxResults) {
- return this.getUsersStream(realm, firstResult, maxResults).collect(Collectors.toList());
- }
+ List getUsers(RealmModel realm, int firstResult, int 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 firstResult first result 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 getUsersStream(RealmModel realm, int firstResult, int maxResults);
+ default Stream getUsersStream(RealmModel realm, int firstResult, int maxResults) {
+ List 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.
@@ -184,9 +185,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #searchForUserStream(String, RealmModel) searchForUserStream} instead.
*/
@Deprecated
- default List searchForUser(String search, RealmModel realm) {
- return this.searchForUserStream(search, realm).collect(Collectors.toList());
- }
+ List searchForUser(String search, RealmModel realm);
/**
* 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 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 searchForUserStream(String search, RealmModel realm);
+ default Stream searchForUserStream(String search, RealmModel realm) {
+ List 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.
@@ -215,9 +217,7 @@ public interface UserQueryProvider {
* @deprecated Use {@link #searchForUserStream(String, RealmModel, int, int) searchForUserStream} instead.
*/
@Deprecated
- default List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
- return this.searchForUserStream(search, realm, firstResult, maxResults).collect(Collectors.toList());
- }
+ List searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
/**
* 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 firstResult first result 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 searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults);
+ default Stream searchForUserStream(String search, RealmModel realm, int firstResult, int maxResults) {
+ List