[KEYCLOAK-16232] Streamify the UserCredentialStore and UserCredentialManager interfaces
This commit is contained in:
parent
73d0bb34c4
commit
edef93cd49
43 changed files with 447 additions and 303 deletions
|
@ -75,7 +75,7 @@ public class SecretQuestionCredentialProvider implements CredentialProvider<Secr
|
|||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!supportsCredentialType(credentialType)) return false;
|
||||
return !getCredentialStore().getStoredCredentialsByType(realm, user, credentialType).isEmpty();
|
||||
return getCredentialStore().getStoredCredentialsByTypeStream(realm, user, credentialType).count() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.credential.CredentialAuthentication;
|
|||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
|
||||
import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
|
@ -41,10 +40,9 @@ import org.keycloak.storage.UserStorageProviderModel;
|
|||
import org.keycloak.storage.user.ImportedUserValidation;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -52,7 +50,7 @@ import java.util.Set;
|
|||
public class KerberosFederationProvider implements UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
CredentialInputValidator,
|
||||
CredentialInputUpdater,
|
||||
CredentialInputUpdater.Streams,
|
||||
CredentialAuthentication,
|
||||
ImportedUserValidation {
|
||||
|
||||
|
@ -146,8 +144,8 @@ public class KerberosFederationProvider implements UserStorageProvider,
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package org.keycloak.storage.ldap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -88,7 +87,7 @@ import org.keycloak.storage.user.UserRegistrationProvider;
|
|||
*/
|
||||
public class LDAPStorageProvider implements UserStorageProvider,
|
||||
CredentialInputValidator,
|
||||
CredentialInputUpdater,
|
||||
CredentialInputUpdater.Streams,
|
||||
CredentialAuthentication,
|
||||
UserLookupProvider,
|
||||
UserRegistrationProvider,
|
||||
|
@ -687,8 +686,8 @@ public class LDAPStorageProvider implements UserStorageProvider,
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
public Set<String> getSupportedCredentialTypes() {
|
||||
|
|
|
@ -21,7 +21,6 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.federation.sssd.api.Sssd;
|
||||
import org.keycloak.federation.sssd.api.Sssd.User;
|
||||
import org.keycloak.federation.sssd.impl.PAMAuthenticator;
|
||||
|
@ -32,11 +31,10 @@ import org.keycloak.storage.UserStorageProvider;
|
|||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.ImportedUserValidation;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import sun.security.util.Password;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* SPI provider implementation to retrieve data from SSSD and authenticate
|
||||
|
@ -47,7 +45,7 @@ import java.util.Set;
|
|||
*/
|
||||
public class SSSDFederationProvider implements UserStorageProvider,
|
||||
UserLookupProvider,
|
||||
CredentialInputUpdater,
|
||||
CredentialInputUpdater.Streams,
|
||||
CredentialInputValidator,
|
||||
ImportedUserValidation {
|
||||
|
||||
|
@ -205,7 +203,7 @@ public class SSSDFederationProvider implements UserStorageProvider,
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,16 +30,20 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.LockModeType;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.keycloak.utils.StreamsUtil.closing;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JpaUserCredentialStore implements UserCredentialStore {
|
||||
public class JpaUserCredentialStore implements UserCredentialStore.Streams {
|
||||
|
||||
// Typical priority difference between 2 credentials
|
||||
public static final int PRIORITY_DIFFERENCE = 10;
|
||||
|
@ -106,31 +110,27 @@ public class JpaUserCredentialStore implements UserCredentialStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
|
||||
List<CredentialEntity> results = getStoredCredentialEntities(realm, user);
|
||||
|
||||
// list is ordered correctly by priority (lowest priority value first)
|
||||
return results.stream().map(this::toModel).collect(Collectors.toList());
|
||||
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||
return this.getStoredCredentialEntities(realm, user).map(this::toModel);
|
||||
}
|
||||
|
||||
private List<CredentialEntity> getStoredCredentialEntities(RealmModel realm, UserModel user) {
|
||||
private Stream<CredentialEntity> getStoredCredentialEntities(RealmModel realm, UserModel user) {
|
||||
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUser", CredentialEntity.class)
|
||||
.setParameter("user", userEntity);
|
||||
return query.getResultList();
|
||||
return closing(query.getResultStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
||||
return getStoredCredentials(realm, user).stream().filter(credential -> type.equals(credential.getType())).collect(Collectors.toList());
|
||||
public Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type) {
|
||||
return getStoredCredentialsStream(realm, user).filter(credential -> Objects.equals(type, credential.getType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
|
||||
List<CredentialModel> results = getStoredCredentials(realm, user).stream().filter(credential ->
|
||||
type.equals(credential.getType()) && name.equals(credential.getUserLabel())).collect(Collectors.toList());
|
||||
if (results.isEmpty()) return null;
|
||||
return results.get(0);
|
||||
return getStoredCredentialsStream(realm, user).filter(credential ->
|
||||
Objects.equals(type, credential.getType()) && Objects.equals(name, credential.getUserLabel()))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -151,7 +151,7 @@ public class JpaUserCredentialStore implements UserCredentialStore {
|
|||
entity.setUser(userRef);
|
||||
|
||||
//add in linkedlist to last position
|
||||
List<CredentialEntity> credentials = getStoredCredentialEntities(realm, user);
|
||||
List<CredentialEntity> credentials = getStoredCredentialEntities(realm, user).collect(Collectors.toList());
|
||||
int priority = credentials.isEmpty() ? PRIORITY_DIFFERENCE : credentials.get(credentials.size() - 1).getPriority() + PRIORITY_DIFFERENCE;
|
||||
entity.setPriority(priority);
|
||||
|
||||
|
@ -165,14 +165,11 @@ public class JpaUserCredentialStore implements UserCredentialStore {
|
|||
|
||||
int currentPriority = entity.getPriority();
|
||||
|
||||
List<CredentialEntity> credentials = getStoredCredentialEntities(realm, user);
|
||||
|
||||
// Decrease priority of all credentials after our
|
||||
for (CredentialEntity cred : credentials) {
|
||||
this.getStoredCredentialEntities(realm, user).forEach(cred -> {
|
||||
if (cred.getPriority() > currentPriority) {
|
||||
cred.setPriority(cred.getPriority() - PRIORITY_DIFFERENCE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
em.remove(entity);
|
||||
em.flush();
|
||||
|
@ -182,11 +179,9 @@ public class JpaUserCredentialStore implements UserCredentialStore {
|
|||
////Operations to handle the linked list of credentials
|
||||
@Override
|
||||
public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId) {
|
||||
List<CredentialEntity> sortedCreds = getStoredCredentialEntities(realm, user);
|
||||
|
||||
// 1 - Create new list and move everything to it.
|
||||
List<CredentialEntity> newList = new ArrayList<>();
|
||||
newList.addAll(sortedCreds);
|
||||
List<CredentialEntity> newList = this.getStoredCredentialEntities(realm, user).collect(Collectors.toList());
|
||||
|
||||
// 2 - Find indexes of our and newPrevious credential
|
||||
int ourCredentialIndex = -1;
|
||||
|
|
|
@ -64,12 +64,10 @@ import java.util.Collection;
|
|||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.persistence.LockModeType;
|
||||
|
@ -82,7 +80,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
@SuppressWarnings("JpaQueryApiInspection")
|
||||
public class JpaUserProvider implements UserProvider.Streams, UserCredentialStore {
|
||||
public class JpaUserProvider implements UserProvider.Streams, UserCredentialStore.Streams {
|
||||
|
||||
private static final String EMAIL = "email";
|
||||
private static final String EMAIL_VERIFIED = "emailVerified";
|
||||
|
@ -1022,27 +1020,20 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
|
||||
return credentialStore.getStoredCredentials(realm, user);
|
||||
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||
return credentialStore.getStoredCredentialsStream(realm, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
||||
List<CredentialEntity> results;
|
||||
public Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type) {
|
||||
UserEntity userEntity = userInEntityManagerContext(user.getId());
|
||||
if (userEntity != null) {
|
||||
|
||||
// user already in persistence context, no need to execute a query
|
||||
results = userEntity.getCredentials().stream().filter(it -> type.equals(it.getType()))
|
||||
return userEntity.getCredentials().stream().filter(it -> type.equals(it.getType()))
|
||||
.sorted(Comparator.comparingInt(CredentialEntity::getPriority))
|
||||
.collect(Collectors.toList());
|
||||
List<CredentialModel> rtn = new LinkedList<>();
|
||||
for (CredentialEntity entity : results) {
|
||||
rtn.add(toModel(entity));
|
||||
}
|
||||
return rtn;
|
||||
.map(this::toModel);
|
||||
} else {
|
||||
return credentialStore.getStoredCredentialsByType(realm, user, type);
|
||||
return credentialStore.getStoredCredentialsByTypeStream(realm, user, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
|
|||
*/
|
||||
public class JpaUserFederatedStorageProvider implements
|
||||
UserFederatedStorageProvider.Streams,
|
||||
UserCredentialStore {
|
||||
UserCredentialStore.Streams {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(JpaUserFederatedStorageProvider.class);
|
||||
|
||||
|
@ -690,13 +690,13 @@ public class JpaUserFederatedStorageProvider implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
|
||||
return getStoredCredentialsStream(realm, user.getId()).collect(Collectors.toList());
|
||||
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||
return getStoredCredentialsStream(realm, user.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
||||
return getStoredCredentialsByTypeStream(realm, user.getId(), type).collect(Collectors.toList());
|
||||
public Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type) {
|
||||
return getStoredCredentialsByTypeStream(realm, user.getId(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,11 +7,13 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface CredentialValidator<T extends CredentialProvider> {
|
||||
T getCredentialProvider(KeycloakSession session);
|
||||
default List<CredentialModel> getCredentials(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
return session.userCredentialManager().getStoredCredentialsByType(realm, user, getCredentialProvider(session).getType());
|
||||
return session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, getCredentialProvider(session).getType())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
default String getType(KeycloakSession session) {
|
||||
return getCredentialProvider(session).getType();
|
||||
|
|
|
@ -188,7 +188,8 @@ public class ModelToRepresentation {
|
|||
rep.setEnabled(user.isEnabled());
|
||||
rep.setEmailVerified(user.isEmailVerified());
|
||||
rep.setTotp(session.userCredentialManager().isConfiguredFor(realm, user, OTPCredentialModel.TYPE));
|
||||
rep.setDisableableCredentialTypes(session.userCredentialManager().getDisableableCredentialTypes(realm, user));
|
||||
rep.setDisableableCredentialTypes(session.userCredentialManager()
|
||||
.getDisableableCredentialTypesStream(realm, user).collect(Collectors.toSet()));
|
||||
rep.setFederationLink(user.getFederationLink());
|
||||
rep.setNotBefore(session.users().getNotBeforeOfUser(realm, user));
|
||||
rep.setRequiredActions(user.getRequiredActionsStream().collect(Collectors.toList()));
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.models.credential.PasswordCredentialModel;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -53,37 +54,36 @@ public class HistoryPasswordPolicyProvider implements PasswordPolicyProvider {
|
|||
PasswordPolicy policy = session.getContext().getRealm().getPasswordPolicy();
|
||||
int passwordHistoryPolicyValue = policy.getPolicyConfig(PasswordPolicy.PASSWORD_HISTORY_ID);
|
||||
if (passwordHistoryPolicyValue != -1) {
|
||||
List<CredentialModel> storedPasswords = session.userCredentialManager().getStoredCredentialsByType(realm, user, PasswordCredentialModel.TYPE);
|
||||
for (CredentialModel cred : storedPasswords) {
|
||||
PasswordCredentialModel passwordCredential = PasswordCredentialModel.createFromCredentialModel(cred);
|
||||
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, passwordCredential.getPasswordCredentialData().getAlgorithm());
|
||||
if (hash == null) continue;
|
||||
if (hash.verify(password, passwordCredential)) {
|
||||
return new PolicyError(ERROR_MESSAGE, passwordHistoryPolicyValue);
|
||||
}
|
||||
if (session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, PasswordCredentialModel.TYPE)
|
||||
.map(PasswordCredentialModel::createFromCredentialModel)
|
||||
.anyMatch(passwordCredential -> {
|
||||
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class,
|
||||
passwordCredential.getPasswordCredentialData().getAlgorithm());
|
||||
return hash != null && hash.verify(password, passwordCredential);
|
||||
})) {
|
||||
return new PolicyError(ERROR_MESSAGE, passwordHistoryPolicyValue);
|
||||
}
|
||||
|
||||
if (passwordHistoryPolicyValue > 0) {
|
||||
List<CredentialModel> passwordHistory = session.userCredentialManager().getStoredCredentialsByType(realm, user, PasswordCredentialModel.PASSWORD_HISTORY);
|
||||
List<CredentialModel> recentPasswordHistory = getRecent(passwordHistory, passwordHistoryPolicyValue - 1);
|
||||
for (CredentialModel cred : recentPasswordHistory) {
|
||||
PasswordCredentialModel passwordCredential = PasswordCredentialModel.createFromCredentialModel(cred);
|
||||
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, passwordCredential.getPasswordCredentialData().getAlgorithm());
|
||||
if (hash.verify(password, passwordCredential)) {
|
||||
return new PolicyError(ERROR_MESSAGE, passwordHistoryPolicyValue);
|
||||
}
|
||||
|
||||
if (this.getRecent(session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, PasswordCredentialModel.PASSWORD_HISTORY),
|
||||
passwordHistoryPolicyValue - 1)
|
||||
.map(PasswordCredentialModel::createFromCredentialModel)
|
||||
.anyMatch(passwordCredential -> {
|
||||
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class,
|
||||
passwordCredential.getPasswordCredentialData().getAlgorithm());
|
||||
return hash.verify(password, passwordCredential);
|
||||
})) {
|
||||
return new PolicyError(ERROR_MESSAGE, passwordHistoryPolicyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<CredentialModel> getRecent(List<CredentialModel> passwordHistory, int limit) {
|
||||
return passwordHistory.stream()
|
||||
private Stream<CredentialModel> getRecent(Stream<CredentialModel> passwordHistory, int limit) {
|
||||
return passwordHistory
|
||||
.sorted(CredentialModel.comparingByStartDateDesc())
|
||||
.limit(limit)
|
||||
.collect(Collectors.toList());
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -37,6 +39,39 @@ public interface CredentialInputUpdater {
|
|||
* @param realm
|
||||
* @param user
|
||||
* @return
|
||||
* @deprecated Use {@link #getDisableableCredentialTypesStream(RealmModel, UserModel) getDisableableCredentialTypesStream}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user);
|
||||
|
||||
/**
|
||||
* Obtains the set of credential types that can be disabled via {@link #disableCredentialType(RealmModel, UserModel, String)
|
||||
* disableCredentialType}.
|
||||
*
|
||||
* @param realm a reference to the realm.
|
||||
* @param user the user whose credentials are being searched.
|
||||
* @return a non-null {@link Stream} of credential types.
|
||||
*/
|
||||
default Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
Set<String> result = this.getDisableableCredentialTypes(realm, user);
|
||||
return result != null ? result.stream() : Stream.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link CredentialInputUpdater.Streams} interface makes all collection-based methods in {@link CredentialInputUpdater}
|
||||
* 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 CredentialInputUpdater {
|
||||
@Override
|
||||
default Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return this.getDisableableCredentialTypesStream(realm, user).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -43,11 +41,9 @@ public interface CredentialProvider<T extends CredentialModel> extends Provider
|
|||
T getCredentialFromModel(CredentialModel model);
|
||||
|
||||
default T getDefaultCredential(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
List<CredentialModel> models = session.userCredentialManager().getStoredCredentialsByType(realm, user, getType());
|
||||
if (models.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getCredentialFromModel(models.get(0));
|
||||
CredentialModel model = session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, getType())
|
||||
.findFirst().orElse(null);
|
||||
return model != null ? getCredentialFromModel(model) : null;
|
||||
}
|
||||
|
||||
CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext);
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -31,11 +33,72 @@ public interface UserCredentialStore extends Provider {
|
|||
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
|
||||
boolean removeStoredCredential(RealmModel realm, UserModel user, String id);
|
||||
CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getStoredCredentialsStream(RealmModel, UserModel) getStoredCredentialsStream} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user);
|
||||
|
||||
/**
|
||||
* Obtains the stored credentials associated with the specified user.
|
||||
*
|
||||
* @param realm a reference to the realm.
|
||||
* @param user the user whose credentials are being searched.
|
||||
* @return a non-null {@link Stream} of credentials.
|
||||
*/
|
||||
default Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||
List<CredentialModel> result = this.getStoredCredentials(realm, user);
|
||||
return result != null ? result.stream() : Stream.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getStoredCredentialsByTypeStream(RealmModel, UserModel, String) getStoredCredentialsByTypeStream}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type);
|
||||
|
||||
/**
|
||||
* Obtains the stored credentials associated with the specified user that match the specified type.
|
||||
*
|
||||
* @param realm a reference to the realm.
|
||||
* @param user the user whose credentials are being searched.
|
||||
* @param type the type of credentials being searched.
|
||||
* @return a non-null {@link Stream} of credentials.
|
||||
*/
|
||||
default Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type) {
|
||||
List<CredentialModel> result = this.getStoredCredentialsByType(realm, user, type);
|
||||
return result != null ? result.stream() : Stream.empty();
|
||||
}
|
||||
|
||||
CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
|
||||
|
||||
//list operations
|
||||
boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId);
|
||||
|
||||
/**
|
||||
* The {@link UserCredentialStore.Streams} interface makes all collection-based methods in {@link UserCredentialStore}
|
||||
* 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 UserCredentialStore {
|
||||
@Override
|
||||
default List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
|
||||
return this.getStoredCredentialsStream(realm, user).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user);
|
||||
|
||||
@Override
|
||||
default List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
||||
return this.getStoredCredentialsByTypeStream(realm, user, type).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import org.keycloak.credential.UserCredentialStore;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -95,9 +97,25 @@ public interface UserCredentialManager extends UserCredentialStore {
|
|||
* @param realm
|
||||
* @param user
|
||||
* @return
|
||||
* @deprecated Use {@link #getDisableableCredentialTypesStream(RealmModel, UserModel) getDisableableCredentialTypesStream}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user);
|
||||
|
||||
/**
|
||||
* Obtains the credential types that can be disabled by means of the {@link #disableCredentialType(RealmModel, UserModel, String)}
|
||||
* method.
|
||||
*
|
||||
* @param realm a reference to the realm.
|
||||
* @param user the user whose credentials are being searched.
|
||||
* @return a non-null {@link Stream} of credential types.
|
||||
*/
|
||||
default Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
Set<String> result = this.getDisableableCredentialTypes(realm, user);
|
||||
return result != null ? result.stream() : Stream.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if user has credential type configured. Looks in UserStorageProvider or UserFederationProvider first,
|
||||
* then loops through each CredentialProvider.
|
||||
|
@ -139,6 +157,49 @@ public interface UserCredentialManager extends UserCredentialStore {
|
|||
* This will always return empty list for "local" users, which are not backed by any user storage
|
||||
*
|
||||
* @return
|
||||
* @deprecated Use {@link #getConfiguredUserStorageCredentialTypesStream(RealmModel, UserModel) getConfiguredUserStorageCredentialTypesStream}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
List<String> getConfiguredUserStorageCredentialTypes(RealmModel realm, UserModel user);
|
||||
|
||||
/**
|
||||
* Obtains the credential types provided by the user storage where the specified user is stored. Examples of returned
|
||||
* values are "password", "otp", etc.
|
||||
* <p/>
|
||||
* This method will always return an empty stream for "local" users - i.e. users that are not backed by any user storage.
|
||||
*
|
||||
* @param realm a reference to the realm.
|
||||
* @param user a reference to the user.
|
||||
* @return a non-null {@link Stream} of credential types.
|
||||
*/
|
||||
default Stream<String> getConfiguredUserStorageCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
List<String> result = this.getConfiguredUserStorageCredentialTypes(realm, user);
|
||||
return result != null ? result.stream() : Stream.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link UserCredentialManager.Streams} interface makes all collection-based methods in {@link UserCredentialManager}
|
||||
* 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 UserCredentialManager, UserCredentialStore.Streams {
|
||||
@Override
|
||||
default Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return this.getDisableableCredentialTypesStream(realm, user).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user);
|
||||
|
||||
@Override
|
||||
default List<String> getConfiguredUserStorageCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return this.getConfiguredUserStorageCredentialTypesStream(realm, user).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
Stream<String> getConfiguredUserStorageCredentialTypesStream(RealmModel realm, UserModel user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,13 +76,9 @@ class AuthenticationSelectionResolver {
|
|||
|
||||
//add credential authenticators in order
|
||||
if (processor.getAuthenticationSession().getAuthenticatedUser() != null) {
|
||||
List<CredentialModel> credentials = processor.getSession().userCredentialManager()
|
||||
.getStoredCredentials(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser())
|
||||
.stream()
|
||||
authenticationSelectionList = processor.getSession().userCredentialManager()
|
||||
.getStoredCredentialsStream(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser())
|
||||
.filter(credential -> typeAuthExecMap.containsKey(credential.getType()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
authenticationSelectionList = credentials.stream()
|
||||
.map(CredentialModel::getType)
|
||||
.distinct()
|
||||
.map(credentialType -> new AuthenticationSelectionOption(processor.getSession(), typeAuthExecMap.get(credentialType)))
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.authentication.authenticators.x509;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -67,7 +68,7 @@ public abstract class UserIdentityToModelMapper {
|
|||
for (int i = 1; i <_customAttributes.size(); ++i) {
|
||||
String customAttribute = _customAttributes.get(i);
|
||||
String userIdentityValue = userIdentityValues.get(i);
|
||||
usersStream = usersStream.filter(user -> user.getFirstAttribute(customAttribute).equals(userIdentityValue));
|
||||
usersStream = usersStream.filter(user -> Objects.equals(user.getFirstAttribute(customAttribute), userIdentityValue));
|
||||
}
|
||||
List<UserModel> users = usersStream.collect(Collectors.toList());
|
||||
if (users.size() > 1) {
|
||||
|
|
|
@ -43,8 +43,7 @@ import org.keycloak.utils.CredentialHelper;
|
|||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -96,10 +95,10 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
|
|||
return;
|
||||
}
|
||||
OTPCredentialProvider otpCredentialProvider = (OTPCredentialProvider) context.getSession().getProvider(CredentialProvider.class, "keycloak-otp");
|
||||
final List<CredentialModel> otpCredentials = (otpCredentialProvider.isConfiguredFor(context.getRealm(), context.getUser()))
|
||||
? context.getSession().userCredentialManager().getStoredCredentialsByType(context.getRealm(), context.getUser(), OTPCredentialModel.TYPE)
|
||||
: Collections.EMPTY_LIST;
|
||||
if (otpCredentials.size() >= 1 && Validation.isBlank(userLabel)) {
|
||||
final Stream<CredentialModel> otpCredentials = (otpCredentialProvider.isConfiguredFor(context.getRealm(), context.getUser()))
|
||||
? context.getSession().userCredentialManager().getStoredCredentialsByTypeStream(context.getRealm(), context.getUser(), OTPCredentialModel.TYPE)
|
||||
: Stream.empty();
|
||||
if (otpCredentials.count() >= 1 && Validation.isBlank(userLabel)) {
|
||||
Response challenge = context.form()
|
||||
.setAttribute("mode", mode)
|
||||
.addError(new FormMessage(Validation.FIELD_OTP_LABEL, Messages.MISSING_TOTP_DEVICE_NAME))
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Base64;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -47,7 +48,6 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.WebAuthnPolicy;
|
||||
|
||||
import com.webauthn4j.WebAuthnManager;
|
||||
import com.webauthn4j.converter.util.ObjectConverter;
|
||||
import com.webauthn4j.data.attestation.authenticator.AttestedCredentialData;
|
||||
import com.webauthn4j.data.attestation.statement.AttestationStatement;
|
||||
|
@ -125,15 +125,11 @@ public class WebAuthnRegister implements RequiredActionProvider, CredentialRegis
|
|||
|
||||
String excludeCredentialIds = "";
|
||||
if (avoidSameAuthenticatorRegister) {
|
||||
List<CredentialModel> webAuthnCredentials = session.userCredentialManager().getStoredCredentialsByType(context.getRealm(), userModel, getCredentialType());
|
||||
List<String> webAuthnCredentialPubKeyIds = webAuthnCredentials.stream().map(credentialModel -> {
|
||||
|
||||
WebAuthnCredentialModel credModel = WebAuthnCredentialModel.createFromCredentialModel(credentialModel);
|
||||
return Base64Url.encodeBase64ToBase64Url(credModel.getWebAuthnCredentialData().getCredentialId());
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
excludeCredentialIds = stringifyExcludeCredentialIds(webAuthnCredentialPubKeyIds);
|
||||
excludeCredentialIds = session.userCredentialManager().getStoredCredentialsByTypeStream(context.getRealm(), userModel, getCredentialType())
|
||||
.map(credentialModel -> {
|
||||
WebAuthnCredentialModel credModel = WebAuthnCredentialModel.createFromCredentialModel(credentialModel);
|
||||
return Base64Url.encodeBase64ToBase64Url(credModel.getWebAuthnCredentialData().getCredentialId());
|
||||
}).collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
String isSetRetry = context.getHttpRequest().getDecodedFormParameters().getFirst(WebAuthnConstants.IS_SET_RETRY);
|
||||
|
@ -300,15 +296,6 @@ public class WebAuthnRegister implements RequiredActionProvider, CredentialRegis
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
private String stringifyExcludeCredentialIds(List<String> credentialIdsList) {
|
||||
if (credentialIdsList == null || credentialIdsList.isEmpty()) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : credentialIdsList)
|
||||
if (s != null && !s.isEmpty()) sb.append(s).append(",");
|
||||
if (sb.lastIndexOf(",") > -1) sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void showInfoAfterWebAuthnApiCreate(RegistrationData response) {
|
||||
AttestedCredentialData attestedCredentialData = response.getAttestationObject().getAuthenticatorData().getAttestedCredentialData();
|
||||
AttestationStatement attestationStatement = response.getAttestationObject().getAttestationStatement();
|
||||
|
|
|
@ -92,7 +92,7 @@ public class OTPCredentialProvider implements CredentialProvider<OTPCredentialMo
|
|||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!supportsCredentialType(credentialType)) return false;
|
||||
return !getCredentialStore().getStoredCredentialsByType(realm, user, credentialType).isEmpty();
|
||||
return getCredentialStore().getStoredCredentialsByTypeStream(realm, user, credentialType).count() > 0;
|
||||
}
|
||||
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user){
|
||||
|
|
|
@ -32,15 +32,16 @@ import org.keycloak.models.cache.UserCache;
|
|||
import org.keycloak.policy.PasswordPolicyManagerProvider;
|
||||
import org.keycloak.policy.PolicyError;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PasswordCredentialProvider implements CredentialProvider<PasswordCredentialModel>, CredentialInputUpdater, CredentialInputValidator, OnUserCache {
|
||||
public class PasswordCredentialProvider implements CredentialProvider<PasswordCredentialModel>, CredentialInputUpdater.Streams,
|
||||
CredentialInputValidator, OnUserCache {
|
||||
|
||||
public static final String PASSWORD_CACHE_KEY = PasswordCredentialProvider.class.getName() + "." + PasswordCredentialModel.TYPE;
|
||||
private static final Logger logger = Logger.getLogger(PasswordCredentialProvider.class);
|
||||
|
@ -64,7 +65,7 @@ public class PasswordCredentialProvider implements CredentialProvider<PasswordCr
|
|||
}
|
||||
// if the model was marked for eviction while passwords were initialized, override it from credentialStore
|
||||
if (!(user instanceof CachedUserModel) || ((CachedUserModel) user).isMarkedForEviction()) {
|
||||
passwords = getCredentialStore().getStoredCredentialsByType(realm, user, getType());
|
||||
passwords = getCredentialStore().getStoredCredentialsByTypeStream(realm, user, getType()).collect(Collectors.toList());
|
||||
}
|
||||
if (passwords == null || passwords.isEmpty()) return null;
|
||||
|
||||
|
@ -115,14 +116,12 @@ public class PasswordCredentialProvider implements CredentialProvider<PasswordCr
|
|||
}
|
||||
|
||||
// 3) remove old password history items
|
||||
List<CredentialModel> passwordHistoryList = getCredentialStore().getStoredCredentialsByType(realm, user, PasswordCredentialModel.PASSWORD_HISTORY);
|
||||
final int passwordHistoryListMaxSize = Math.max(0, expiredPasswordsPolicyValue - 1);
|
||||
if (passwordHistoryList.size() > passwordHistoryListMaxSize) {
|
||||
passwordHistoryList.stream()
|
||||
.sorted(CredentialModel.comparingByStartDateDesc())
|
||||
.skip(passwordHistoryListMaxSize)
|
||||
.forEach(p -> getCredentialStore().removeStoredCredential(realm, user, p.getId()));
|
||||
}
|
||||
getCredentialStore().getStoredCredentialsByTypeStream(realm, user, PasswordCredentialModel.PASSWORD_HISTORY)
|
||||
.sorted(CredentialModel.comparingByStartDateDesc())
|
||||
.skip(passwordHistoryListMaxSize)
|
||||
.collect(Collectors.toList())
|
||||
.forEach(p -> getCredentialStore().removeStoredCredential(realm, user, p.getId()));
|
||||
|
||||
UserCache userCache = session.userCache();
|
||||
if (userCache != null) {
|
||||
|
@ -220,8 +219,8 @@ public class PasswordCredentialProvider implements CredentialProvider<PasswordCr
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.emptySet();
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -282,11 +281,9 @@ public class PasswordCredentialProvider implements CredentialProvider<PasswordCr
|
|||
|
||||
@Override
|
||||
public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) {
|
||||
List<CredentialModel> passwords = getCredentialStore().getStoredCredentialsByType(realm, user, getType());
|
||||
if (passwords != null) {
|
||||
user.getCachedWith().put(PASSWORD_CACHE_KEY, passwords);
|
||||
}
|
||||
|
||||
List<CredentialModel> passwords = getCredentialStore().getStoredCredentialsByTypeStream(realm, user, getType())
|
||||
.collect(Collectors.toList());
|
||||
user.getCachedWith().put(PASSWORD_CACHE_KEY, passwords);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.OnUserCache;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.storage.AbstractStorageManager;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
@ -33,12 +32,8 @@ import org.keycloak.storage.UserStorageProviderFactory;
|
|||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -46,7 +41,8 @@ import java.util.stream.Stream;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserCredentialStoreManager extends AbstractStorageManager<UserStorageProvider, UserStorageProviderModel> implements UserCredentialManager, OnUserCache {
|
||||
public class UserCredentialStoreManager extends AbstractStorageManager<UserStorageProvider, UserStorageProviderModel>
|
||||
implements UserCredentialManager.Streams, OnUserCache {
|
||||
|
||||
public UserCredentialStoreManager(KeycloakSession session) {
|
||||
super(session, UserStorageProviderFactory.class, UserStorageProvider.class, UserStorageProviderModel::new, "user");
|
||||
|
@ -89,13 +85,13 @@ public class UserCredentialStoreManager extends AbstractStorageManager<UserStora
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
|
||||
return getStoreForUser(user).getStoredCredentials(realm, user);
|
||||
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||
return getStoreForUser(user).getStoredCredentialsStream(realm, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
|
||||
return getStoreForUser(user).getStoredCredentialsByType(realm, user, type);
|
||||
public Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type) {
|
||||
return getStoreForUser(user).getStoredCredentialsByTypeStream(realm, user, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -218,23 +214,20 @@ public class UserCredentialStoreManager extends AbstractStorageManager<UserStora
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
Set<String> types = new HashSet<>();
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
Stream<String> types = Stream.empty();
|
||||
String providerId = StorageId.isLocalStorage(user) ? user.getFederationLink() : StorageId.resolveProviderId(user);
|
||||
if (providerId != null) {
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
if (model == null || !model.isEnabled()) return Collections.EMPTY_SET;
|
||||
if (model == null || !model.isEnabled()) return types;
|
||||
|
||||
CredentialInputUpdater updater = getStorageProviderInstance(model, CredentialInputUpdater.class);
|
||||
if (updater != null) types.addAll(updater.getDisableableCredentialTypes(realm, user));
|
||||
if (updater != null) types = updater.getDisableableCredentialTypesStream(realm, user);
|
||||
}
|
||||
|
||||
types.addAll(getCredentialProviders(session, CredentialInputUpdater.class)
|
||||
.map(updater -> updater.getDisableableCredentialTypes(realm, user))
|
||||
.flatMap(Set::stream)
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
return types;
|
||||
return Stream.concat(types, getCredentialProviders(session, CredentialInputUpdater.class)
|
||||
.flatMap(updater -> updater.getDisableableCredentialTypesStream(realm, user)))
|
||||
.distinct();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -298,10 +291,9 @@ public class UserCredentialStoreManager extends AbstractStorageManager<UserStora
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> getConfiguredUserStorageCredentialTypes(RealmModel realm, UserModel user) {
|
||||
public Stream<String> getConfiguredUserStorageCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return getCredentialProviders(session, CredentialProvider.class).map(CredentialProvider::getType)
|
||||
.filter(credentialType -> UserStorageCredentialConfigured.CONFIGURED == isConfiguredThroughUserStorage(realm, user, credentialType))
|
||||
.collect(Collectors.toList());
|
||||
.filter(credentialType -> UserStorageCredentialConfigured.CONFIGURED == isConfiguredThroughUserStorage(realm, user, credentialType));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -157,7 +157,7 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
|
|||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (!supportsCredentialType(credentialType)) return false;
|
||||
return !session.userCredentialManager().getStoredCredentialsByType(realm, user, credentialType).isEmpty();
|
||||
return session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, credentialType).count() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -224,9 +224,7 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
|
|||
|
||||
|
||||
private List<WebAuthnCredentialModelInput> getWebAuthnCredentialModelList(RealmModel realm, UserModel user) {
|
||||
List<CredentialModel> credentialModels = session.userCredentialManager().getStoredCredentialsByType(realm, user, getType());
|
||||
|
||||
return credentialModels.stream()
|
||||
return session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, getType())
|
||||
.map(this::getCredentialInputFromCredentialModel)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
|
|
@ -495,12 +495,8 @@ public class ExportUtils {
|
|||
|
||||
// Credentials - extra security, do not export credentials if service accounts
|
||||
if (internal) {
|
||||
List<CredentialModel> creds = session.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialRepresentation> credReps = new ArrayList<>();
|
||||
for (CredentialModel cred : creds) {
|
||||
CredentialRepresentation credRep = exportCredential(cred);
|
||||
credReps.add(credRep);
|
||||
}
|
||||
List<CredentialRepresentation> credReps = session.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.map(ExportUtils::exportCredential).collect(Collectors.toList());
|
||||
userRep.setCredentials(credReps);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,45 +54,7 @@ public class ApplicationsBean {
|
|||
|
||||
this.applications = this.getApplications(session, realm, user)
|
||||
.filter(client -> !isAdminClient(client) || AdminPermissions.realms(session, realm, user).isAdmin())
|
||||
.map(client -> {
|
||||
|
||||
// Construct scope parameter with all optional scopes to see all potentially available roles
|
||||
Stream<ClientScopeModel> allClientScopes = Stream.concat(
|
||||
client.getClientScopes(true, true).values().stream(),
|
||||
client.getClientScopes(false, true).values().stream());
|
||||
allClientScopes = Stream.concat(allClientScopes, Stream.of(client)).distinct();
|
||||
|
||||
Set<RoleModel> availableRoles = TokenManager.getAccess(user, client, allClientScopes);
|
||||
|
||||
// Don't show applications, which user doesn't have access into (any available roles)
|
||||
// unless this is can be changed by approving/revoking consent
|
||||
if (! isAdminClient(client) && availableRoles.isEmpty() && ! client.isConsentRequired()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RoleModel> realmRolesAvailable = new LinkedList<>();
|
||||
MultivaluedHashMap<String, ClientRoleEntry> resourceRolesAvailable = new MultivaluedHashMap<>();
|
||||
processRoles(availableRoles, realmRolesAvailable, resourceRolesAvailable);
|
||||
|
||||
List<ClientScopeModel> orderedScopes = new LinkedList<>();
|
||||
if (client.isConsentRequired()) {
|
||||
UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
|
||||
|
||||
if (consent != null) {
|
||||
orderedScopes.addAll(consent.getGrantedClientScopes());
|
||||
}
|
||||
}
|
||||
List<String> clientScopesGranted = orderedScopes.stream()
|
||||
.sorted(OrderedModel.OrderedModelComparator.getInstance())
|
||||
.map(ClientScopeModel::getConsentScreenText)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> additionalGrants = new ArrayList<>();
|
||||
if (offlineClients.contains(client)) {
|
||||
additionalGrants.add("${offlineToken}");
|
||||
}
|
||||
return new ApplicationEntry(session, realmRolesAvailable, resourceRolesAvailable, client, clientScopesGranted, additionalGrants);
|
||||
})
|
||||
.map(client -> toApplicationEntry(session, realm, user, client, offlineClients))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
@ -204,4 +166,56 @@ public class ApplicationsBean {
|
|||
return roleDescription;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link ApplicationEntry} from the specified parameters.
|
||||
*
|
||||
* @param session a reference to the {@code Keycloak} session.
|
||||
* @param realm a reference to the realm.
|
||||
* @param user a reference to the user.
|
||||
* @param client a reference to the client that contains the applications.
|
||||
* @param offlineClients a {@link Set} containing the offline clients.
|
||||
* @return the constructed {@link ApplicationEntry} instance or {@code null} if the user can't access the applications
|
||||
* in the specified client.
|
||||
*/
|
||||
private ApplicationEntry toApplicationEntry(final KeycloakSession session, final RealmModel realm, final UserModel user,
|
||||
final ClientModel client, final Set<ClientModel> offlineClients) {
|
||||
|
||||
// Construct scope parameter with all optional scopes to see all potentially available roles
|
||||
Stream<ClientScopeModel> allClientScopes = Stream.concat(
|
||||
client.getClientScopes(true, true).values().stream(),
|
||||
client.getClientScopes(false, true).values().stream());
|
||||
allClientScopes = Stream.concat(allClientScopes, Stream.of(client)).distinct();
|
||||
|
||||
Set<RoleModel> availableRoles = TokenManager.getAccess(user, client, allClientScopes);
|
||||
|
||||
// Don't show applications, which user doesn't have access into (any available roles)
|
||||
// unless this is can be changed by approving/revoking consent
|
||||
if (! isAdminClient(client) && availableRoles.isEmpty() && ! client.isConsentRequired()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RoleModel> realmRolesAvailable = new LinkedList<>();
|
||||
MultivaluedHashMap<String, ClientRoleEntry> resourceRolesAvailable = new MultivaluedHashMap<>();
|
||||
processRoles(availableRoles, realmRolesAvailable, resourceRolesAvailable);
|
||||
|
||||
List<ClientScopeModel> orderedScopes = new LinkedList<>();
|
||||
if (client.isConsentRequired()) {
|
||||
UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
|
||||
|
||||
if (consent != null) {
|
||||
orderedScopes.addAll(consent.getGrantedClientScopes());
|
||||
}
|
||||
}
|
||||
List<String> clientScopesGranted = orderedScopes.stream()
|
||||
.sorted(OrderedModel.OrderedModelComparator.getInstance())
|
||||
.map(ClientScopeModel::getConsentScreenText)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> additionalGrants = new ArrayList<>();
|
||||
if (offlineClients.contains(client)) {
|
||||
additionalGrants.add("${offlineToken}");
|
||||
}
|
||||
return new ApplicationEntry(session, realmRolesAvailable, resourceRolesAvailable, client, clientScopesGranted, additionalGrants);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
package org.keycloak.forms.account.freemarker.model;
|
||||
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.credential.CredentialProvider;
|
||||
import org.keycloak.credential.OTPCredentialProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OTPPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -33,6 +31,7 @@ import org.keycloak.utils.TotpUtils;
|
|||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.utils.CredentialHelper.createUserStorageCredentialRepresentation;
|
||||
|
||||
|
@ -54,7 +53,8 @@ public class TotpBean {
|
|||
this.uriBuilder = uriBuilder;
|
||||
this.enabled = session.userCredentialManager().isConfiguredFor(realm, user, OTPCredentialModel.TYPE);
|
||||
if (enabled) {
|
||||
List<CredentialModel> otpCredentials = session.userCredentialManager().getStoredCredentialsByType(realm, user, OTPCredentialModel.TYPE);
|
||||
List<CredentialModel> otpCredentials = session.userCredentialManager()
|
||||
.getStoredCredentialsByTypeStream(realm, user, OTPCredentialModel.TYPE).collect(Collectors.toList());
|
||||
|
||||
if (otpCredentials.isEmpty()) {
|
||||
// Credential is configured on userStorage side. Create the "fake" credential similar like we do for the new account console
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.utils.TotpUtils;
|
|||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Used for UpdateTotp required action
|
||||
|
@ -49,7 +50,8 @@ public class TotpBean {
|
|||
this.uriBuilder = uriBuilder;
|
||||
this.enabled = session.userCredentialManager().isConfiguredFor(realm, user, OTPCredentialModel.TYPE);
|
||||
if (enabled) {
|
||||
otpCredentials = session.userCredentialManager().getStoredCredentialsByType(realm, user, OTPCredentialModel.TYPE);
|
||||
otpCredentials = session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, OTPCredentialModel.TYPE)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
otpCredentials = Collections.EMPTY_LIST;
|
||||
}
|
||||
|
|
|
@ -42,10 +42,8 @@ public class TotpLoginBean {
|
|||
private final List<OTPCredential> userOtpCredentials;
|
||||
|
||||
public TotpLoginBean(KeycloakSession session, RealmModel realm, UserModel user, String selectedCredentialId) {
|
||||
List<CredentialModel> userOtpCredentials = session.userCredentialManager()
|
||||
.getStoredCredentialsByType(realm, user, OTPCredentialModel.TYPE);
|
||||
|
||||
this.userOtpCredentials = userOtpCredentials.stream()
|
||||
this.userOtpCredentials = session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, OTPCredentialModel.TYPE)
|
||||
.map(OTPCredential::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ package org.keycloak.forms.login.freemarker.model;
|
|||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -30,13 +30,13 @@ public class WebAuthnAuthenticatorsBean {
|
|||
|
||||
public WebAuthnAuthenticatorsBean(KeycloakSession session, RealmModel realm, UserModel user, String credentialType) {
|
||||
// should consider multiple credentials in the future, but only single credential supported now.
|
||||
for (CredentialModel credential : session.userCredentialManager().getStoredCredentialsByType(realm, user, credentialType)) {
|
||||
WebAuthnCredentialModel webAuthnCredential = WebAuthnCredentialModel.createFromCredentialModel(credential);
|
||||
|
||||
String credentialId = Base64Url.encodeBase64ToBase64Url(webAuthnCredential.getWebAuthnCredentialData().getCredentialId());
|
||||
String label = (webAuthnCredential.getUserLabel()==null || webAuthnCredential.getUserLabel().isEmpty()) ? "label missing" : webAuthnCredential.getUserLabel();
|
||||
authenticators.add(new WebAuthnAuthenticatorBean(credentialId, label));
|
||||
}
|
||||
this.authenticators = session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, credentialType)
|
||||
.map(WebAuthnCredentialModel::createFromCredentialModel)
|
||||
.map(webAuthnCredential -> {
|
||||
String credentialId = Base64Url.encodeBase64ToBase64Url(webAuthnCredential.getWebAuthnCredentialData().getCredentialId());
|
||||
String label = (webAuthnCredential.getUserLabel()==null || webAuthnCredential.getUserLabel().isEmpty()) ? "label missing" : webAuthnCredential.getUserLabel();
|
||||
return new WebAuthnAuthenticatorBean(credentialId, label);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<WebAuthnAuthenticatorBean> getAuthenticators() {
|
||||
|
|
|
@ -170,14 +170,9 @@ public class AccountCredentialResource {
|
|||
.collect(Collectors.toList());
|
||||
Set<String> enabledCredentialTypes = getEnabledCredentialTypes(credentialProviders);
|
||||
|
||||
List<CredentialModel> models = includeUserCredentials ? session.userCredentialManager().getStoredCredentials(realm, user) : null;
|
||||
|
||||
Stream<CredentialModel> modelsStream = includeUserCredentials ? session.userCredentialManager().getStoredCredentialsStream(realm, user) : Stream.empty();
|
||||
// Don't return secrets from REST endpoint
|
||||
if (models != null) {
|
||||
for (CredentialModel credential : models) {
|
||||
credential.setSecretData(null);
|
||||
}
|
||||
}
|
||||
List<CredentialModel> models = modelsStream.peek(model -> model.setSecretData(null)).collect(Collectors.toList());
|
||||
|
||||
Function<CredentialProvider, CredentialContainer> toCredentialContainer = (credentialProvider) -> {
|
||||
CredentialTypeMetadataContext ctx = CredentialTypeMetadataContext.builder()
|
||||
|
|
|
@ -19,7 +19,13 @@ package org.keycloak.services.resources.account;
|
|||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
|
|
@ -600,7 +600,7 @@ public class UserResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Stream<CredentialRepresentation> credentials(){
|
||||
auth.users().requireManage(user);
|
||||
return session.userCredentialManager().getStoredCredentials(realm, user).stream()
|
||||
return session.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.peek(model -> model.setSecretData(null))
|
||||
.map(ModelToRepresentation::toRepresentation);
|
||||
}
|
||||
|
@ -616,11 +616,11 @@ public class UserResource {
|
|||
@Path("configured-user-storage-credential-types")
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<String> getConfiguredUserStorageCredentialTypes() {
|
||||
public Stream<String> getConfiguredUserStorageCredentialTypes() {
|
||||
// This has "requireManage" due the compatibility with "credentials()" endpoint. Strictly said, it is reading endpoint, not writing,
|
||||
// so may be revisited if to rather use "requireView" here in the future.
|
||||
auth.users().requireManage(user);
|
||||
return session.userCredentialManager().getConfiguredUserStorageCredentialTypes(realm, user);
|
||||
return session.userCredentialManager().getConfiguredUserStorageCredentialTypesStream(realm, user);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
|
||||
package org.keycloak.testsuite.federation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.Time;
|
||||
|
@ -57,7 +60,7 @@ import org.keycloak.storage.user.UserRegistrationProvider;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class BackwardsCompatibilityUserStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider,
|
||||
CredentialInputUpdater, CredentialInputValidator, UserQueryProvider.Streams {
|
||||
CredentialInputUpdater, CredentialInputValidator, UserQueryProvider {
|
||||
|
||||
private static final Logger log = Logger.getLogger(BackwardsCompatibilityUserStorage.class);
|
||||
|
||||
|
@ -82,7 +85,7 @@ public class BackwardsCompatibilityUserStorage implements UserLookupProvider, Us
|
|||
}
|
||||
|
||||
private UserModel createUser(RealmModel realm, String username) {
|
||||
return new AbstractUserAdapterFederatedStorage.Streams(session, realm, model) {
|
||||
return new AbstractUserAdapterFederatedStorage(session, realm, model) {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
|
@ -316,57 +319,58 @@ public class BackwardsCompatibilityUserStorage implements UserLookupProvider, Us
|
|||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> getUsersStream(RealmModel realm) {
|
||||
return getUsersStream(realm, -1, -1);
|
||||
public List<UserModel> getUsers(RealmModel realm) {
|
||||
return getUsers(realm, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> getUsersStream(RealmModel realm, int firstResult, int maxResults) {
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
|
||||
return users.values()
|
||||
.stream()
|
||||
.skip(firstResult).limit(maxResults)
|
||||
.map(myUser -> createUser(realm, myUser.username));
|
||||
.map(myUser -> createUser(realm, myUser.username))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(String search, RealmModel realm) {
|
||||
return searchForUserStream(search, realm, -1, -1);
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||
return searchForUser(search, realm, -1, -1);
|
||||
}
|
||||
|
||||
@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) {
|
||||
UserModel user = getUserByUsername(search, realm);
|
||||
return user == null ? Stream.empty() : Stream.of(user);
|
||||
return user == null ? Collections.emptyList() : Arrays.asList(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm) {
|
||||
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) {
|
||||
// Assume that this is not supported
|
||||
return Stream.empty();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserStream(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
|
||||
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
|
||||
// Assume that this is not supported
|
||||
return Stream.empty();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@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) {
|
||||
// Assume that this is not supported
|
||||
return Stream.empty();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group) {
|
||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
|
||||
// Assume that this is not supported
|
||||
return Stream.empty();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UserModel> searchForUserByUserAttributeStream(String attrName, String attrValue, RealmModel realm) {
|
||||
public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
|
||||
// Assume that this is not supported
|
||||
return Stream.empty();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,10 +33,8 @@ import org.keycloak.storage.user.ImportedUserValidation;
|
|||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
|
@ -44,7 +42,7 @@ import java.util.stream.Stream;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FailableHardcodedStorageProvider implements UserStorageProvider, UserLookupProvider, UserQueryProvider.Streams,
|
||||
ImportedUserValidation, CredentialInputUpdater, CredentialInputValidator {
|
||||
ImportedUserValidation, CredentialInputUpdater.Streams, CredentialInputValidator {
|
||||
|
||||
public static String username = "billb";
|
||||
public static String password = "password";
|
||||
|
@ -97,9 +95,9 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
checkForceFail();
|
||||
return Collections.EMPTY_SET;
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.credential.CredentialInputValidator;
|
|||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
@ -32,10 +31,10 @@ import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
|
|||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -83,12 +82,9 @@ public class PassThroughFederatedUserStorageProvider implements
|
|||
if (INITIAL_PASSWORD.equals(input.getChallengeResponse())) {
|
||||
return true;
|
||||
}
|
||||
Optional<CredentialModel> existing = session.userFederatedStorage()
|
||||
.getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD")
|
||||
.findFirst();
|
||||
if (existing.isPresent())
|
||||
return existing.get().getSecretData().equals("{\"value\":\"" + input.getChallengeResponse() + "\"}");
|
||||
return false;
|
||||
return session.userFederatedStorage().getStoredCredentialsByTypeStream(realm, user.getId(), "CLEAR_TEXT_PASSWORD")
|
||||
.map(credentialModel -> credentialModel.getSecretData())
|
||||
.anyMatch(Predicate.isEqual("{\"value\":\"" + input.getChallengeResponse() + "\"}"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ import org.keycloak.storage.user.UserLookupProvider;
|
|||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -54,7 +53,7 @@ import static org.keycloak.storage.UserStorageProviderModel.IMPORT_ENABLED;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater,
|
||||
public class UserMapStorage implements UserLookupProvider, UserStorageProvider, UserRegistrationProvider, CredentialInputUpdater.Streams,
|
||||
CredentialInputValidator, UserGroupMembershipFederatedStorage.Streams, UserQueryProvider.Streams, ImportedUserValidation {
|
||||
|
||||
private static final Logger log = Logger.getLogger(UserMapStorage.class);
|
||||
|
@ -174,8 +173,8 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
return Collections.EMPTY_SET;
|
||||
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by st on 26.01.17.
|
||||
|
@ -55,7 +56,9 @@ public class RunHelpers {
|
|||
return (FetchOnServer) session -> {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = session.users().getUserByUsername(username, realm);
|
||||
List<CredentialModel> storedCredentialsByType = session.userCredentialManager().getStoredCredentialsByType(realm, user, CredentialRepresentation.PASSWORD);
|
||||
List<CredentialModel> storedCredentialsByType = session.userCredentialManager()
|
||||
.getStoredCredentialsByTypeStream(realm, user, CredentialRepresentation.PASSWORD)
|
||||
.collect(Collectors.toList());
|
||||
System.out.println(storedCredentialsByType.size());
|
||||
return storedCredentialsByType.get(0);
|
||||
};
|
||||
|
|
|
@ -79,6 +79,7 @@ import java.util.Arrays;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assume;
|
||||
|
@ -482,7 +483,8 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = session.users().getUserById(uId, realm);
|
||||
assertThat(user, Matchers.notNullValue());
|
||||
List<CredentialModel> storedCredentials = session.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> storedCredentials = session.userCredentialManager()
|
||||
.getStoredCredentialsStream(realm, user).collect(Collectors.toList());
|
||||
assertThat(storedCredentials, Matchers.hasSize(expectedNumberOfStoredCredentials));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
|
@ -630,9 +632,9 @@ public class KcinitTest extends AbstractTestRealmKeycloakTest {
|
|||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
UserModel user = session.users().getUserByUsername("wburke", realm);
|
||||
for (CredentialModel c: session.userCredentialManager().getStoredCredentialsByType(realm, user, OTPCredentialModel.TYPE)){
|
||||
session.userCredentialManager().removeStoredCredential(realm, user, c.getId());
|
||||
}
|
||||
session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, OTPCredentialModel.TYPE)
|
||||
.collect(Collectors.toList())
|
||||
.forEach(model -> session.userCredentialManager().removeStoredCredential(realm, user, model.getId()));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -892,7 +892,10 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
|
|||
|
||||
UserCredentialModel cred = UserCredentialModel.password("Candycand1", true);
|
||||
session.userCredentialManager().updateCredential(appRealm, user, cred);
|
||||
CredentialModel userCredentialValueModel = session.userCredentialManager().getStoredCredentialsByType(appRealm, user, PasswordCredentialModel.TYPE).get(0);
|
||||
CredentialModel userCredentialValueModel = session.userCredentialManager()
|
||||
.getStoredCredentialsByTypeStream(appRealm, user, PasswordCredentialModel.TYPE)
|
||||
.findFirst().orElse(null);
|
||||
Assert.assertNotNull(userCredentialValueModel);
|
||||
Assert.assertEquals(PasswordCredentialModel.TYPE, userCredentialValueModel.getType());
|
||||
Assert.assertTrue(session.userCredentialManager().isValid(appRealm, user, cred));
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.net.URISyntaxException;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
|
@ -292,8 +293,8 @@ public class BackwardsCompatibilityUserStorageTest extends AbstractAuthTest {
|
|||
testingClient.server().run(session -> {
|
||||
RealmModel realm1 = session.realms().getRealmByName("test");
|
||||
UserModel user1 = session.users().getUserByUsername("otp1", realm1);
|
||||
List<CredentialModel> keycloakDBCredentials = session.userCredentialManager().getStoredCredentials(realm1, user1);
|
||||
Assert.assertTrue(keycloakDBCredentials.isEmpty());
|
||||
Assert.assertEquals(0, session.userCredentialManager()
|
||||
.getStoredCredentialsStream(realm1, user1).count());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Calendar.DAY_OF_WEEK;
|
||||
import static java.util.Calendar.HOUR_OF_DAY;
|
||||
|
@ -879,8 +880,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
Assert.assertFalse(StorageId.isLocalStorage(user));
|
||||
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
org.keycloak.testsuite.Assert.assertEquals(0, list.size());
|
||||
Stream<CredentialModel> credentials = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user);
|
||||
org.keycloak.testsuite.Assert.assertEquals(0, credentials.count());
|
||||
|
||||
// Create password
|
||||
CredentialModel passwordCred = PasswordCredentialModel.createFromValues("my-algorithm", "theSalt".getBytes(), 22, "ABC");
|
||||
|
@ -902,7 +903,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
|
||||
// Assert priorities: password, otp1, otp2
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, passwordId.get(), otp1Id.get(), otp2Id.get());
|
||||
|
||||
// Assert can't move password when newPreviousCredential not found
|
||||
|
@ -920,7 +922,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
|
||||
// Assert priorities: password, otp2, otp1
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, passwordId.get(), otp2Id.get(), otp1Id.get());
|
||||
|
||||
// Move otp2 to the top
|
||||
|
@ -932,7 +935,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
|
||||
// Assert priorities: otp2, password, otp1
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp2Id.get(), passwordId.get(), otp1Id.get());
|
||||
|
||||
// Move password down
|
||||
|
@ -944,7 +948,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
|
||||
// Assert priorities: otp2, otp1, password
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp2Id.get(), otp1Id.get(), passwordId.get());
|
||||
|
||||
// Remove otp2 down two positions
|
||||
|
@ -956,7 +961,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
|
||||
// Assert priorities: otp2, otp1, password
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp1Id.get(), passwordId.get(), otp2Id.get());
|
||||
|
||||
// Remove password
|
||||
|
@ -968,7 +974,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("thor", realm);
|
||||
|
||||
// Assert priorities: otp2, password
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp1Id.get(), otp2Id.get());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -242,7 +242,8 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest {
|
|||
return testingClient.server("test").fetch(session -> {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserModel user = session.users().getUserByUsername(username, realm);
|
||||
return session.userCredentialManager().getStoredCredentialsByType(realm, user, CredentialRepresentation.PASSWORD).get(0);
|
||||
return session.userCredentialManager().getStoredCredentialsByTypeStream(realm, user, CredentialRepresentation.PASSWORD)
|
||||
.findFirst().orElse(null);
|
||||
}, CredentialModel.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.keycloak.testsuite.arquillian.annotation.ModelTest;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||
|
||||
|
@ -41,7 +42,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
RealmModel realm = currentSession.realms().getRealmByName("test");
|
||||
|
||||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
Assert.assertEquals(1, list.size());
|
||||
passwordId.set(list.get(0).getId());
|
||||
|
||||
|
@ -60,7 +62,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
|
||||
// Assert priorities: password, otp1, otp2
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, passwordId.get(), otp1Id.get(), otp2Id.get());
|
||||
|
||||
// Assert can't move password when newPreviousCredential not found
|
||||
|
@ -78,7 +81,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
|
||||
// Assert priorities: password, otp2, otp1
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, passwordId.get(), otp2Id.get(), otp1Id.get());
|
||||
|
||||
// Move otp2 to the top
|
||||
|
@ -90,7 +94,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
|
||||
// Assert priorities: otp2, password, otp1
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp2Id.get(), passwordId.get(), otp1Id.get());
|
||||
|
||||
// Move password down
|
||||
|
@ -102,7 +107,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
|
||||
// Assert priorities: otp2, otp1, password
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp2Id.get(), otp1Id.get(), passwordId.get());
|
||||
|
||||
// Remove otp2 down two positions
|
||||
|
@ -114,7 +120,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
|
||||
// Assert priorities: otp2, otp1, password
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp1Id.get(), passwordId.get(), otp2Id.get());
|
||||
|
||||
// Remove password
|
||||
|
@ -126,7 +133,8 @@ public class CredentialModelTest extends AbstractTestRealmKeycloakTest {
|
|||
UserModel user = currentSession.users().getUserByUsername("test-user@localhost", realm);
|
||||
|
||||
// Assert priorities: otp2, password
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentials(realm, user);
|
||||
List<CredentialModel> list = currentSession.userCredentialManager().getStoredCredentialsStream(realm, user)
|
||||
.collect(Collectors.toList());
|
||||
assertOrder(list, otp1Id.get(), otp2Id.get());
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue