diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java new file mode 100644 index 0000000000..3f36df6f39 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java @@ -0,0 +1,94 @@ +package com.fasterxml.jackson.databind.jsontype; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; + +/** + * Interface for classes that handle validation of class name-based subtypes used + * with Polymorphic Deserialization: both via "default typing" and explicit + * {@code @JsonTypeInfo} when using class name as Type Identifier. + * The main purpose, initially, is to allow pluggable allow/deny lists to avoid + * security problems that occur with unlimited class names + * (See + * this article for full explanation). + *

+ * Notes on implementations: implementations must be thread-safe and shareable (usually meaning they + * are stateless). Determinations for validity are usually effectively cached on per-property + * basis (by virtue of subtype deserializers being cached by polymorphic deserializers) so + * caching at validator level is usually not needed. If caching is used, however, it must be done + * in thread-safe manner as validators are shared within {@link ObjectMapper} as well as possible + * across mappers (in case of default/standard validator). + * + * @since 2.10 + */ +public abstract class PolymorphicTypeValidator +{ + /** + * Definition of return values to indicate determination regarding validity. + */ + public enum Validity { + /** + * Value that indicates that Class name or Class is allowed for use without further checking + */ + ALLOWED, + /** + * Value that indicates that Class name or Class is NOT allowed and no further checks are + * needed or allowed + */ + DENIED, + + /** + * Value that indicates that Class name or Class validity can not be confirmed by validator + * and further checks are needed. + *

+ * Typically if validator can not establish validity from Type Id or Class (name), eventual + * determination will be {@code DENIED}, for safety reasons. + */ + INDETERMINATE + ; + } + + /** + * Method called after intended class name for subtype has been read (and in case of minimal + * class name, expanded to fully-qualified class name) but before attempt is made to + * look up actual {@link java.lang.Class} or {@link JavaType}. + * Validator may be able to + * determine validity of eventual type (and return {@link Validity#ALLOWED} or + * {@link Validity#DENIED}) or, if not able to, can defer validation to actual + * resolved type by returning {@link Validity#INDETERMINATE}. + *

+ * Validator may also choose to indicate denial by throwing a {@link JsonMappingException} + * (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException}) + * + * @param ctxt Context for resolution: typically will be {@code DeserializationContext} + * @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances + * of this type and assignment compatibility is verified by Jackson core + * @param subClassName Name of class that will be resolved to {@link java.lang.Class} if + * (and only if) validity check is not denied. + * + * @return Determination of validity of given class name, as a subtype of given base type: + * should NOT return {@code null} + */ + public abstract Validity validateSubClassName(MapperConfig ctxt, JavaType baseType, + String subClassName) throws JsonMappingException; + + /** + * Method called after class name has been resolved to actual type, in cases where previous + * call to {@link #validateSubClassName} returned {@link Validity#INDETERMINATE}. + * Validator should be able to determine validity and return appropriate {@link Validity} + * value, although it may also + *

+ * Validator may also choose to indicate denial by throwing a {@link JsonMappingException} + * (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException}) + * + * @param ctxt Context for resolution: typically will be {@code DeserializationContext} + * @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances + * of this type and assignment compatibility has been verified by Jackson core + * @param subType Resolved subtype to validate + * + * @return Determination of validity of given class name, as a subtype of given base type: + * should NOT return {@code null} + */ + public abstract Validity validateSubType(MapperConfig ctxt, JavaType baseType, + JavaType subType) throws JsonMappingException; +} diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java index 58f1a5f9a4..9e51d905f1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java @@ -133,8 +133,8 @@ public String toString() sb.append('[').append(getClass().getName()); sb.append("; base-type:").append(_baseType); sb.append("; id-resolver: ").append(_idResolver); - sb.append(']'); - return sb.toString(); + sb.append(']'); + return sb.toString(); } /* @@ -242,14 +242,13 @@ protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationCont * * @since 2.4 */ - protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt, Object typeId) + protected Object _deserializeWithNativeTypeId(JsonParser p, DeserializationContext ctxt, Object typeId) throws IOException { JsonDeserializer deser; if (typeId == null) { - /* 04-May-2014, tatu: Should error be obligatory, or should there be another method - * for "try to deserialize with native tpye id"? - */ + // 04-May-2014, tatu: Should error be obligatory, or should there be another method + // for "try to deserialize with native type id"? deser = _findDefaultImplDeserializer(ctxt); if (deser == null) { return ctxt.reportInputMismatch(baseType(), @@ -259,7 +258,7 @@ protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationCont String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId); deser = _findDeserializer(ctxt, typeIdStr); } - return deser.deserialize(jp, ctxt); + return deser.deserialize(p, ctxt); } /** diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java index d607fbf07b..d192ebd08e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java @@ -113,9 +113,8 @@ protected String idFromClass(Class clazz) @Override public String idFromValueAndType(Object value, Class type) { - /* 18-Jan-2013, tatu: We may be called with null value occasionally - * it seems; nothing much we can figure out that way. - */ + // 18-Jan-2013, tatu: We may be called with null value occasionally + // it seems; nothing much we can figure out that way. if (value == null) { return idFromClass(type); } @@ -128,11 +127,10 @@ public JavaType typeFromId(DatabindContext context, String id) { } protected JavaType _typeFromId(String id) { - /* Now: if no type is found, should we try to locate it by - * some other means? (specifically, if in same package as base type, - * could just try Class.forName) - * For now let's not add any such workarounds; can add if need be - */ + // Now: if no type is found, should we try to locate it by + // some other means? (specifically, if in same package as base type, + // could just try Class.forName) + // For now let's not add any such workarounds; can add if need be return _idToType.get(id); }