DATAREST-316 - UriStringDeserializer now guards against UriTemplate.

We now defensively guard against URI templates submitted for related resources and expand the incoming URI string source to avoid URI.create(…) to fail.

Also, RepositoryPropertyReferenceController.loadPropertyValue(…) now also uses UriTemplate to guard against Uri templates provided for property references.
This commit is contained in:
Oliver Gierke
2014-06-10 16:57:11 +02:00
parent 9e2d8f674e
commit 4843a81fff
3 changed files with 94 additions and 7 deletions

View File

@@ -319,7 +319,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
// Add to the existing collection
for (Link l : incoming.getLinks()) {
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
Object propVal = loadPropertyValue(prop.propertyType, l);
coll.add(propVal);
}
@@ -336,7 +336,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
// Add to the existing collection
for (Link l : incoming.getLinks()) {
Object propVal = loadPropertyValue(prop.propertyType, l.getHref());
Object propVal = loadPropertyValue(prop.propertyType, l);
m.put(l.getRel(), propVal);
}
@@ -354,7 +354,7 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
"Must send only 1 link to update a property reference that isn't a List or a Map.");
}
Object propVal = loadPropertyValue(prop.propertyType, incoming.getLinks().get(0).getHref());
Object propVal = loadPropertyValue(prop.propertyType, incoming.getLinks().get(0));
prop.wrapper.setProperty(prop.property, propVal);
}
@@ -431,8 +431,9 @@ class RepositoryPropertyReferenceController extends AbstractRepositoryRestContro
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
}
private Object loadPropertyValue(Class<?> type, String href) {
private Object loadPropertyValue(Class<?> type, Link link) {
String href = link.expand().getHref();
String id = href.substring(href.lastIndexOf('/') + 1);
return conversionService.convert(id, type);
}

View File

@@ -40,6 +40,7 @@ import org.springframework.data.rest.webmvc.PersistentEntityResource;
import org.springframework.data.rest.webmvc.support.RepositoryLinkBuilder;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.UriTemplate;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonGenerationException;
@@ -355,7 +356,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
*
* @author Oliver Gierke
*/
private static class UriStringDeserializer extends StdDeserializer<Object> {
static class UriStringDeserializer extends StdDeserializer<Object> {
private static final long serialVersionUID = -2175900204153350125L;
@@ -384,10 +385,10 @@ public class PersistentEntityJackson2Module extends SimpleModule {
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String uriString = jp.getValueAsString();
URI uri = new UriTemplate(jp.getValueAsString()).expand();
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(property.getActualType());
return converter.convert(URI.create(uriString), URI_DESCRIPTOR, typeDescriptor);
return converter.convert(uri, URI_DESCRIPTOR, typeDescriptor);
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2014 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;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.net.URI;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.rest.core.UriToEntityConverter;
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.UriStringDeserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
/**
* Unit tests for {@link UriStringDeserializer}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class UriStringDeserializerUnitTests {
@Mock UriToEntityConverter converter;
@Mock PersistentProperty<?> property;
@Mock JsonParser parser;
@Mock DeserializationContext context;
UriStringDeserializer deserializer;
@Before
public void setUp() {
this.deserializer = new UriStringDeserializer(property, converter);
}
/**
* @see DATAREST-316
*/
@Test
public void extractsUriToForwardToConverter() throws Exception {
assertConverterInvokedWithUri("/foo/32", URI.create("/foo/32"));
}
/**
* @see DATAREST-316
*/
@Test
public void extractsUriFromTemplateToForwardToConverter() throws Exception {
assertConverterInvokedWithUri("/foo/32{?projection}", URI.create("/foo/32"));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void assertConverterInvokedWithUri(String source, URI expected) throws Exception {
when(property.getActualType()).thenReturn((Class) Object.class);
when(parser.getValueAsString()).thenReturn(source);
deserializer.deserialize(parser, context);
verify(converter)
.convert(eq(expected), Mockito.any(TypeDescriptor.class), eq(TypeDescriptor.valueOf(Object.class)));
}
}