Skip to content

Commit 1135b2c

Browse files
committed
Start work on #2195: add tentative API as PolymorphicTypeValidator
1 parent 3ebfcd1 commit 1135b2c

File tree

3 files changed

+106
-15
lines changed

3 files changed

+106
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.fasterxml.jackson.databind.jsontype;
2+
3+
import com.fasterxml.jackson.databind.*;
4+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
5+
6+
/**
7+
* Interface for classes that handle validation of class name-based subtypes used
8+
* with Polymorphic Deserialization: both via "default typing" and explicit
9+
* {@code @JsonTypeInfo} when using class name as Type Identifier.
10+
* The main purpose, initially, is to allow pluggable allow/deny lists to avoid
11+
* security problems that occur with unlimited class names
12+
* (See <a href="https://medium.com/@cowtowncoder/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062">
13+
* this article</a> for full explanation).
14+
*<p>
15+
* Notes on implementations: implementations must be thread-safe and shareable (usually meaning they
16+
* are stateless). Determinations for validity are usually effectively cached on per-property
17+
* basis (by virtue of subtype deserializers being cached by polymorphic deserializers) so
18+
* caching at validator level is usually not needed. If caching is used, however, it must be done
19+
* in thread-safe manner as validators are shared within {@link ObjectMapper} as well as possible
20+
* across mappers (in case of default/standard validator).
21+
*
22+
* @since 2.10
23+
*/
24+
public abstract class PolymorphicTypeValidator
25+
{
26+
/**
27+
* Definition of return values to indicate determination regarding validity.
28+
*/
29+
public enum Validity {
30+
/**
31+
* Value that indicates that Class name or Class is allowed for use without further checking
32+
*/
33+
ALLOWED,
34+
/**
35+
* Value that indicates that Class name or Class is NOT allowed and no further checks are
36+
* needed or allowed
37+
*/
38+
DENIED,
39+
40+
/**
41+
* Value that indicates that Class name or Class validity can not be confirmed by validator
42+
* and further checks are needed.
43+
*<p>
44+
* Typically if validator can not establish validity from Type Id or Class (name), eventual
45+
* determination will be {@code DENIED}, for safety reasons.
46+
*/
47+
INDETERMINATE
48+
;
49+
}
50+
51+
/**
52+
* Method called after intended class name for subtype has been read (and in case of minimal
53+
* class name, expanded to fully-qualified class name) but before attempt is made to
54+
* look up actual {@link java.lang.Class} or {@link JavaType}.
55+
* Validator may be able to
56+
* determine validity of eventual type (and return {@link Validity#ALLOWED} or
57+
* {@link Validity#DENIED}) or, if not able to, can defer validation to actual
58+
* resolved type by returning {@link Validity#INDETERMINATE}.
59+
*<p>
60+
* Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
61+
* (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
62+
*
63+
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
64+
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
65+
* of this type and assignment compatibility is verified by Jackson core
66+
* @param subClassName Name of class that will be resolved to {@link java.lang.Class} if
67+
* (and only if) validity check is not denied.
68+
*
69+
* @return Determination of validity of given class name, as a subtype of given base type:
70+
* should NOT return {@code null}
71+
*/
72+
public abstract Validity validateSubClassName(MapperConfig<?> ctxt, JavaType baseType,
73+
String subClassName) throws JsonMappingException;
74+
75+
/**
76+
* Method called after class name has been resolved to actual type, in cases where previous
77+
* call to {@link #validateSubClassName} returned {@link Validity#INDETERMINATE}.
78+
* Validator should be able to determine validity and return appropriate {@link Validity}
79+
* value, although it may also
80+
*<p>
81+
* Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
82+
* (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
83+
*
84+
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
85+
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
86+
* of this type and assignment compatibility has been verified by Jackson core
87+
* @param subType Resolved subtype to validate
88+
*
89+
* @return Determination of validity of given class name, as a subtype of given base type:
90+
* should NOT return {@code null}
91+
*/
92+
public abstract Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
93+
JavaType subType) throws JsonMappingException;
94+
}

src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ public String toString()
133133
sb.append('[').append(getClass().getName());
134134
sb.append("; base-type:").append(_baseType);
135135
sb.append("; id-resolver: ").append(_idResolver);
136-
sb.append(']');
137-
return sb.toString();
136+
sb.append(']');
137+
return sb.toString();
138138
}
139139

140140
/*
@@ -242,14 +242,13 @@ protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationCont
242242
*
243243
* @since 2.4
244244
*/
245-
protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt, Object typeId)
245+
protected Object _deserializeWithNativeTypeId(JsonParser p, DeserializationContext ctxt, Object typeId)
246246
throws IOException
247247
{
248248
JsonDeserializer<Object> deser;
249249
if (typeId == null) {
250-
/* 04-May-2014, tatu: Should error be obligatory, or should there be another method
251-
* for "try to deserialize with native tpye id"?
252-
*/
250+
// 04-May-2014, tatu: Should error be obligatory, or should there be another method
251+
// for "try to deserialize with native type id"?
253252
deser = _findDefaultImplDeserializer(ctxt);
254253
if (deser == null) {
255254
return ctxt.reportInputMismatch(baseType(),
@@ -259,7 +258,7 @@ protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationCont
259258
String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId);
260259
deser = _findDeserializer(ctxt, typeIdStr);
261260
}
262-
return deser.deserialize(jp, ctxt);
261+
return deser.deserialize(p, ctxt);
263262
}
264263

265264
/**

src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java

+6-8
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ protected String idFromClass(Class<?> clazz)
113113

114114
@Override
115115
public String idFromValueAndType(Object value, Class<?> type) {
116-
/* 18-Jan-2013, tatu: We may be called with null value occasionally
117-
* it seems; nothing much we can figure out that way.
118-
*/
116+
// 18-Jan-2013, tatu: We may be called with null value occasionally
117+
// it seems; nothing much we can figure out that way.
119118
if (value == null) {
120119
return idFromClass(type);
121120
}
@@ -128,11 +127,10 @@ public JavaType typeFromId(DatabindContext context, String id) {
128127
}
129128

130129
protected JavaType _typeFromId(String id) {
131-
/* Now: if no type is found, should we try to locate it by
132-
* some other means? (specifically, if in same package as base type,
133-
* could just try Class.forName)
134-
* For now let's not add any such workarounds; can add if need be
135-
*/
130+
// Now: if no type is found, should we try to locate it by
131+
// some other means? (specifically, if in same package as base type,
132+
// could just try Class.forName)
133+
// For now let's not add any such workarounds; can add if need be
136134
return _idToType.get(id);
137135
}
138136

0 commit comments

Comments
 (0)