Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2208 Make SubTypeValidator configurable #2269

Closed
wants to merge 4 commits into from

Conversation

meeque
Copy link

@meeque meeque commented Feb 28, 2019

This PR is just a first draft towards a solution for #2208. Feel free to comment, so I can refine it. Do not merge as-is, for the following reasons:

Moreover, I'm still uncertain about several aspects. I'll add respective comments/questions to the code. Maybe someone can help me getting this to a point where the PR could be accepted?

* default sub-type validator blocks deserialization widgets, i.e. classes that
* are known to be dangerous when deserialized from untrusted sources.
*/
protected final SubTypeValidator _subTypeValidator;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we support an array of SubTypeValidators instead?
That would be more in line with the other configuration options in this class.
Moreover, it could help ensuring that the default SubTypeValidator cannot be bypassed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, just one is enough. But I do have other concerns.

@meeque
Copy link
Author

meeque commented Feb 28, 2019

Currently, this PR leaves SubTypeValidator itself untouched, but allows users to hook custom sub-classes into the DeserializerFactory, and ultimately into the ObjectMapper.

It might be desirable to convert SubTypeValidator to an interface instead. The current implementation could be moved into a new class, e.g. DefaultSubTypeValidator.

API compatibility could be preserved for most part. All public methods would work as previously. However, SubTypeValidator has a protected constructor, which would get lost, if converted to an interface. Developers, who have already sub-classed SubTypeValidator would need to sub-class a different class instead.


/*
/**********************************************************
/* Helper classes (beans)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have created helper beans for this test from scratch.
Could I reuse existing beans that make use of @JsonTypeInfo? Where to find these?

}
}

static class FriendlyAnimalValidator
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validator combines the black-list approach from the default SubTypeValidator with a white-list approach for one specific part of the type hierarchy.

Ultimately, it is the goal of #2208 to make the black-list from SubTypeValidator extensible. This is not demonstrated here. But I could add a suitable SubTypeValidator sub-class to this PR, if desired? Or, leave that exercise to the users of Jackson databind?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fyi, I've added separate class ExtensibleSubTypeValidator to this PR now, which addresses extensibility of the default black-list. Tests for that are below...


/*
/**********************************************************
/* Unit tests
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left the number of assertions at a minimum. Ultimately, I focused on testing whether a specific validator will reject inputs or not. I can certainly add more assertions on the deserialized data?

mapper.readValue(PETTING_ZOO_JSON, Zoo.class);
}

public void testDenyAllValidator() throws Exception
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each test in this class currently test 3 different JSON inputs. Should I rather split each test into 3 separate ones?

/**********************************************************
*/

protected ObjectMapper objectMapper(SubTypeValidator validator)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fyi, this factory method demonstrates how an ObjectMapper could be configured with a custom SubTypeValidator in real-life.

I chose to use sub-class JsonMapper, because ObjectMapper does not seem to be configurable enough. Could that limit the use of custom SubTypeValidators? Is there a way to use ObjectMapper directly, after all?

extends SubTypeValidator
{
private static Class<?>[] FRIENDLY_ANIMAL_WHITELIST = new Class<?>[] {
Animal.class,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally, I did not plan to include Animal itself here. It is an abstract class, and I always specify concrete sub-types in my JSON payloads.

However, during debugging I noticed that Animal itself sometimes gets passed as JavaType into SubTypeValidator.validateSubType(...). I could not figure out why yet. I fear it might be an unrelated bug in how the DeserializerFactory calls SubTypeValidator. If that were true, the existing black-list may not be effective in all situations!?! More likely, I have missed something trivial.

@cowtowncoder
Copy link
Member

Ok. So, there are 2 main concerns:

  1. Configurability of helper objects with 2.10 and 3.x needs to go mostly via ObjectMapper.Builder (for things that can not be changed on per-call basis); it would then be used to construct DeserializationConfig. But more importantly...
  2. I do not think SubTypeValidator, as added, should be an extension point at all. It was not designed for extension.

So, while I appreciate help, I plan on proceeding with #2195, to rewrite this functionality.
With that it is possibly to consider what to do with SubTypeValidator that currently exists. Perhaps it can remain as baseline check so it will not even be possible to use those classes; or maybe its functionality would be moved to the new class, for default implementation.

@meeque
Copy link
Author

meeque commented Mar 1, 2019

Thanks for the quick feedback, @cowtowncoder!

Regarding 1.)

I could not find ObjectMapper.Builder on master. Have I started off the wrong branch?

There are ObjectMapper.PrivateBuilder and its super class MapperBuilder though. Did you mean one of those? I could certainly try plugging into Mapper Builder...

Regarding 2.)

I'm a little confused now. I've read #2195 and what you said there:

I also think that name SubTypeValidator could be reused in 2.10, as long as we move it to a new package.

Did you mean the name only? Or could we reuse the signature of its public method, too?

I know that signature may not be ideal. (E.g., I think validation result could be returned or thrown, rather than set on the given DeserializationContext.) However, I wanted to change as little as possible in this PR. (My hidden agenda is to eventually backport this to 1.9.*.)

Where would you put that new interface? How would you adjust the signature?

…implementation: ExtensibleSubTypeValidator.
@meeque
Copy link
Author

meeque commented Mar 1, 2019

One more thing regarding 2.)

I had already experimented with extracting an interface from SubTypeValidator. I've added these changes to the PR now.

The solution is rather crude, but afaikt it fully preserves backward compatibility. The interface and the existing implementation both have the same name, but different packages.

@meeque
Copy link
Author

meeque commented Mar 25, 2019

Hello @cowtowncoder,
This has been stale for a while. Is there anything that I could do to turn this into a valuable contribution? If not, feel free to reject this PR. Otherwise, please give me more guidance. E.g., how can I align this better with #2195?

@cowtowncoder
Copy link
Member

So, similar to notes I added on #2208 itself, I do not actually think there is future for this half-thought theoretical extension point. I am sorry for this ending wasted time for you.
I do plan on implementing #2195 before 2.10 can be released, but my time has been and will be quite limited on Jackson things -- current blocker is JDK9+ module info addition, which is mostly done but not completely. It will go in 2.10 and is the last significant work before tackling this problem.

@meeque
Copy link
Author

meeque commented Apr 18, 2019

OK, no worries. I still learned a lot about Jackson while working on this.

In particular, it taught me how to extend the SubTypeValidator and plug it back into a BeanDeserializerFactory and ultimately into an ObjectMapper. I was even able to apply that solution to Jackson 1.9.*, which helps us maintaining a custom deserialization blacklist for some legacy projects.

However, you're right that all of that is indeed half-thought. Thus, I'm closing this PR now.

@cowtowncoder
Copy link
Member

@meeque Ok glad there was some benefit at least.

On plus side, I started doing basic scaffolding for the main ticket last night. Baby steps, but getting there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants