diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java index 533bb89335f..0c70cbc8b5d 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java @@ -28,9 +28,9 @@ import org.keycloak.models.utils.KeycloakModelUtils; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -61,6 +61,15 @@ public class RoleAdapter implements RoleModel { } } + protected void getDelegateForRename(String newName) { + if (!Objects.equals(newName, cached.getName())) { + // New role name might have been cached as non-existent + String containerId = getContainerId(); + cacheSession.registerRoleInvalidation(cached.getId(), newName, containerId); + } + getDelegateForUpdate(); + } + protected boolean invalidated; public void invalidate() { @@ -102,7 +111,7 @@ public class RoleAdapter implements RoleModel { @Override public void setName(String name) { - getDelegateForUpdate(); + getDelegateForRename(name); updated.setName(name); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java index d2249a64244..29271e3a2e6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java @@ -32,11 +32,13 @@ import org.keycloak.testsuite.util.RoleBuilder; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; + import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -47,6 +49,7 @@ import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -135,6 +138,7 @@ public class RoleByIdResourceTest extends AbstractAdminTest { resource.getRole(ids.get("role-a")); fail("Expected 404"); } catch (NotFoundException e) { + // Ignore } } @@ -276,8 +280,63 @@ public class RoleByIdResourceTest extends AbstractAdminTest { } } - @Test (expected = BadRequestException.class) + @Test(expected = BadRequestException.class) public void deleteDefaultRole() { resource.deleteRole(adminClient.realm(REALM_NAME).roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME).toRepresentation().getId()); } + + /** + * see #37320 + */ + @Test + public void renameRoleToNamePreviouslyCached() { + String roleName = "realm-role-new-" + new Random().nextInt(); + RoleRepresentation newRoleRepresentation = RoleBuilder.create() + .name(roleName) + .build(); + adminClient.realm(REALM_NAME).roles().create(newRoleRepresentation); + RoleRepresentation roleRepresentation = adminClient.realm(REALM_NAME).roles().get(roleName).toRepresentation(); + getCleanup().addRoleId(roleRepresentation.getId()); + + String newRoleName = "realm-role-renamed-" + new Random().nextInt(); + cacheMissingRoleName(newRoleName); + + RoleRepresentation updatedRoleRepresentation = RoleBuilder.create() + .id(roleRepresentation.getId()) + .name(newRoleName) + .build(); + resource.updateRole(roleRepresentation.getId(), updatedRoleRepresentation); + + try { + adminClient.realm(REALM_NAME).roles().get(newRoleName).toRepresentation(); + } catch (NotFoundException e) { + fail("Role is incorrectly cached"); + } + } + + /** + * see #37320 + */ + @Test + public void createRolePreviouslyCached() { + String roleName = "realm-role-new-" + new Random().nextInt(); + RoleRepresentation roleRepresentation = RoleBuilder.create() + .name(roleName) + .build(); + + cacheMissingRoleName(roleName); + + adminClient.realm(REALM_NAME).roles().create(roleRepresentation); + + try { + roleRepresentation = adminClient.realm(REALM_NAME).roles().get(roleName).toRepresentation(); + getCleanup().addRoleId(roleRepresentation.getId()); + } catch (NotFoundException e) { + fail("Role is incorrectly cached"); + } + } + + private void cacheMissingRoleName(String missingRoleName) { + assertThrows(NotFoundException.class, () -> adminClient.realm(REALM_NAME).roles().get(missingRoleName).toRepresentation()); + } }