From 8f9ed32a66c970bf4d77b53d92446665ea813d2f Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Fri, 23 Jun 2017 15:16:23 +0200 Subject: [PATCH] KEYCLOAK-5078 ConcurrencyTest fails intermittently This commit fixes 401 Unauthorized issues --- .../models/cache/infinispan/GroupAdapter.java | 4 ++-- .../models/cache/infinispan/RealmAdapter.java | 4 ++-- .../models/cache/infinispan/UserAdapter.java | 2 +- .../keycloak/credential/CredentialModel.java | 17 ++++++++++++++++- .../keycloak/models/cache/CachedUserModel.java | 4 ++-- .../credential/PasswordCredentialProvider.java | 12 ++++++++---- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java index d32d35cc36..21bcc66ed6 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupAdapter.java @@ -35,7 +35,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class GroupAdapter implements GroupModel { - protected GroupModel updated; + protected volatile GroupModel updated; protected CachedGroup cached; protected RealmCacheSession cacheSession; protected KeycloakSession keycloakSession; @@ -56,7 +56,7 @@ public class GroupAdapter implements GroupModel { } } - protected boolean invalidated; + protected volatile boolean invalidated; public void invalidate() { invalidated = true; } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index 0bed8262a1..d1945ad42a 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -55,7 +55,7 @@ import java.util.concurrent.ConcurrentHashMap; public class RealmAdapter implements CachedRealmModel { protected CachedRealm cached; protected RealmCacheSession cacheSession; - protected RealmModel updated; + protected volatile RealmModel updated; protected RealmCache cache; protected KeycloakSession session; @@ -75,7 +75,7 @@ public class RealmAdapter implements CachedRealmModel { return updated; } - protected boolean invalidated; + protected volatile boolean invalidated; protected void invalidateFlag() { invalidated = true; diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java index 3094521fd4..0056edf493 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java @@ -41,7 +41,7 @@ import java.util.concurrent.ConcurrentHashMap; * @version $Revision: 1 $ */ public class UserAdapter implements CachedUserModel { - protected UserModel updated; + protected volatile UserModel updated; protected CachedUser cached; protected UserCacheSession userProviderCache; protected KeycloakSession keycloakSession; diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java b/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java index d3600ba125..f466183508 100755 --- a/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java +++ b/server-spi/src/main/java/org/keycloak/credential/CredentialModel.java @@ -56,7 +56,22 @@ public class CredentialModel implements Serializable { private int period; private MultivaluedHashMap config; - + public CredentialModel shallowClone() { + CredentialModel res = new CredentialModel(); + res.id = id; + res.type = type; + res.value = value; + res.device = device; + res.salt = salt; + res.hashIterations = hashIterations; + res.createdDate = createdDate; + res.counter = counter; + res.algorithm = algorithm; + res.digits = digits; + res.period = period; + res.config = config; + return res; + } public String getId() { return id; diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java b/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java index 8434d9de4c..1d3f59f289 100644 --- a/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java +++ b/server-spi/src/main/java/org/keycloak/models/cache/CachedUserModel.java @@ -18,7 +18,7 @@ package org.keycloak.models.cache; import org.keycloak.models.UserModel; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * Cached users will implement this interface @@ -55,5 +55,5 @@ public interface CachedUserModel extends UserModel { * * @return */ - ConcurrentHashMap getCachedWith(); + ConcurrentMap getCachedWith(); } diff --git a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java index a3f468ed74..2be347130a 100644 --- a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java +++ b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java @@ -56,12 +56,14 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia } public CredentialModel getPassword(RealmModel realm, UserModel user) { - List passwords; + List passwords = null; if (user instanceof CachedUserModel && !((CachedUserModel)user).isMarkedForEviction()) { CachedUserModel cached = (CachedUserModel)user; passwords = (List)cached.getCachedWith().get(PASSWORD_CACHE_KEY); - } else { + } + // 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, CredentialModel.PASSWORD); } if (passwords == null || passwords.isEmpty()) return null; @@ -207,8 +209,10 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia return true; } - hash.encode(cred.getValue(), policy.getHashIterations(), password); - getCredentialStore().updateCredential(realm, user, password); + CredentialModel newPassword = password.shallowClone(); + hash.encode(cred.getValue(), policy.getHashIterations(), newPassword); + getCredentialStore().updateCredential(realm, user, newPassword); + UserCache userCache = session.userCache(); if (userCache != null) { userCache.evict(realm, user);