diff --git a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java index dd32007b8b..d96766b378 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java @@ -251,6 +251,7 @@ public JavaType resolveAndValidateSubType(JavaType baseType, String subClass, return _throwNotASubtype(baseType, subClass); } final JavaType subType = config.getTypeFactory().constructSpecializedType(baseType, cls); + // May skip check if type was allowed by subclass name already if (vld != Validity.ALLOWED) { if (ptv.validateSubType(config, baseType, subType) != Validity.ALLOWED) { return _throwSubtypeClassNotAllowed(baseType, subClass, ptv); diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 9df0c509fd..c1bc87cb42 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -319,7 +319,13 @@ protected final static class LaissezFaireValidator private static final long serialVersionUID = 1L; public final static LaissezFaireValidator instance = new LaissezFaireValidator(); - + + @Override + public Validity validateBaseType(MapperConfig> ctxt, JavaType baseType) + throws JsonMappingException { + return Validity.INDETERMINATE; + } + @Override public Validity validateSubClassName(MapperConfig> ctxt, JavaType baseType, String subClassName) { diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/BasicPolymorphicTypeValidator.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/BasicPolymorphicTypeValidator.java index ac0ef779f9..cd1299314a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/BasicPolymorphicTypeValidator.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/BasicPolymorphicTypeValidator.java @@ -228,6 +228,12 @@ public static Builder builder() { return new Builder(); } + // !!! TODO + @Override + public Validity validateBaseType(MapperConfig> ctxt, JavaType baseType) { + return Validity.INDETERMINATE; + } + @Override public Validity validateSubClassName(MapperConfig> ctxt, JavaType baseType, String subClassName) throws JsonMappingException diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java index 59ab342d97..870ae9ff81 100644 --- a/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java +++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java @@ -6,12 +6,14 @@ /** * 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. + * {@code @JsonTypeInfo} when using Java 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). *
+ * Call fal + *
* 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
@@ -51,6 +53,27 @@ public enum Validity {
;
}
+ /**
+ * Method called when a property with polymorphic value is encountered, and a
+ * {@code TypeResolverBuilder} is needed. Intent is to allow early determination
+ * of cases where subtyping is completely denied (for example for security reasons),
+ * or, conversely, allowed for allow subtypes (when base type guarantees that all subtypes
+ * are known to be safe). Check can be thought of as both optimization (for latter case)
+ * and eager-fail (for former case) to give better feedback.
+ *
+ * @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
+ *
+ * @return Determination of general validity of all subtypes of given base type; if
+ * {@link Validity#ALLOWED} returned, all subtypes will automatically be accepted without
+ * further checks; is {@link Validity#DENIED} returned no subtyping allowed at all
+ * (caller will usually throw an exception); otherwise (return {@link Validity#INDETERMINATE})
+ * per sub-type validation calls are made for each new subclass encountered.
+ */
+ public abstract Validity validateBaseType(MapperConfig> ctxt, JavaType baseType)
+ throws JsonMappingException;
+
/**
* 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
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
index 0047b24a46..c0457513e7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
@@ -20,28 +20,14 @@ public class ClassNameIdResolver
{
private final static String JAVA_UTIL_PKG = "java.util.";
- /**
- * @since 2.10
- */
protected final PolymorphicTypeValidator _subTypeValidator;
- /**
- * @deprecated since 2.10
- */
- @Deprecated
- public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) {
- this(baseType, typeFactory, null);
- }
-
public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory,
PolymorphicTypeValidator ptv) {
super(baseType, typeFactory);
_subTypeValidator = ptv;
}
- /**
- * @since 2.10
- */
public static ClassNameIdResolver construct(JavaType baseType, MapperConfig> config,
PolymorphicTypeValidator ptv) {
return new ClassNameIdResolver(baseType, config.getTypeFactory(), ptv);
@@ -72,7 +58,7 @@ public JavaType typeFromId(DatabindContext context, String id) throws IOExceptio
protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException
{
// 24-Apr-2019, tatu: [databind#2195] validate as well as resolve:
- JavaType t = ctxt.resolveAndValidateSubType(_baseType, id);
+ JavaType t = ctxt.resolveAndValidateSubType(_baseType, id, _subTypeValidator);
if (t == null) {
if (ctxt instanceof DeserializationContext) {
// First: we may have problem handlers that can deal with it?
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
index 649dfc42ab..dc7dfb39ea 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
@@ -39,9 +39,6 @@ protected MinimalClassNameIdResolver(JavaType baseType, TypeFactory typeFactory,
}
}
- /**
- * @since 2.10
- */
public static MinimalClassNameIdResolver construct(JavaType baseType, MapperConfig> config,
PolymorphicTypeValidator ptv) {
return new MinimalClassNameIdResolver(baseType, config.getTypeFactory(), ptv);
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
index b9dd513a51..cf8c4d241b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
@@ -84,7 +84,8 @@ public TypeSerializer buildTypeSerializer(SerializationConfig config,
if (baseType.isPrimitive()) {
return null;
}
- TypeIdResolver idRes = idResolver(config, baseType, subtypes, true, false);
+ TypeIdResolver idRes = idResolver(config, baseType, subTypeValidator(config),
+ subtypes, true, false);
switch (_includeAs) {
case WRAPPER_ARRAY:
return new AsArrayTypeSerializer(idRes, null);
@@ -118,7 +119,11 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
return null;
}
- TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
+ // 27-Apr-2019, tatu: Part of [databind#2195]; must first check whether any subtypes
+ // of basetypes might be denied or allowed
+ final PolymorphicTypeValidator subTypeValidator = verifyBaseTypeValidity(config, baseType);
+
+ TypeIdResolver idRes = idResolver(config, baseType, subTypeValidator, subtypes, false, true);
JavaType defaultImpl = defineDefaultImpl(config, baseType);
@@ -236,10 +241,10 @@ public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) {
public String getTypeProperty() { return _typeProperty; }
public boolean isTypeIdVisible() { return _typeIdVisible; }
-
+
/*
/**********************************************************
- /* Internal/subtype methods
+ /* Internal/subtype factory methods
/**********************************************************
*/
@@ -249,16 +254,17 @@ public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) {
* given configuration.
*/
protected TypeIdResolver idResolver(MapperConfig> config,
- JavaType baseType, Collection