Skip to content

Commit

Permalink
[RESTEASY-2469] Allow manual setup of whitelist validator
Browse files Browse the repository at this point in the history
  • Loading branch information
asoldano committed Feb 6, 2020
1 parent 9b6a874 commit e7c675e
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.github.fge.jsonpatch.JsonPatch;
import com.github.fge.jsonpatch.JsonPatchException;
/*
Expand Down Expand Up @@ -76,7 +78,12 @@ public void filter(ContainerRequestContext requestContext) throws IOException
msgBodyWriter.writeTo(object, object.getClass(), object.getClass(), methodInvoker.getMethodAnnotations(),
MediaType.APPLICATION_JSON_TYPE, new MultivaluedTreeMap<String, Object>(), tmpOutputStream);
ObjectMapper mapper = getObjectMapper();
mapper.setPolymorphicTypeValidator(new WhiteListPolymorphicTypeValidatorBuilder().build());
PolymorphicTypeValidator ptv = mapper.getPolymorphicTypeValidator();
//the check is protected by test org.jboss.resteasy.test.providers.jackson2.whitelist.JacksonConfig,
//be sure to keep that in synch if changing anything here.
if (ptv == null || ptv instanceof LaissezFaireSubTypeValidator) {
mapper.setPolymorphicTypeValidator(new WhiteListPolymorphicTypeValidatorBuilder().build());
}
JsonNode targetJson = mapper.readValue(tmpOutputStream.toByteArray(), JsonNode.class);
JsonPatch patch = JsonPatch.fromJson(mapper.readValue(request.getInputStream(), JsonNode.class));
JsonNode result = patch.apply(targetJson);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.jaxrs.cfg.ObjectWriterInjector;
import com.fasterxml.jackson.jaxrs.cfg.ObjectWriterModifier;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
Expand Down Expand Up @@ -160,7 +162,12 @@ public Object readFrom(Class<Object> type, final Type genericType, Annotation[]
// not yet resolved (or not cached any more)? Resolve!
if (endpoint == null) {
ObjectMapper mapper = locateMapper(type, mediaType);
mapper.setPolymorphicTypeValidator(new WhiteListPolymorphicTypeValidatorBuilder().build());
PolymorphicTypeValidator ptv = mapper.getPolymorphicTypeValidator();
//the check is protected by test org.jboss.resteasy.test.providers.jackson2.whitelist.JacksonConfig,
//be sure to keep that in synch if changing anything here.
if (ptv == null || ptv instanceof LaissezFaireSubTypeValidator) {
mapper.setPolymorphicTypeValidator(new WhiteListPolymorphicTypeValidatorBuilder().build());
}
endpoint = _configForReading(mapper, annotations, null);
_readers.put(key, endpoint);
}
Expand Down Expand Up @@ -222,7 +229,12 @@ public void flush() throws IOException {
// not yet resolved (or not cached any more)? Resolve!
if (endpoint == null) {
ObjectMapper mapper = locateMapper(type, mediaType);
mapper.setPolymorphicTypeValidator(new WhiteListPolymorphicTypeValidatorBuilder().build());
PolymorphicTypeValidator ptv = mapper.getPolymorphicTypeValidator();
//the check is protected by test org.jboss.resteasy.test.providers.jackson2.whitelist.JacksonConfig,
//be sure to keep that in synch if changing anything here.
if (ptv == null || ptv instanceof LaissezFaireSubTypeValidator) {
mapper.setPolymorphicTypeValidator(new WhiteListPolymorphicTypeValidatorBuilder().build());
}
mapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, WRITE_DURATIONS_AS_TIMESTAMPS);
endpoint = _configForWriting(mapper, annotations, null);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.jboss.resteasy.test.providers.jackson2.whitelist;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.jboss.resteasy.plugins.providers.jackson.WhiteListPolymorphicTypeValidatorBuilder;
import org.jboss.resteasy.test.providers.jackson2.whitelist.model.land.Automobile2;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonConfig implements ContextResolver<ObjectMapper>
{
private final ObjectMapper objectMapper;

public JacksonConfig()
{
objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
if (objectMapper.getPolymorphicTypeValidator() != null && !(objectMapper.getPolymorphicTypeValidator() instanceof LaissezFaireSubTypeValidator)) {
throw new RuntimeException(
"Unexpected default allow-everything implementation of Polymorphic Type Validator. If Jackson 2 actually changed this, RESTEasy has to be fixed accordingly, see changes for RESTEASY-2469.");
}
objectMapper.setPolymorphicTypeValidator(
new WhiteListPolymorphicTypeValidatorBuilder().allowIfBaseType(Automobile2.class).allowIfSubType(Automobile2.class).build());
}

@Override
public ObjectMapper getContext(Class<?> type)
{
return objectMapper;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.jboss.resteasy.test.providers.jackson2.whitelist;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.logging.Logger;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.util.HttpResponseCodes;
import org.jboss.resteasy.test.providers.jackson2.whitelist.model.AbstractVehicle;
import org.jboss.resteasy.test.providers.jackson2.whitelist.model.TestPolymorphicType;
import org.jboss.resteasy.test.providers.jackson2.whitelist.model.air.Aircraft;
import org.jboss.resteasy.test.providers.jackson2.whitelist.model.land.Automobile;
import org.jboss.resteasy.test.providers.jackson2.whitelist.model.land.Automobile2;
import org.jboss.resteasy.utils.PortProviderUtil;
import org.jboss.resteasy.utils.TestUtil;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.ws.rs.client.ClientBuilder;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;


/**
* @tpSubChapter Jackson2 provider
* @tpChapter Integration tests
* @tpSince RESTEasy 4.5.0
*/
@RunWith(Arquillian.class)
@RunAsClient
public class WhiteListPolymorphicTypeValidatorManualOverrideTest {

protected static final Logger logger = Logger.getLogger(WhiteListPolymorphicTypeValidatorManualOverrideTest.class.getName());

static ResteasyClient client;

@Deployment(name = "default")
public static Archive<?> deploy() {
WebArchive war = TestUtil.prepareArchive(WhiteListPolymorphicTypeValidatorManualOverrideTest.class.getSimpleName());
war.addClass(WhiteListPolymorphicTypeValidatorManualOverrideTest.class);
return TestUtil.finishContainerPrepare(war, null, JaxRsActivator.class, TestRESTService.class,
TestPolymorphicType.class, AbstractVehicle.class, Automobile.class, Automobile2.class, Aircraft.class, JacksonConfig.class);
}

@Before
public void init() {
client = (ResteasyClient) ClientBuilder.newClient();
}

@After
public void after() throws Exception {
client.close();
}

private String generateURL(String path) {
return PortProviderUtil.generateURL(path, WhiteListPolymorphicTypeValidatorManualOverrideTest.class.getSimpleName());
}

@Test
public void testManualOverrideGood() throws Exception {
String response = sendPost(new TestPolymorphicType(new Automobile2()));
logger.info("response: " + response);
Assert.assertNotNull(response);
Assert.assertTrue(response.contains("Response code: " + HttpResponseCodes.SC_CREATED));
Assert.assertTrue(response.contains("Created"));
}

@Test
public void testAircraftFailure() throws Exception {
String response = sendPost(new TestPolymorphicType(new Aircraft()));
logger.info("response: " + response);
Assert.assertNotNull(response);
Assert.assertTrue(response.contains("Response code: " + HttpResponseCodes.SC_BAD_REQUEST));
Assert.assertTrue(response.contains("Configured `PolymorphicTypeValidator`") && response.contains("denied resolution"));
}

private String createJSONString(TestPolymorphicType t) throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(t);
}

