keycloak-aplcache

Details

diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java
index b046efc..60c089b 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java
@@ -1,11 +1,14 @@
 package org.keycloak.models.cache.infinispan;
 
 import org.infinispan.Cache;
+import org.infinispan.commands.write.RemoveCommand;
+import org.infinispan.container.entries.CacheEntry;
+import org.infinispan.context.InvocationContext;
+import org.infinispan.interceptors.base.CommandInterceptor;
 import org.infinispan.notifications.Listener;
-import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
-import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
-import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
-import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
+import org.infinispan.notifications.cachelistener.annotation.*;
+import org.infinispan.notifications.cachelistener.event.*;
+import org.jboss.logging.Logger;
 import org.keycloak.Config;
 import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
 import org.keycloak.models.KeycloakSession;
@@ -21,6 +24,8 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class InfinispanCacheUserProviderFactory implements CacheUserProviderFactory {
 
+    private static final Logger log = Logger.getLogger(InfinispanCacheUserProviderFactory.class);
+
     protected InfinispanUserCache userCache;
 
     protected final RealmLookup usernameLookup = new RealmLookup();
@@ -82,36 +87,63 @@ public class InfinispanCacheUserProviderFactory implements CacheUserProviderFact
         @CacheEntryCreated
         public void userCreated(CacheEntryCreatedEvent<String, CachedUser> event) {
             if (!event.isPre()) {
-
-                CachedUser cachedUser;
+                CachedUser user;
 
                 // Try optimized version if available
                 if (isNewInfinispan) {
-                    cachedUser = event.getValue();
+                    user = event.getValue();
                 } else {
                     String userId = event.getKey();
-                    cachedUser = event.getCache().get(userId);
+                    user = event.getCache().get(userId);
                 }
 
-                if (cachedUser != null) {
-                    String realm = cachedUser.getRealm();
-                    usernameLookup.put(realm, cachedUser.getUsername(), cachedUser.getId());
-                    if (cachedUser.getEmail() != null) {
-                        emailLookup.put(realm, cachedUser.getEmail(), cachedUser.getId());
+                if (user != null) {
+                    String realm = user.getRealm();
+
+                    usernameLookup.put(realm, user.getUsername(), user.getId());
+                    if (user.getEmail() != null) {
+                        emailLookup.put(realm, user.getEmail(), user.getId());
                     }
+
+                    log.tracev("User added realm={0}, id={1}, username={2}", realm, event.getValue().getId(), event.getValue().getUsername());
                 }
             }
         }
 
         @CacheEntryRemoved
         public void userRemoved(CacheEntryRemovedEvent<String, CachedUser> event) {
-            if (event.isPre() && event.getValue() != null) {
-                CachedUser cachedUser = event.getValue();
-                String realm = cachedUser.getRealm();
-                usernameLookup.remove(realm, cachedUser.getUsername());
-                if (cachedUser.getEmail() != null) {
-                    emailLookup.remove(realm, cachedUser.getEmail());
-                }
+            CachedUser user = event.getOldValue();
+            if (event.isPre() &&  user != null) {
+                removeUser(user);
+
+                log.tracev("User invalidated realm={0}, id={1}, username={2}", user.getRealm(), user.getId(), user.getUsername());
+            }
+        }
+
+        @CacheEntryInvalidated
+        public void userInvalidated(CacheEntryInvalidatedEvent<String, CachedUser> event) {
+            CachedUser user = event.getValue();
+            if (event.isPre() && user != null) {
+                removeUser(user);
+
+                log.tracev("User invalidated realm={0}, id={1}, username={2}", user.getRealm(), user.getId(), user.getUsername());
+            }
+        }
+
+        @CacheEntriesEvicted
+        public void userEvicted(CacheEntriesEvictedEvent<String, CachedUser> event) {
+            for (CachedUser user : event.getEntries().values()) {
+                removeUser(user);
+
+                log.tracev("User evicted realm={0}, id={1}, username={2}", user.getRealm(), user.getId(), user.getUsername());
+            }
+        }
+
+        private void removeUser(CachedUser cachedUser) {
+            String realm = cachedUser.getRealm();
+            usernameLookup.remove(realm, cachedUser.getUsername());
+            if (cachedUser.getEmail() != null) {
+                emailLookup.remove(realm, cachedUser.getEmail());
             }
         }
 
@@ -119,12 +151,12 @@ public class InfinispanCacheUserProviderFactory implements CacheUserProviderFact
 
     static class RealmLookup {
 
-        protected final ConcurrentHashMap<String, ConcurrentHashMap<String, String>> lookup = new ConcurrentHashMap<String, ConcurrentHashMap<String, String>>();
+        protected final ConcurrentHashMap<String, ConcurrentHashMap<String, String>> lookup = new ConcurrentHashMap<>();
 
         public void put(String realm, String key, String value) {
             ConcurrentHashMap<String, String> map = lookup.get(realm);
             if(map == null) {
-                map = new ConcurrentHashMap<String, String>();
+                map = new ConcurrentHashMap<>();
                 ConcurrentHashMap<String, String> p = lookup.putIfAbsent(realm, map);
                 if (p != null) {
                     map = p;
diff --git a/services/src/main/java/org/keycloak/services/util/LocaleHelper.java b/services/src/main/java/org/keycloak/services/util/LocaleHelper.java
index 0a15bf1..eeefc81 100644
--- a/services/src/main/java/org/keycloak/services/util/LocaleHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/LocaleHelper.java
@@ -113,15 +113,7 @@ public class LocaleHelper {
                                            RealmModel realm,
                                            String locale) {
         boolean secure = realm.getSslRequired().isRequired(session.getContext().getUri().getRequestUri().getHost());
-        addCookie(LOCALE_COOKIE, locale, AuthenticationManager.getRealmCookiePath(realm, session.getContext().getUri()), null, null, -1, secure, true);
-    }
-
-    private static void addCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) {
-        HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
-        StringBuffer cookieBuf = new StringBuffer();
-        ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly);
-        String cookie = cookieBuf.toString();
-        response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie);
+        CookieHelper.addCookie(LOCALE_COOKIE, locale, AuthenticationManager.getRealmCookiePath(realm, session.getContext().getUri()), null, null, -1, secure, true);
     }
 
     private static Locale findLocale(Set<String> supportedLocales, String... localeStrings) {
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 9458998..d02c255 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
@@ -28,6 +28,7 @@ import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventType;
 import org.keycloak.migration.MigrationModel;
@@ -468,6 +469,104 @@ public class AccountTest {
         }
     }
 
+    @Test
+    public void changeUsernameLoginWithOldUsername() {
+        KeycloakSession session = keycloakRule.startSession();
+        UserModel user = session.users().addUser(session.realms().getRealm("test"), "change-username");
+        user.setEnabled(true);
+        user.setEmail("change-username@localhost");
+        user.setFirstName("first");
+        user.setLastName("last");
+        user.updateCredential(UserCredentialModel.password("password"));
+        keycloakRule.stopSession(session, true);
+
+        keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.setEditUsernameAllowed(true);
+            }
+        });
+
+        try {
+            profilePage.open();
+            loginPage.login("change-username", "password");
+
+            profilePage.updateUsername("change-username-updated");
+
+            Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
+
+            profilePage.logout();
+
+            profilePage.open();
+
+            Assert.assertTrue(loginPage.isCurrent());
+
+            loginPage.login("change-username", "password");
+
+            Assert.assertTrue(loginPage.isCurrent());
+            Assert.assertEquals("Invalid username or password.", loginPage.getError());
+
+            loginPage.login("change-username-updated", "password");
+        } finally {
+            events.clear();
+            keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+                @Override
+                public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                    appRealm.setEditUsernameAllowed(false);
+                }
+            });
+        }
+    }
+
+    @Test
+    public void changeEmailLoginWithOldEmail() {
+        KeycloakSession session = keycloakRule.startSession();
+        UserModel user = session.users().addUser(session.realms().getRealm("test"), "change-email");
+        user.setEnabled(true);
+        user.setEmail("change-username@localhost");
+        user.setFirstName("first");
+        user.setLastName("last");
+        user.updateCredential(UserCredentialModel.password("password"));
+        keycloakRule.stopSession(session, true);
+
+        keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.setEditUsernameAllowed(true);
+            }
+        });
+
+        try {
+            profilePage.open();
+            loginPage.login("change-username@localhost", "password");
+
+            profilePage.updateEmail("change-username-updated@localhost");
+
+            Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
+
+            profilePage.logout();
+
+            profilePage.open();
+
+            Assert.assertTrue(loginPage.isCurrent());
+
+            loginPage.login("change-username@localhost", "password");
+
+            Assert.assertTrue(loginPage.isCurrent());
+            Assert.assertEquals("Invalid username or password.", loginPage.getError());
+
+            loginPage.login("change-username-updated@localhost", "password");
+        } finally {
+            events.clear();
+            keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+                @Override
+                public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                    appRealm.setEditUsernameAllowed(false);
+                }
+            });
+        }
+    }
+
     // KEYCLOAK-1534
     @Test
     public void changeEmailToExisting() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
index c013345..1fe6203 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
@@ -87,6 +87,18 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
         submitButton.click();
     }
 
+    public void updateUsername(String username) {
+        usernameInput.clear();
+        usernameInput.sendKeys(username);
+        submitButton.click();
+    }
+
+    public void updateEmail(String email) {
+        emailInput.clear();
+        emailInput.sendKeys(email);
+        submitButton.click();
+    }
+
     public void clickCancel() {
         cancelButton.click();
     }