From f0d02f24b14952feac63a153980e629e300f90df Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Fri, 20 Jun 2014 17:06:56 -0400 Subject: [PATCH] user cache --- .../models/cache/CacheKeycloakSession.java | 2 + .../keycloak/models/cache/ClientAdapter.java | 3 +- .../cache/DefaultCacheKeycloakSession.java | 59 +++- .../keycloak/models/cache/KeycloakCache.java | 15 + .../models/cache/NoCacheKeycloakSession.java | 5 + .../keycloak/models/cache/SimpleCache.java | 127 +++++++- .../keycloak/models/cache/UserAdapter.java | 283 ++++++++++++++++++ .../models/cache/entities/CachedUser.java | 24 +- .../adapters/MongoKeycloakSession.java | 8 +- .../mongo/keycloak/adapters/RealmAdapter.java | 11 +- .../resources/META-INF/keycloak-server.json | 2 +- .../testsuite/account/AccountTest.java | 9 +- 12 files changed, 522 insertions(+), 26 deletions(-) create mode 100755 model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheKeycloakSession.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheKeycloakSession.java index 7ad72b882b..095538738f 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheKeycloakSession.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/CacheKeycloakSession.java @@ -16,4 +16,6 @@ public interface CacheKeycloakSession extends KeycloakSession { void registerRoleInvalidation(String id); void registerOAuthClientInvalidation(String id); + + void registerUserInvalidation(String id); } diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java index 82e0332626..660c738b35 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java @@ -174,8 +174,9 @@ public abstract class ClientAdapter implements ClientModel { public boolean hasScope(RoleModel role) { if (updatedClient != null) return updatedClient.hasScope(role); + if (cachedClient.getScope().contains(role.getId())) return true; + Set roles = getScopeMappings(); - if (roles.contains(role)) return true; for (RoleModel mapping : roles) { if (mapping.hasRole(role)) return true; diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheKeycloakSession.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheKeycloakSession.java index b4f6376b04..1a69c6c405 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheKeycloakSession.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheKeycloakSession.java @@ -17,6 +17,7 @@ import org.keycloak.models.cache.entities.CachedOAuthClient; import org.keycloak.models.cache.entities.CachedRealm; import org.keycloak.models.cache.entities.CachedRealmRole; import org.keycloak.models.cache.entities.CachedRole; +import org.keycloak.models.cache.entities.CachedUser; import org.keycloak.provider.ProviderSession; import java.util.HashMap; @@ -41,10 +42,12 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession { protected Set appInvalidations = new HashSet(); protected Set roleInvalidations = new HashSet(); protected Set clientInvalidations = new HashSet(); + protected Set userInvalidations = new HashSet(); protected Map managedRealms = new HashMap(); protected Map managedApplications = new HashMap(); protected Map managedClients = new HashMap(); protected Map managedRoles = new HashMap(); + protected Map managedUsers = new HashMap(); protected boolean clearAll; @@ -88,6 +91,11 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession { clientInvalidations.add(id); } + @Override + public void registerUserInvalidation(String id) { + userInvalidations.add(id); + } + protected void runInvalidations() { for (String id : realmInvalidations) { cache.invalidateCachedRealmById(id); @@ -101,6 +109,9 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession { for (String id : clientInvalidations) { cache.invalidateCachedOAuthClientById(id); } + for (String id : userInvalidations) { + cache.invalidateCachedUserById(id); + } } @@ -210,17 +221,59 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession { @Override public UserModel getUserById(String id, RealmModel realm) { - return getDelegate().getUserById(id, realm); + CachedUser cached = cache.getCachedUser(id); + if (cached == null) { + UserModel model = getDelegate().getUserById(id, realm); + if (model == null) return null; + if (userInvalidations.contains(id)) return model; + cached = new CachedUser(realm, model); + cache.addCachedUser(cached); + } else if (userInvalidations.contains(id)) { + return getDelegate().getUserById(id, realm); + } else if (managedUsers.containsKey(id)) { + return managedUsers.get(id); + } + UserAdapter adapter = new UserAdapter(cached, cache, this, realm); + managedUsers.put(id, adapter); + return adapter; } @Override public UserModel getUserByUsername(String username, RealmModel realm) { - return getDelegate().getUserByUsername(username, realm); + CachedUser cached = cache.getCachedUserByUsername(username, realm); + if (cached == null) { + UserModel model = getDelegate().getUserByUsername(username, realm); + if (model == null) return null; + if (userInvalidations.contains(model.getId())) return model; + cached = new CachedUser(realm, model); + cache.addCachedUser(cached); + } else if (userInvalidations.contains(cached.getId())) { + return getDelegate().getUserById(cached.getId(), realm); + } else if (managedUsers.containsKey(cached.getId())) { + return managedUsers.get(cached.getId()); + } + UserAdapter adapter = new UserAdapter(cached, cache, this, realm); + managedUsers.put(cached.getId(), adapter); + return adapter; } @Override public UserModel getUserByEmail(String email, RealmModel realm) { - return getDelegate().getUserByEmail(email, realm); + CachedUser cached = cache.getCachedUserByEmail(email, realm); + if (cached == null) { + UserModel model = getDelegate().getUserByEmail(email, realm); + if (model == null) return null; + if (userInvalidations.contains(model.getId())) return model; + cached = new CachedUser(realm, model); + cache.addCachedUser(cached); + } else if (userInvalidations.contains(cached.getId())) { + return getDelegate().getUserByEmail(email, realm); + } else if (managedUsers.containsKey(cached.getId())) { + return managedUsers.get(cached.getId()); + } + UserAdapter adapter = new UserAdapter(cached, cache, this, realm); + managedUsers.put(cached.getId(), adapter); + return adapter; } @Override diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/KeycloakCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/KeycloakCache.java index 20ad44b1fc..dca8fa17af 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/KeycloakCache.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/KeycloakCache.java @@ -1,9 +1,11 @@ package org.keycloak.models.cache; +import org.keycloak.models.RealmModel; import org.keycloak.models.cache.entities.CachedApplication; import org.keycloak.models.cache.entities.CachedOAuthClient; import org.keycloak.models.cache.entities.CachedRealm; import org.keycloak.models.cache.entities.CachedRole; +import org.keycloak.models.cache.entities.CachedUser; /** * @author Bill Burke @@ -48,4 +50,17 @@ public interface KeycloakCache { void invalidateRoleById(String id); + + CachedUser getCachedUser(String id); + + void invalidateCachedUser(CachedUser user); + + void addCachedUser(CachedUser user); + + CachedUser getCachedUserByUsername(String name, RealmModel realm); + CachedUser getCachedUserByEmail(String name, RealmModel realm); + + void invalidedCachedUserById(String id); + + void invalidateCachedUserById(String id); } diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheKeycloakSession.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheKeycloakSession.java index afc35e30d3..12410e1a1c 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheKeycloakSession.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheKeycloakSession.java @@ -278,4 +278,9 @@ public class NoCacheKeycloakSession implements CacheKeycloakSession { public void removeUserSessions(RealmModel realm) { getDelegate().removeUserSessions(realm); } + + @Override + public void registerUserInvalidation(String id) { + //To change body of implemented methods use File | Settings | File Templates. + } } diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java index 69bd613814..f40723b48a 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/SimpleCache.java @@ -1,18 +1,15 @@ package org.keycloak.models.cache; -import org.keycloak.Config; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.RealmModel; import org.keycloak.models.cache.entities.CachedApplication; import org.keycloak.models.cache.entities.CachedOAuthClient; import org.keycloak.models.cache.entities.CachedRealm; import org.keycloak.models.cache.entities.CachedRole; -import org.keycloak.provider.ProviderSession; -import org.keycloak.provider.ProviderSessionFactory; +import org.keycloak.models.cache.entities.CachedUser; -import java.util.List; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -27,6 +24,121 @@ public class SimpleCache implements KeycloakCache { protected ConcurrentHashMap clientCache = new ConcurrentHashMap(); protected ConcurrentHashMap roleCache = new ConcurrentHashMap(); + protected int maxUserCacheSize = 10000; + protected boolean userCacheEnabled = true; + + protected Map usersById = Collections.synchronizedMap(new LRUCache()); + protected Map usersByUsername = new ConcurrentHashMap(); + protected Map usersByEmail = new ConcurrentHashMap(); + + protected class LRUCache extends LinkedHashMap { + public LRUCache() { + super(1000, 1.1F, true); + } + + @Override + public CachedUser put(String key, CachedUser value) { + usersByUsername.put(value.getUsernameKey(), value); + if (value.getEmail() != null) { + usersByEmail.put(value.getEmailKey(), value); + } + return super.put(key, value); + } + + @Override + public CachedUser remove(Object key) { + CachedUser user = super.remove(key); + if (user == null) return null; + removeUser(user); + return user; + } + + @Override + public void clear() { + super.clear(); + usersByUsername.clear(); + usersByEmail.clear(); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + boolean evict = size() > maxUserCacheSize; + if (evict) { + removeUser(eldest.getValue()); + } + return evict; + } + + private void removeUser(CachedUser value) { + usersByUsername.remove(value.getUsernameKey()); + if (value.getEmail() != null) usersByEmail.remove(value.getEmailKey()); + } + } + + public int getMaxUserCacheSize() { + return maxUserCacheSize; + } + + public void setMaxUserCacheSize(int maxUserCacheSize) { + this.maxUserCacheSize = maxUserCacheSize; + } + + public boolean isUserCacheEnabled() { + return userCacheEnabled; + } + + public void setUserCacheEnabled(boolean userCacheEnabled) { + this.userCacheEnabled = userCacheEnabled; + } + + @Override + public CachedUser getCachedUser(String id) { + if (!userCacheEnabled) return null; + return usersById.get(id); + } + + @Override + public void invalidateCachedUser(CachedUser user) { + if (!userCacheEnabled) return; + usersById.remove(user.getId()); + } + + @Override + public void invalidateCachedUserById(String id) { + if (!userCacheEnabled) return; + usersById.remove(id); + } + + @Override + public void addCachedUser(CachedUser user) { + if (!userCacheEnabled) return; + usersById.put(user.getId(), user); + } + + @Override + public CachedUser getCachedUserByUsername(String name, RealmModel realm) { + if (!userCacheEnabled) return null; + CachedUser user = usersByUsername.get(realm.getId() + "." +name); + if (user == null) return null; + usersById.get(user.getId()); // refresh cache entry age + return user; + } + + @Override + public CachedUser getCachedUserByEmail(String name, RealmModel realm) { + if (!userCacheEnabled) return null; + CachedUser user = usersByEmail.get(realm.getId() + "." +name); + if (user == null) return null; + usersById.get(user.getId()); // refresh cache entry age + return user; + } + + @Override + public void invalidedCachedUserById(String id) { + if (!userCacheEnabled) return; + usersById.remove(id); + } + @Override public void clear() { realmCache.clear(); @@ -34,6 +146,7 @@ public class SimpleCache implements KeycloakCache { applicationCache.clear(); clientCache.clear(); roleCache.clear(); + usersById.clear(); } @Override diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java new file mode 100755 index 0000000000..a31f3e042d --- /dev/null +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java @@ -0,0 +1,283 @@ +package org.keycloak.models.cache; + +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.AuthenticationLinkModel; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleContainerModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.cache.entities.CachedUser; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class UserAdapter implements UserModel { + protected UserModel updated; + protected CachedUser cached; + protected KeycloakCache cache; + protected CacheKeycloakSession cacheSession; + protected RealmModel realm; + + public UserAdapter(CachedUser cached, KeycloakCache cache, CacheKeycloakSession session, RealmModel realm) { + this.cached = cached; + this.cache = cache; + this.cacheSession = session; + this.realm = realm; + } + + protected void getDelegateForUpdate() { + if (updated == null) { + cacheSession.registerUserInvalidation(getId()); + updated = cacheSession.getDelegate().getUserById(getId(), realm); + if (updated == null) throw new IllegalStateException("Not found in database"); + } + } + @Override + public String getId() { + if (updated != null) return updated.getId(); + return cached.getId(); + } + + @Override + public String getLoginName() { + if (updated != null) return updated.getLoginName(); + return cached.getLoginName(); + } + + @Override + public void setLoginName(String loginName) { + getDelegateForUpdate(); + updated.setLoginName(loginName); + } + + @Override + public boolean isEnabled() { + if (updated != null) return updated.isEnabled(); + return cached.isEnabled(); + } + + @Override + public boolean isTotp() { + if (updated != null) return updated.isTotp(); + return cached.isTotp(); + } + + @Override + public void setEnabled(boolean enabled) { + getDelegateForUpdate(); + updated.setEnabled(enabled); + } + + @Override + public void setAttribute(String name, String value) { + getDelegateForUpdate(); + updated.setAttribute(name, value); + } + + @Override + public void removeAttribute(String name) { + getDelegateForUpdate(); + updated.removeAttribute(name); + } + + @Override + public String getAttribute(String name) { + if (updated != null) return updated.getAttribute(name); + return cached.getAttributes().get(name); + } + + @Override + public Map getAttributes() { + if (updated != null) return updated.getAttributes(); + return cached.getAttributes(); + } + + @Override + public Set getRequiredActions() { + if (updated != null) return updated.getRequiredActions(); + return cached.getRequiredActions(); + } + + @Override + public void addRequiredAction(RequiredAction action) { + getDelegateForUpdate(); + updated.addRequiredAction(action); + } + + @Override + public void removeRequiredAction(RequiredAction action) { + getDelegateForUpdate(); + updated.removeRequiredAction(action); + } + + @Override + public String getFirstName() { + if (updated != null) return updated.getFirstName(); + return cached.getFirstName(); + } + + @Override + public void setFirstName(String firstName) { + getDelegateForUpdate(); + updated.setFirstName(firstName); + } + + @Override + public String getLastName() { + if (updated != null) return updated.getLastName(); + return cached.getLastName(); + } + + @Override + public void setLastName(String lastName) { + getDelegateForUpdate(); + updated.setLastName(lastName); + } + + @Override + public String getEmail() { + if (updated != null) return updated.getEmail(); + return cached.getEmail(); + } + + @Override + public void setEmail(String email) { + getDelegateForUpdate(); + updated.setEmail(email); + } + + @Override + public boolean isEmailVerified() { + if (updated != null) return updated.isEmailVerified(); + return cached.isEmailVerified(); + } + + @Override + public void setEmailVerified(boolean verified) { + getDelegateForUpdate(); + updated.setEmailVerified(verified); + } + + @Override + public void setTotp(boolean totp) { + getDelegateForUpdate(); + updated.setTotp(totp); + } + + @Override + public int getNotBefore() { + if (updated != null) return updated.getNotBefore(); + return cached.getNotBefore(); + } + + @Override + public void setNotBefore(int notBefore) { + getDelegateForUpdate(); + updated.setNotBefore(notBefore); + } + + @Override + public void updateCredential(UserCredentialModel cred) { + getDelegateForUpdate(); + updated.updateCredential(cred); + } + + @Override + public List getCredentialsDirectly() { + if (updated != null) return updated.getCredentialsDirectly(); + return cached.getCredentials(); + } + + @Override + public void updateCredentialDirectly(UserCredentialValueModel cred) { + getDelegateForUpdate(); + updated.updateCredentialDirectly(cred); + } + + @Override + public AuthenticationLinkModel getAuthenticationLink() { + if (updated != null) return updated.getAuthenticationLink(); + return cached.getAuthenticationLink(); + } + + @Override + public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) { + getDelegateForUpdate(); + updated.setAuthenticationLink(authenticationLink); + } + + @Override + public Set getRealmRoleMappings() { + if (updated != null) return updated.getRealmRoleMappings(); + Set roleMappings = getRoleMappings(); + Set realmMappings = new HashSet(); + for (RoleModel role : roleMappings) { + RoleContainerModel container = role.getContainer(); + if (container instanceof RealmModel) { + if (((RealmModel) container).getId().equals(realm.getId())) { + realmMappings.add(role); + } + } + } + return realmMappings; + } + + @Override + public Set getApplicationRoleMappings(ApplicationModel app) { + if (updated != null) return updated.getApplicationRoleMappings(app); + Set roleMappings = getRoleMappings(); + Set appMappings = new HashSet(); + for (RoleModel role : roleMappings) { + RoleContainerModel container = role.getContainer(); + if (container instanceof ApplicationModel) { + if (((ApplicationModel) container).getId().equals(app.getId())) { + appMappings.add(role); + } + } + } + return appMappings; + } + + @Override + public boolean hasRole(RoleModel role) { + if (updated != null) return updated.hasRole(role); + if (cached.getRoleMappings().contains(role.getId())) return true; + + Set mappings = getRoleMappings(); + for (RoleModel mapping: mappings) { + if (mapping.hasRole(role)) return true; + } + return false; + } + + @Override + public void grantRole(RoleModel role) { + getDelegateForUpdate(); + updated.grantRole(role); + } + + @Override + public Set getRoleMappings() { + if (updated != null) return updated.getRoleMappings(); + Set roles = new HashSet(); + for (String id : cached.getRoleMappings()) { + roles.add(cacheSession.getRoleById(id, realm)); + + } + return roles; + } + + @Override + public void deleteRoleMapping(RoleModel role) { + getDelegateForUpdate(); + updated.deleteRoleMapping(role); + } +} diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java index 557e7e9feb..465f08fc51 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java @@ -1,5 +1,7 @@ package org.keycloak.models.cache.entities; +import org.keycloak.models.AuthenticationLinkModel; +import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; @@ -18,32 +20,40 @@ import java.util.Set; public class CachedUser { private String id; private String loginName; + private String usernameKey; private String firstName; private String lastName; private String email; + private String emailKey; private boolean emailVerified; private int notBefore; private List credentials = new LinkedList(); private boolean enabled; private boolean totp; + private AuthenticationLinkModel authenticationLink; private Map attributes = new HashMap(); private Set requiredActions = new HashSet(); private Set roleMappings = new HashSet(); - public CachedUser(UserModel user) { + public CachedUser(RealmModel realm, UserModel user) { this.id = user.getId(); this.loginName = user.getLoginName(); + this.usernameKey = realm.getId() + "." + this.loginName; this.firstName = user.getFirstName(); this.lastName = user.getLastName(); this.attributes.putAll(user.getAttributes()); this.email = user.getEmail(); + if (this.email != null) { + this.emailKey = realm.getId() + "." + this.email; + } this.emailVerified = user.isEmailVerified(); this.notBefore = user.getNotBefore(); this.credentials.addAll(user.getCredentialsDirectly()); this.enabled = user.isEnabled(); this.totp = user.isTotp(); this.requiredActions.addAll(user.getRequiredActions()); + this.authenticationLink = user.getAuthenticationLink(); for (RoleModel role : user.getRoleMappings()) { roleMappings.add(role.getId()); } @@ -57,6 +67,14 @@ public class CachedUser { return loginName; } + public String getUsernameKey() { + return usernameKey; + } + + public String getEmailKey() { + return emailKey; + } + public String getFirstName() { return firstName; } @@ -100,4 +118,8 @@ public class CachedUser { public Set getRoleMappings() { return roleMappings; } + + public AuthenticationLinkModel getAuthenticationLink() { + return authenticationLink; + } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java index 018e4e6a20..20d51232db 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java @@ -254,7 +254,8 @@ public class MongoKeycloakSession implements KeycloakSession { } @Override - public Set getSocialLinks(UserModel user, RealmModel realm) { + public Set getSocialLinks(UserModel userModel, RealmModel realm) { + UserModel user = getUserById(userModel.getId(), realm); MongoUserEntity userEntity = ((UserAdapter) user).getUser(); List linkEntities = userEntity.getSocialLinks(); @@ -271,7 +272,8 @@ public class MongoKeycloakSession implements KeycloakSession { return result; } - private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) { + private SocialLinkEntity findSocialLink(UserModel userModel, String socialProvider, RealmModel realm) { + UserModel user = getUserById(userModel.getId(), realm); MongoUserEntity userEntity = ((UserAdapter) user).getUser(); List linkEntities = userEntity.getSocialLinks(); if (linkEntities == null) { @@ -289,7 +291,7 @@ public class MongoKeycloakSession implements KeycloakSession { @Override public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) { - SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider); + SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider, realm); return socialLinkEntity != null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null; } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index ecf5b763df..2602222e17 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -857,18 +857,17 @@ public class RealmAdapter extends AbstractMongoAdapter impleme } @Override - public boolean removeSocialLink(UserModel user, String socialProvider) { - SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider); + public boolean removeSocialLink(UserModel userModel, String socialProvider) { + UserModel user = getUserById(userModel.getId()); + MongoUserEntity userEntity = ((UserAdapter) user).getUser(); + SocialLinkEntity socialLinkEntity = findSocialLink(userEntity, socialProvider); if (socialLinkEntity == null) { return false; } - MongoUserEntity userEntity = ((UserAdapter) user).getUser(); - return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext); } - private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) { - MongoUserEntity userEntity = ((UserAdapter) user).getUser(); + private SocialLinkEntity findSocialLink(MongoUserEntity userEntity, String socialProvider) { List linkEntities = userEntity.getSocialLinks(); if (linkEntities == null) { return null; diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json index 65197a4cd9..a1e1ec17ff 100755 --- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json @@ -24,7 +24,7 @@ }, "modelCache": { - "provider": "${keycloak.model.cache.provider:none}" + "provider": "${keycloak.model.cache.provider:simple}" }, "timer": { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 1603229a6d..770ef84838 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -34,6 +34,7 @@ import org.keycloak.models.ApplicationModel; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.representations.idm.CredentialRepresentation; @@ -144,7 +145,7 @@ public class AccountTest { @After public void after() { - keycloakRule.configure(new KeycloakSetup() { + keycloakRule.update(new KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) { UserModel user = appRealm.getUser("test-user@localhost"); @@ -239,7 +240,7 @@ public class AccountTest { @Test public void changePasswordWithPasswordPolicy() { - keycloakRule.configure(new KeycloakRule.KeycloakSetup() { + keycloakRule.update(new KeycloakRule.KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { appRealm.setPasswordPolicy(new PasswordPolicy("length")); @@ -263,11 +264,11 @@ public class AccountTest { events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent(); } finally { - keycloakRule.configure(new KeycloakRule.KeycloakSetup() { + keycloakRule.update(new KeycloakRule.KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { appRealm.setPasswordPolicy(new PasswordPolicy(null)); - } + } }); } }