From 460b0394369cd94c95a846b7998ea96b133d93ba Mon Sep 17 00:00:00 2001 From: Martin Kanis Date: Tue, 10 Nov 2026 14:41:27 +0100 Subject: [PATCH] SCIM: PATCH on a group does not update meta.lastModified Closes #54565 Signed-off-by: Martin Kanis --- .../spi/AbstractScimResourceTypeProvider.java | 1 + .../keycloak/tests/scim/tck/GroupTest.java | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/scim/core/src/main/java/org/keycloak/scim/resource/spi/AbstractScimResourceTypeProvider.java b/scim/core/src/main/java/org/keycloak/scim/resource/spi/AbstractScimResourceTypeProvider.java index f005b901e17d..3a0248866571 100644 --- a/scim/core/src/main/java/org/keycloak/scim/resource/spi/AbstractScimResourceTypeProvider.java +++ b/scim/core/src/main/java/org/keycloak/scim/resource/spi/AbstractScimResourceTypeProvider.java @@ -63,6 +63,7 @@ public void patch(R existing, List operations) { } applyPatch(existing, model, operations); + onUpdate(model, existing); } protected void applyPatch(R existing, M model, List operations) { diff --git a/scim/tests/base/src/test/java/org/keycloak/tests/scim/tck/GroupTest.java b/scim/tests/base/src/test/java/org/keycloak/tests/scim/tck/GroupTest.java index 7766a553461b..f03cb16429d0 100644 --- a/scim/tests/base/src/test/java/org/keycloak/tests/scim/tck/GroupTest.java +++ b/scim/tests/base/src/test/java/org/keycloak/tests/scim/tck/GroupTest.java @@ -30,6 +30,8 @@ import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.events.AdminEventAssertion; import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet; +import org.keycloak.testframework.remote.timeoffset.TimeOffSet; import org.keycloak.testframework.util.ApiUtil; import org.junit.jupiter.api.Test; @@ -38,6 +40,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -50,6 +53,9 @@ @KeycloakIntegrationTest public class GroupTest extends AbstractScimTest { + @InjectTimeOffSet + TimeOffSet timeOffSet; + @Test public void testCreate() { Group expected = new Group(); @@ -416,6 +422,33 @@ public void testMetaTimestamps() { } + @Test + public void testPatchUpdatesMetaTimestamps() { + Group group = new Group(); + group.setDisplayName(KeycloakModelUtils.generateId()); + group = client.groups().create(group); + User user = createScimUser(); + + Instant lastModifiedBeforePatch = Instant.parse(group.getMeta().getLastModified()); + + timeOffSet.set(10); + client.groups().patch(group.getId(), PatchRequest.create() + .add("members", "[{\"value\":\"" + user.getId() + "\"}]").build()); + + Group afterPatch = client.groups().get(group.getId(), List.of("displayName", "members")); + assertEquals(1, afterPatch.getMembers().size()); + assertMember(afterPatch.getMembers(), user.getId(), user.getUserName()); + assertEquals(group.getMeta().getCreated(), afterPatch.getMeta().getCreated()); + assertThat(Instant.parse(afterPatch.getMeta().getLastModified()), greaterThan(lastModifiedBeforePatch)); + + String filter = ResourceFilter.filter().eq("displayName", afterPatch.getDisplayName()).and() + .gt("meta.lastModified", lastModifiedBeforePatch.toString()).build(); + ListResponse response = client.groups().getAll(filter); + assertEquals(1, response.getTotalResults()); + assertEquals(group.getId(), response.getResources().get(0).getId()); + assertEquals(afterPatch.getMeta().getLastModified(), response.getResources().get(0).getMeta().getLastModified()); + } + @Test public void testGetWithAttributes() { Group group = new Group();