diff --git a/scim-core/src/main/java/org/apache/directory/scim/core/repository/DefaultPatchHandler.java b/scim-core/src/main/java/org/apache/directory/scim/core/repository/DefaultPatchHandler.java index aa15875b1..659b12c6e 100644 --- a/scim-core/src/main/java/org/apache/directory/scim/core/repository/DefaultPatchHandler.java +++ b/scim-core/src/main/java/org/apache/directory/scim/core/repository/DefaultPatchHandler.java @@ -48,6 +48,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.Predicate; +import org.apache.directory.scim.spec.schema.Schema.Attribute.Type; import static java.util.stream.Collectors.toList; @@ -337,8 +338,16 @@ public void applySingleValue(Map sourceAsMap, Attribute attribut checkMutability(attribute.getAttribute(subAttributeName), parentValue.get(subAttributeName)); parentValue.put(subAttributeName, value); } else { - checkMutability(attribute, sourceAsMap.get(attribute.getName())); - sourceAsMap.put(attribute.getName(), value); + Object currentValue = sourceAsMap.get(attribute.getName()); + checkMutability(attribute, currentValue); + // PATCH on complex attributes should only replace the values given, leaving any not provided in the request untouched + if (attribute.getType() == Type.COMPLEX) { + Map newValue = ((Map) currentValue); + newValue.putAll((Map) value); + sourceAsMap.put(attribute.getName(), newValue); + } else { + sourceAsMap.put(attribute.getName(), value); + } } } diff --git a/scim-core/src/test/java/org/apache/directory/scim/core/repository/PatchHandlerTest.java b/scim-core/src/test/java/org/apache/directory/scim/core/repository/PatchHandlerTest.java index 2df84326f..9583ea865 100644 --- a/scim-core/src/test/java/org/apache/directory/scim/core/repository/PatchHandlerTest.java +++ b/scim-core/src/test/java/org/apache/directory/scim/core/repository/PatchHandlerTest.java @@ -56,6 +56,26 @@ public PatchHandlerTest() { this.patchHandler = new DefaultPatchHandler(schemaRegistry); } + @Test + public void applyReplaceComplexAttribute() { + // setup existing user + Name existingName = new Name(); + existingName.setFamilyName("Family Name"); + existingName.setGivenName("Given Name"); + ScimUser user = user(); + user.setName(existingName); + + // setup patch ops + Map newName = Map.of("familyName", "New Family Name"); + PatchOperation op = patchOperation(REPLACE, "name", newName); + + //execute + ScimUser updatedUser = patchHandler.apply(user, List.of(op)); + assertThat(updatedUser.getName().getFamilyName()).isEqualTo("New Family Name"); + // assert that PATCH did not update fields not provided in PatchOperation + assertThat(updatedUser.getName().getGivenName()).isNotNull(); + } + @Test public void applyReplaceUserName() { String newUserName = "testUser_updated@test.com";