private String sendPost(TestPolymorphicType t) throws Exception {

logger.info("Creating JSON test data");
String jsonData = createJSONString(t);

logger.info("jsonData: " + jsonData);

String urlString = generateURL("/test/post");
logger.info("POST data to : " + urlString);
URL url = new URL(urlString);
URLConnection con = url.openConnection();
HttpURLConnection http = (HttpURLConnection) con;
http.setRequestMethod("POST"); // PUT is another valid option
http.setDoOutput(true);

byte[] out = jsonData.getBytes(StandardCharsets.UTF_8);

http.setFixedLengthStreamingMode(out.length);
http.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
http.connect();
try (OutputStream os = http.getOutputStream()) {
os.write(out);
}

InputStream is = null;
if (http.getResponseCode() != 200) {
is = http.getErrorStream();
} else {
/* error from server */
is = http.getInputStream();
}

String result = is == null ? "" : new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n"));
String response = String.format("Response code: %s response message: %s %s", http.getResponseCode(), http.getResponseMessage(), result);

logger.info("Response: " + response);

return response;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.jboss.resteasy.test.providers.jackson2.whitelist.model.land;

import org.jboss.resteasy.test.providers.jackson2.whitelist.model.AbstractVehicle;

/**
* @author bmaxwell
*/
public class Automobile2 extends AbstractVehicle {

private int speed;

public Automobile2() {
super("Automobile2");
}

public int getSpeed() {
return speed;
}

public void setSpeed(int speed) {
this.speed = speed;
}

public String toString() {
return String.format("%s speed: %d", super.toString(), speed);
}
}

0 comments on commit e7c675e

Please sign in to comment.