keycloak-uncached

Details

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 7ad72b8..0955387 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 82e0332..660c738 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<RoleModel> 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 b4f6376..1a69c6c 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<String> appInvalidations = new HashSet<String>();
     protected Set<String> roleInvalidations = new HashSet<String>();
     protected Set<String> clientInvalidations = new HashSet<String>();
+    protected Set<String> userInvalidations = new HashSet<String>();
     protected Map<String, RealmModel> managedRealms = new HashMap<String, RealmModel>();
     protected Map<String, ApplicationModel> managedApplications = new HashMap<String, ApplicationModel>();
     protected Map<String, OAuthClientModel> managedClients = new HashMap<String, OAuthClientModel>();
     protected Map<String, RoleModel> managedRoles = new HashMap<String, RoleModel>();
+    protected Map<String, UserModel> managedUsers = new HashMap<String, UserModel>();
 
     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/entities/CachedUser.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
index 557e7e9..465f08f 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<UserCredentialValueModel> credentials = new LinkedList<UserCredentialValueModel>();
     private boolean enabled;
     private boolean totp;
+    private AuthenticationLinkModel authenticationLink;
     private Map<String, String> attributes = new HashMap<String, String>();
     private Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
     private Set<String> roleMappings = new HashSet<String>();
 
 
-    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<String> getRoleMappings() {
         return roleMappings;
     }
+
+    public AuthenticationLinkModel getAuthenticationLink() {
+        return authenticationLink;
+    }
 }
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 20ad44b..dca8fa1 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -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 afc35e3..12410e1 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 69bd613..f40723b 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<String, CachedOAuthClient> clientCache = new ConcurrentHashMap<String, CachedOAuthClient>();
     protected ConcurrentHashMap<String, CachedRole> roleCache = new ConcurrentHashMap<String, CachedRole>();
 
+    protected int maxUserCacheSize = 10000;
+    protected boolean userCacheEnabled = true;
+
+    protected Map<String, CachedUser> usersById = Collections.synchronizedMap(new LRUCache());
+    protected Map<String, CachedUser> usersByUsername = new ConcurrentHashMap<String, CachedUser>();
+    protected Map<String, CachedUser> usersByEmail = new ConcurrentHashMap<String, CachedUser>();
+
+    protected class LRUCache extends LinkedHashMap<String, CachedUser> {
+        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<String, CachedUser> 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 0000000..a31f3e0
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<String, String> getAttributes() {
+        if (updated != null) return updated.getAttributes();
+        return cached.getAttributes();
+    }
+
+    @Override
+    public Set<RequiredAction> 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<UserCredentialValueModel> 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<RoleModel> getRealmRoleMappings() {
+        if (updated != null) return updated.getRealmRoleMappings();
+        Set<RoleModel> roleMappings = getRoleMappings();
+        Set<RoleModel> realmMappings = new HashSet<RoleModel>();
+        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<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
+        if (updated != null) return updated.getApplicationRoleMappings(app);
+        Set<RoleModel> roleMappings = getRoleMappings();
+        Set<RoleModel> appMappings = new HashSet<RoleModel>();
+        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<RoleModel> 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<RoleModel> getRoleMappings() {
+        if (updated != null) return updated.getRoleMappings();
+        Set<RoleModel> roles = new HashSet<RoleModel>();
+        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/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 018e4e6..20d5123 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<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+    public Set<SocialLinkModel> getSocialLinks(UserModel userModel, RealmModel realm) {
+        UserModel user = getUserById(userModel.getId(), realm);
         MongoUserEntity userEntity = ((UserAdapter) user).getUser();
         List<SocialLinkEntity> 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<SocialLinkEntity> 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 ecf5b76..2602222 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<MongoRealmEntity> 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<SocialLinkEntity> 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 65197a4..a1e1ec1 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 1603229..770ef84 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));
-                }
+               }
             });
         }
     }