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 a3a72aca5496..aadead506eab 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 @@ -170,6 +170,9 @@ public boolean isEnabled() { @Override public void setEnabled(boolean enabled) { + if (updated == null && cached.isEnabled() == enabled) { + return; + } getDelegateForUpdate(); updated.setEnabled(enabled); } @@ -214,10 +217,11 @@ public void setAttribute(String name, List values) { @Override public void removeAttribute(String name) { - if (getFirstAttribute(name) != null) { - getDelegateForUpdate(); - updated.removeAttribute(name); + if (updated == null && getFirstAttribute(name) == null) { + return; } + getDelegateForUpdate(); + updated.removeAttribute(name); } @Override @@ -247,30 +251,42 @@ public Stream getRequiredActionsStream() { @Override public void addRequiredAction(RequiredAction action) { + if (action == null || updated == null && getCachedRequiredActions().contains(action.name())) { + return; + } getDelegateForUpdate(); updated.addRequiredAction(action); } @Override public void removeRequiredAction(RequiredAction action) { - if (getRequiredActionsStream().anyMatch(s -> Objects.equals(s, action.name()))) { - getDelegateForUpdate(); - updated.removeRequiredAction(action); + if (action == null || updated == null && !getCachedRequiredActions().contains(action.name())) { + return; } + getDelegateForUpdate(); + updated.removeRequiredAction(action); } @Override public void addRequiredAction(String action) { + if (updated == null && getCachedRequiredActions().contains(action)) { + return; + } getDelegateForUpdate(); updated.addRequiredAction(action); } @Override public void removeRequiredAction(String action) { - if (getRequiredActionsStream().anyMatch(s -> Objects.equals(s, action))) { - getDelegateForUpdate(); - updated.removeRequiredAction(action); + if (updated == null && !getCachedRequiredActions().contains(action)) { + return; } + getDelegateForUpdate(); + updated.removeRequiredAction(action); + } + + private Set getCachedRequiredActions() { + return cached.getRequiredActions(keycloakSession, modelSupplier); } @Override @@ -281,6 +297,9 @@ public boolean isEmailVerified() { @Override public void setEmailVerified(boolean verified) { + if (updated == null && cached.isEmailVerified() == verified) { + return; + } getDelegateForUpdate(); updated.setEmailVerified(verified); } @@ -293,6 +312,9 @@ public String getFederationLink() { @Override public void setFederationLink(String link) { + if (updated == null && Objects.equals(cached.getFederationLink(), link)) { + return; + } getDelegateForUpdate(); updated.setFederationLink(link); } @@ -305,6 +327,9 @@ public String getServiceAccountClientLink() { @Override public void setServiceAccountClientLink(String clientInternalId) { + if (updated == null && Objects.equals(cached.getServiceAccountClientLink(), clientInternalId)) { + return; + } getDelegateForUpdate(); updated.setServiceAccountClientLink(clientInternalId); } @@ -388,6 +413,9 @@ public boolean hasRole(RoleModel role) { @Override public void grantRole(RoleModel role) { + if (updated == null && cached.getRoleMappings(keycloakSession, modelSupplier).contains(role.getId())) { + return; + } getDelegateForUpdate(); updated.grantRole(role); } @@ -411,6 +439,9 @@ public Stream getRoleMappingsStream() { @Override public void deleteRoleMapping(RoleModel role) { + if (updated == null && !cached.getRoleMappings(keycloakSession, modelSupplier).contains(role.getId())) { + return; + } getDelegateForUpdate(); updated.deleteRoleMapping(role); } @@ -454,6 +485,9 @@ public long getGroupsCountByNameContaining(String search) { @Override public void joinGroup(GroupModel group) { + if (group.getType() == Type.REALM && cached.getGroups(keycloakSession, modelSupplier).contains(group.getId())) { + return; + } getDelegateForUpdate(); updated.joinGroup(group); @@ -461,6 +495,9 @@ public void joinGroup(GroupModel group) { @Override public void leaveGroup(GroupModel group) { + if (group.getType() == Type.REALM && updated == null && !cached.getGroups(keycloakSession, modelSupplier).contains(group.getId())) { + return; + } getDelegateForUpdate(); updated.leaveGroup(group); } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java index 8480d141c2c1..0d9de2cd6fb2 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java @@ -376,7 +376,13 @@ protected UserModel validateCache(RealmModel realm, CachedUser cached, Supplier< } } - return new UserAdapter(cached, this, session, realm); + UserAdapter userAdapter = new UserAdapter(cached, this, session, realm); + + if (isReadOnlyOrganizationMember(session, userAdapter)) { + return new ReadOnlyUserModelDelegate(userAdapter, false); + } + + return userAdapter; } protected UserModel cacheUser(RealmModel realm, UserModel delegate, Long revision) { diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/JpaChangesPerformer.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/JpaChangesPerformer.java index f7dfdd17c928..a615cb83de59 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/JpaChangesPerformer.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/JpaChangesPerformer.java @@ -169,6 +169,7 @@ private static void processClientSessionUpdate(Keyc SessionUpdatesList sessionUpdates = entry.getValue(); SessionEntityWrapper sessionWrapper = sessionUpdates.getEntityWrapper(); RealmModel realm = sessionUpdates.getRealm(); + session.getContext().setRealm(realm); UserSessionPersisterProvider userSessionPersister = session.getProvider(UserSessionPersisterProvider.class); switch (merged.getOperation()) { @@ -433,6 +434,7 @@ private static void processUserSessionUpdate(Keyclo SessionUpdatesList sessionUpdates = entry.getValue(); SessionEntityWrapper sessionWrapper = sessionUpdates.getEntityWrapper(); RealmModel realm = sessionUpdates.getRealm(); + session.getContext().setRealm(realm); UserSessionPersisterProvider userSessionPersister = session.getProvider(UserSessionPersisterProvider.class); UserSessionEntity entity = (UserSessionEntity) sessionWrapper.getEntity(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiTest.java index 4d7c8a424ca6..1ef36debd265 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAdminRestApiTest.java @@ -259,6 +259,7 @@ public void testErrorResponseWhenLdapIsFailing() { UserResource userResource = testRealm().users().get(newUserId1); try { + user1.setFirstName(user1.getFirstName() + " updated"); userResource.update(user1); Assert.fail("Not expected to successfully update user"); } catch (WebApplicationException expected) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java index 27373071fd40..ed627c32f25a 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java @@ -1658,6 +1658,7 @@ public void updateLDAPUsernameTest() { RealmModel testRealm = ctx.getRealm(); UserModel importedUser = UserStoragePrivateUtil.userLocalStorage(session).getUserByUsername(testRealm, "beckybecks"); + Assert.assertNotNull(importedUser); // Update user 'beckybecks' in LDAP LDAPObject becky = ctx.getLdapProvider().loadLDAPUserByUsername(testRealm, importedUser.getUsername()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java index d65800e0caed..ced3ad150b52 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java @@ -146,7 +146,7 @@ public void testRestartSession(KeycloakSession session) { Time.setOffset(100); try { KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), kcSession -> { - kcSession.getContext().setRealm(realm); + kcSession.getContext().setRealm(session.realms().getRealm(realm.getId())); UserSessionModel userSession = kcSession.sessions().getUserSession(realm, sessions[1].getId()); assertSession(userSession, kcSession.users().getUserByUsername(realm, "user1"), "127.0.0.2", started, started, "test-app"); AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(realm.getClientByClientId("test-app").getId()); @@ -166,6 +166,7 @@ public void testRestartSession(KeycloakSession session) { }); KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), kcSession -> { + kcSession.getContext().setRealm(session.realms().getRealm(realm.getId())); UserSessionModel userSession = kcSession.sessions().getUserSession(realm, sessions[1].getId()); assertThat(userSession.getNotes(), Matchers.anEmptyMap());