diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index 87c6487..5f8094b 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -21,10 +21,13 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
+import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.credential.CredentialInput;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@@ -41,15 +44,19 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
+import org.keycloak.models.cache.CachedUserModel;
+import org.keycloak.models.entities.CredentialEntity;
import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.entities.UserConsentEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.utils.CredentialValidation;
+import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -59,7 +66,7 @@ import java.util.regex.Pattern;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class MongoUserProvider implements UserProvider {
+public class MongoUserProvider implements UserProvider, UserCredentialStore {
private final MongoStoreInvocationContext invocationContext;
private final KeycloakSession session;
@@ -627,4 +634,185 @@ public class MongoUserProvider implements UserProvider {
}
+ @Override
+ public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ CredentialEntity credentialEntity = getCredentialEntity(cred, mongoUser);
+ if (credentialEntity == null) return;
+ // old store may not have id set
+ if (credentialEntity.getId() == null) credentialEntity.setId(KeycloakModelUtils.generateId());
+ setValues(cred, credentialEntity);
+ getMongoStore().updateEntity(mongoUser, invocationContext);
+
+ }
+
+ public CredentialEntity getCredentialEntity(CredentialModel cred, MongoUserEntity mongoUser) {
+ CredentialEntity credentialEntity = null;
+ // old store may not have id set
+ for (CredentialEntity entity : mongoUser.getCredentials()) {
+ if (cred.getId() != null && cred.getId().equals(entity.getId())) {
+ credentialEntity = entity;
+ break;
+ } else if (cred.getType().equals(entity.getType())) {
+ credentialEntity = entity;
+ break;
+ }
+ }
+ return credentialEntity;
+ }
+
+ public MongoUserEntity getMongoUserEntity(UserModel user) {
+ UserAdapter adapter = null;
+ if (user instanceof CachedUserModel) {
+ adapter = (UserAdapter)((CachedUserModel)user).getDelegateForUpdate();
+ } else if (user instanceof UserAdapter ){
+ adapter = (UserAdapter)user;
+ } else {
+ return getMongoStore().loadEntity(MongoUserEntity.class, user.getId(), invocationContext);
+
+ }
+ return adapter.getMongoEntity();
+ }
+
+ @Override
+ public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ CredentialEntity credentialEntity = new CredentialEntity();
+ credentialEntity.setId(KeycloakModelUtils.generateId());
+ setValues(cred, credentialEntity);
+ cred.setId(credentialEntity.getId());
+ mongoUser.getCredentials().add(credentialEntity);
+ getMongoStore().updateEntity(mongoUser, invocationContext);
+ cred.setId(credentialEntity.getId());
+ return cred;
+ }
+
+ public void setValues(CredentialModel cred, CredentialEntity credentialEntity) {
+ credentialEntity.setType(cred.getType());
+ credentialEntity.setDevice(cred.getDevice());
+ credentialEntity.setValue(cred.getValue());
+ credentialEntity.setSalt(cred.getSalt());
+ credentialEntity.setDevice(cred.getDevice());
+ credentialEntity.setHashIterations(cred.getHashIterations());
+ credentialEntity.setCounter(cred.getCounter());
+ credentialEntity.setAlgorithm(cred.getAlgorithm());
+ credentialEntity.setDigits(cred.getDigits());
+ credentialEntity.setPeriod(cred.getPeriod());
+ if (cred.getConfig() == null) {
+ credentialEntity.setConfig(null);
+ }
+ else {
+ if (credentialEntity.getConfig() == null) credentialEntity.setConfig(new MultivaluedHashMap<>());
+ credentialEntity.getConfig().clear();
+ credentialEntity.getConfig().putAll(cred.getConfig());
+ }
+ }
+
+ @Override
+ public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ Iterator<CredentialEntity> it = mongoUser.getCredentials().iterator();
+ while (it.hasNext()) {
+ CredentialEntity entity = it.next();
+ if (id.equals(entity.getId())) {
+ it.remove();
+ getMongoStore().updateEntity(mongoUser, invocationContext);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ for (CredentialEntity credEntity : mongoUser.getCredentials()) {
+ if(id.equals(credEntity.getId())) {
+ if (credEntity.getId() == null) {
+ credEntity.setId(KeycloakModelUtils.generateId());
+ getMongoStore().updateEntity(mongoUser, invocationContext);
+ }
+ return toModel(credEntity);
+ }
+
+ }
+ return null;
+ }
+
+ public CredentialModel toModel(CredentialEntity credEntity) {
+ CredentialModel credModel = new CredentialModel();
+ credModel.setId(credEntity.getId());
+ credModel.setType(credEntity.getType());
+ credModel.setDevice(credEntity.getDevice());
+ credModel.setCreatedDate(credEntity.getCreatedDate());
+ credModel.setValue(credEntity.getValue());
+ credModel.setSalt(credEntity.getSalt());
+ credModel.setHashIterations(credEntity.getHashIterations());
+ credModel.setAlgorithm(credEntity.getAlgorithm());
+ credModel.setCounter(credEntity.getCounter());
+ credModel.setPeriod(credEntity.getPeriod());
+ credModel.setDigits(credEntity.getDigits());
+ if (credEntity.getConfig() != null) {
+ credModel.setConfig(new MultivaluedHashMap<>());
+ credModel.getConfig().putAll(credEntity.getConfig());
+ }
+ return credModel;
+ }
+
+ @Override
+ public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
+ List<CredentialModel> list = new LinkedList<>();
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ boolean update = false;
+ for (CredentialEntity credEntity : mongoUser.getCredentials()) {
+ if (credEntity.getId() == null) {
+ credEntity.setId(KeycloakModelUtils.generateId());
+ update = true;
+ }
+ CredentialModel credModel = toModel(credEntity);
+ list.add(credModel);
+
+ }
+ if (update) getMongoStore().updateEntity(mongoUser, invocationContext);
+ return list;
+
+ }
+
+ @Override
+ public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
+ List<CredentialModel> list = new LinkedList<>();
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ boolean update = false;
+ for (CredentialEntity credEntity : mongoUser.getCredentials()) {
+ if (credEntity.getId() == null) {
+ credEntity.setId(KeycloakModelUtils.generateId());
+ update = true;
+ }
+ if (credEntity.getType().equals(type)) {
+ CredentialModel credModel = toModel(credEntity);
+ list.add(credModel);
+ }
+ }
+ if (update) getMongoStore().updateEntity(mongoUser, invocationContext);
+ return list;
+ }
+
+ @Override
+ public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
+ MongoUserEntity mongoUser = getMongoUserEntity(user);
+ boolean update = false;
+ CredentialModel credModel = null;
+ for (CredentialEntity credEntity : mongoUser.getCredentials()) {
+ if (credEntity.getId() == null) {
+ credEntity.setId(KeycloakModelUtils.generateId());
+ update = true;
+ }
+ if (credEntity.getType().equals(type) && name.equals(credEntity.getDevice())) {
+ credModel = toModel(credEntity);
+ break;
+ }
+ }
+ if (update) getMongoStore().updateEntity(mongoUser, invocationContext);
+ return credModel;
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/entities/CredentialEntity.java b/server-spi/src/main/java/org/keycloak/models/entities/CredentialEntity.java
index ef1932e..aa4752c 100755
--- a/server-spi/src/main/java/org/keycloak/models/entities/CredentialEntity.java
+++ b/server-spi/src/main/java/org/keycloak/models/entities/CredentialEntity.java
@@ -17,6 +17,11 @@
package org.keycloak.models.entities;
+import org.keycloak.common.util.MultivaluedHashMap;
+
+import java.util.List;
+import java.util.Map;
+
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@@ -33,6 +38,7 @@ public class CredentialEntity extends AbstractIdentifiableEntity {
protected String algorithm;
protected int digits;
protected int period;
+ protected Map<String, List<String>> config = new MultivaluedHashMap<>();
public String getType() {
@@ -122,4 +128,12 @@ public class CredentialEntity extends AbstractIdentifiableEntity {
public void setPeriod(int period) {
this.period = period;
}
+
+ public Map<String, List<String>> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, List<String>> config) {
+ this.config = config;
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPMultipleAttributesTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPMultipleAttributesTest.java
index fb23b5d..ec95eaf 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPMultipleAttributesTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPMultipleAttributesTest.java
@@ -20,6 +20,7 @@ package org.keycloak.testsuite.federation.ldap.base;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -149,7 +150,9 @@ public class LDAPMultipleAttributesTest {
// Actually there are 2 postalCodes
List<String> postalCodes = user.getAttribute("postal_code");
assertPostalCodes(postalCodes, "88441", "77332");
-
+ List<String> tmp = new LinkedList<>();
+ tmp.addAll(postalCodes);
+ postalCodes = tmp;
postalCodes.remove("77332");
user.setAttribute("postal_code", postalCodes);
@@ -163,7 +166,9 @@ public class LDAPMultipleAttributesTest {
UserModel user = session.users().getUserByUsername("bwilson", appRealm);
List<String> postalCodes = user.getAttribute("postal_code");
assertPostalCodes(postalCodes, "88441");
-
+ List<String> tmp = new LinkedList<>();
+ tmp.addAll(postalCodes);
+ postalCodes = tmp;
postalCodes.add("77332");
user.setAttribute("postal_code", postalCodes);
} finally {