DATAREST-1137 - All patch operations now verify path expressions.

We now make sure that all patch operations now get the path they're supposed to be applied to verified before execution.
This commit is contained in:
Oliver Gierke
2017-09-25 18:57:15 +02:00
parent 1b959bd8f4
commit d330455e53
8 changed files with 94 additions and 25 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,10 +38,10 @@ class AddOperation extends PatchOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#perform(java.lang.Object, java.lang.Class)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#doPerform(java.lang.Object, java.lang.Class)
*/
@Override
<T> void perform(Object targetObject, Class<T> type) {
<T> void doPerform(Object targetObject, Class<T> type) {
addValue(targetObject, evaluateValueFromTarget(targetObject, type));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,10 +53,10 @@ class CopyOperation extends FromOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#perform(java.lang.Object, java.lang.Class)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#doPerform(java.lang.Object, java.lang.Class)
*/
@Override
<T> void perform(Object target, Class<T> type) {
<T> void doPerform(Object target, Class<T> type) {
addValue(target, pathToExpression(getFrom()).getValue(target));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,10 +44,10 @@ class MoveOperation extends FromOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#perform(java.lang.Object, java.lang.Class)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#doPerform(java.lang.Object, java.lang.Class)
*/
@Override
<T> void perform(Object target, Class<T> type) {
<T> void doPerform(Object target, Class<T> type) {
addValue(target, popValueAtPath(target, getFrom()));
}
}

View File

@@ -209,11 +209,25 @@ public abstract class PatchOperation {
}
/**
* Perform the operation.
* Perform the operation in the given target object.
*
* @param target the target of the operation.
* @param target the target of the operation, must not be {@literal null}.
* @param type must not be {@literal null}.
*/
abstract <T> void perform(Object target, Class<T> type);
final <T> void perform(Object target, Class<T> type) {
verifyPath(type);
doPerform(target, type);
}
/**
* Implements the actually application of the operation.
*
* @param target must not be {@literal null}.
* @param type must not be {@literal null}.
*/
abstract <T> void doPerform(Object target, Class<T> type);
private Integer targetListIndex(String path) {
@@ -241,7 +255,7 @@ public abstract class PatchOperation {
List<String> segments = new ArrayList<String>();
for (String segment : path.split("/")) {
if (!(segment.matches("\\d+") || segment.equals("-") || segment.isEmpty())) {
if (!(segment.matches("\\d+") || segment.equals("-") || segment.equals("~") || segment.isEmpty())) {
segments.add(segment);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ package org.springframework.data.rest.webmvc.json.patch;
* @author Craig Walls
* @author Oliver Gierke
*/
public class RemoveOperation extends PatchOperation {
class RemoveOperation extends PatchOperation {
/**
* Constructs the remove operation
@@ -35,10 +35,10 @@ public class RemoveOperation extends PatchOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#perform(java.lang.Object, java.lang.Class)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#doPerform(java.lang.Object, java.lang.Class)
*/
@Override
<T> void perform(Object target, Class<T> type) {
<T> void doPerform(Object target, Class<T> type) {
popValueAtPath(target, path);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,9 @@ package org.springframework.data.rest.webmvc.json.patch;
* Operation that replaces the value at the given path with a new value.
*
* @author Craig Walls
* @author Oliver Gierke
*/
public class ReplaceOperation extends PatchOperation {
class ReplaceOperation extends PatchOperation {
/**
* Constructs the replace operation
@@ -34,10 +35,10 @@ public class ReplaceOperation extends PatchOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#perform(java.lang.Object, java.lang.Class)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#doPerform(java.lang.Object, java.lang.Class)
*/
@Override
<T> void perform(Object target, Class<T> type) {
<T> void doPerform(Object target, Class<T> type) {
setValueOnTarget(target, evaluateValueFromTarget(target, type));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.springframework.util.ObjectUtils;
* </p>
*
* @author Craig Walls
* @author Oliver Gierke
*/
class TestOperation extends PatchOperation {
@@ -45,10 +46,10 @@ class TestOperation extends PatchOperation {
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#perform(java.lang.Object, java.lang.Class)
* @see org.springframework.data.rest.webmvc.json.patch.PatchOperation#doPerform(java.lang.Object, java.lang.Class)
*/
@Override
<T> void perform(Object target, Class<T> type) {
<T> void doPerform(Object target, Class<T> type) {
Object expected = normalizeIfNumber(evaluateValueFromTarget(target, type));
Object actual = normalizeIfNumber(getValueFromTarget(target));
@@ -58,7 +59,7 @@ class TestOperation extends PatchOperation {
}
}
private Object normalizeIfNumber(Object expected) {
private static Object normalizeIfNumber(Object expected) {
if (expected instanceof Double || expected instanceof Float) {
expected = BigDecimal.valueOf(((Number) expected).doubleValue());

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.rest.webmvc.json.patch;
import org.junit.Assert;
import org.junit.Test;
/**
* General unit tests for {@link PatchOperation} implementations.
*
* @author Oliver Gierke
*/
public class PatchOperationUnitTests {
@Test // DATAREST-1137
public void invalidPathGetsRejected() {
String invalidPath = "/nonExistant";
verifyIllegalPath(new AddOperation(invalidPath, null));
verifyIllegalPath(new CopyOperation(invalidPath, null));
verifyIllegalPath(new MoveOperation(invalidPath, null));
verifyIllegalPath(new RemoveOperation(invalidPath));
verifyIllegalPath(new ReplaceOperation(invalidPath, null));
verifyIllegalPath(new TestOperation(invalidPath, null));
}
private static void verifyIllegalPath(PatchOperation operation) {
try {
Todo todo = new Todo(1L, "A", false);
operation.perform(todo, Todo.class);
Assert.fail("Expected PatchException!");
} catch (PatchException o_O) {}
}
}