• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.jsontype.vld;
2 
3 import java.util.regex.Pattern;
4 
5 import com.fasterxml.jackson.databind.*;
6 import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
7 import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
8 import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
9 import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
10 import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
11 
12 /**
13  * Tests for the main user-configurable {@code PolymorphicTypeValidator},
14  * {@link BasicPolymorphicTypeValidator}.
15  */
16 public class BasicPTVTest extends BaseMapTest
17 {
18     // // // Value types
19 
20     static abstract class BaseValue {
21         public int x = 3;
22     }
23 
24     static class ValueA extends BaseValue {
ValueA()25         protected ValueA() { }
ValueA(int x)26         public ValueA(int x) {
27             super();
28             this.x = x;
29         }
30     }
31 
32     static class ValueB extends BaseValue {
ValueB()33         protected ValueB() { }
ValueB(int x)34         public ValueB(int x) {
35             super();
36             this.x = x;
37         }
38     }
39 
40     // // // Value types
41 
42     // make this type `final` to avoid polymorphic handling
43     static final class BaseValueWrapper {
44         public BaseValue value;
45 
BaseValueWrapper()46         protected BaseValueWrapper() { }
47 
withA(int x)48         public static BaseValueWrapper withA(int x) {
49             BaseValueWrapper w = new BaseValueWrapper();
50             w.value = new ValueA(x);
51             return w;
52         }
53 
withB(int x)54         public static BaseValueWrapper withB(int x) {
55             BaseValueWrapper w = new BaseValueWrapper();
56             w.value = new ValueB(x);
57             return w;
58         }
59     }
60 
61     static final class ObjectWrapper {
62         public Object value;
63 
ObjectWrapper()64         protected ObjectWrapper() { }
ObjectWrapper(Object v)65         public ObjectWrapper(Object v) { value = v; }
66     }
67 
68     static final class NumberWrapper {
69         public Number value;
70 
NumberWrapper()71         protected NumberWrapper() { }
NumberWrapper(Number v)72         public NumberWrapper(Number v) { value = v; }
73     }
74 
75     /*
76     /**********************************************************************
77     /* Test methods: by base type, pass
78     /**********************************************************************
79      */
80 
81     // First: test simple Base-type-as-class allowing
testAllowByBaseClass()82     public void testAllowByBaseClass() throws Exception {
83         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
84                 .allowIfBaseType(BaseValue.class)
85                 .build();
86         ObjectMapper mapper = jsonMapperBuilder()
87                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
88                 .build();
89 
90         // First, test accepted case
91         final String json = mapper.writeValueAsString(BaseValueWrapper.withA(42));
92         BaseValueWrapper w = mapper.readValue(json, BaseValueWrapper.class);
93         assertEquals(42, w.value.x);
94 
95         // then non-accepted
96         final String json2 = mapper.writeValueAsString(new NumberWrapper(Byte.valueOf((byte) 4)));
97         try {
98             mapper.readValue(json2, NumberWrapper.class);
99             fail("Should not pass");
100         } catch (InvalidTypeIdException e) {
101             verifyException(e, "Could not resolve type id 'java.lang.Byte'");
102             verifyException(e, "as a subtype of");
103         }
104 
105         // and then yet again accepted one with different config
106         ObjectMapper mapper2 = jsonMapperBuilder()
107                 .activateDefaultTyping(BasicPolymorphicTypeValidator.builder()
108                         .allowIfBaseType(Number.class)
109                         .build(), DefaultTyping.NON_FINAL)
110                 .build();
111         NumberWrapper nw = mapper2.readValue(json2, NumberWrapper.class);
112         assertNotNull(nw);
113         assertEquals(Byte.valueOf((byte) 4), nw.value);
114     }
115 
116     // Then subtype-prefix
testAllowByBaseClassPrefix()117     public void testAllowByBaseClassPrefix() throws Exception {
118         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
119                 .allowIfBaseType("com.fasterxml.")
120                 .build();
121         ObjectMapper mapper = jsonMapperBuilder()
122                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
123                 .build();
124 
125         // First, test accepted case
126         final String json = mapper.writeValueAsString(BaseValueWrapper.withA(42));
127         BaseValueWrapper w = mapper.readValue(json, BaseValueWrapper.class);
128         assertEquals(42, w.value.x);
129 
130         // then non-accepted
131         final String json2 = mapper.writeValueAsString(new NumberWrapper(Byte.valueOf((byte) 4)));
132         try {
133             mapper.readValue(json2, NumberWrapper.class);
134             fail("Should not pass");
135         } catch (InvalidTypeIdException e) {
136             verifyException(e, "Could not resolve type id 'java.lang.Byte'");
137             verifyException(e, "as a subtype of");
138         }
139     }
140 
141     // Then subtype-pattern
testAllowByBaseClassPattern()142     public void testAllowByBaseClassPattern() throws Exception {
143         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
144                 .allowIfBaseType(Pattern.compile("\\w+\\.fasterxml\\..+"))
145                 .build();
146         ObjectMapper mapper = jsonMapperBuilder()
147                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
148                 .build();
149 
150         // First, test accepted case
151         final String json = mapper.writeValueAsString(BaseValueWrapper.withA(42));
152         BaseValueWrapper w = mapper.readValue(json, BaseValueWrapper.class);
153         assertEquals(42, w.value.x);
154 
155         // then non-accepted
156         final String json2 = mapper.writeValueAsString(new NumberWrapper(Byte.valueOf((byte) 4)));
157         try {
158             mapper.readValue(json2, NumberWrapper.class);
159             fail("Should not pass");
160         } catch (InvalidTypeIdException e) {
161             verifyException(e, "Could not resolve type id 'java.lang.Byte'");
162             verifyException(e, "as a subtype of");
163         }
164     }
165 
166     // And finally, block by specific direct-match base type
testDenyByBaseClass()167     public void testDenyByBaseClass() throws Exception {
168         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
169                 // indicate that all subtypes `BaseValue` would be fine
170                 .allowIfBaseType(BaseValue.class)
171                 // but that nominal base type MUST NOT be `Object.class`
172                 .denyForExactBaseType(Object.class)
173                 .build();
174         ObjectMapper mapper = jsonMapperBuilder()
175                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
176                 .build();
177         final String json = mapper.writeValueAsString(new ObjectWrapper(new ValueA(15)));
178         try {
179             mapper.readValue(json, ObjectWrapper.class);
180             fail("Should not pass");
181 
182         // NOTE: different exception type since denial was for whole property, not just specific values
183         } catch (InvalidDefinitionException e) {
184             verifyException(e, "denied resolution of all subtypes of base type `java.lang.Object`");
185         }
186     }
187 
188     /*
189     /**********************************************************************
190     /* Test methods: by sub type
191     /**********************************************************************
192      */
193 
testAllowBySubClass()194     public void testAllowBySubClass() throws Exception {
195         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
196                 .allowIfSubType(ValueB.class)
197                 .build();
198         ObjectMapper mapper = jsonMapperBuilder()
199                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
200                 .build();
201 
202         // First, test accepted case
203         final String json = mapper.writeValueAsString(BaseValueWrapper.withB(42));
204         BaseValueWrapper w = mapper.readValue(json, BaseValueWrapper.class);
205         assertEquals(42, w.value.x);
206 
207         // then non-accepted
208         try {
209             mapper.readValue(mapper.writeValueAsString(BaseValueWrapper.withA(43)),
210                     BaseValueWrapper.class);
211             fail("Should not pass");
212         } catch (InvalidTypeIdException e) {
213             verifyException(e, "Could not resolve type id 'com.fasterxml.jackson.");
214             verifyException(e, "as a subtype of");
215         }
216     }
217 
testAllowBySubClassPrefix()218     public void testAllowBySubClassPrefix() throws Exception {
219         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
220                 .allowIfSubType(ValueB.class.getName())
221                 .build();
222         ObjectMapper mapper = jsonMapperBuilder()
223                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
224                 .build();
225 
226         // First, test accepted case
227         final String json = mapper.writeValueAsString(BaseValueWrapper.withB(42));
228         BaseValueWrapper w = mapper.readValue(json, BaseValueWrapper.class);
229         assertEquals(42, w.value.x);
230 
231         // then non-accepted
232         try {
233             mapper.readValue(mapper.writeValueAsString(BaseValueWrapper.withA(43)),
234                     BaseValueWrapper.class);
235             fail("Should not pass");
236         } catch (InvalidTypeIdException e) {
237             verifyException(e, "Could not resolve type id 'com.fasterxml.jackson.");
238             verifyException(e, "as a subtype of");
239         }
240     }
241 
testAllowBySubClassPattern()242     public void testAllowBySubClassPattern() throws Exception {
243         final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
244                 .allowIfSubType(Pattern.compile(Pattern.quote(ValueB.class.getName())))
245                 .build();
246         ObjectMapper mapper = jsonMapperBuilder()
247                 .activateDefaultTyping(ptv, DefaultTyping.NON_FINAL)
248                 .build();
249 
250         // First, test accepted case
251         final String json = mapper.writeValueAsString(BaseValueWrapper.withB(42));
252         BaseValueWrapper w = mapper.readValue(json, BaseValueWrapper.class);
253         assertEquals(42, w.value.x);
254 
255         // then non-accepted
256         try {
257             mapper.readValue(mapper.writeValueAsString(BaseValueWrapper.withA(43)),
258                     BaseValueWrapper.class);
259             fail("Should not pass");
260         } catch (InvalidTypeIdException e) {
261             verifyException(e, "Could not resolve type id 'com.fasterxml.jackson.");
262             verifyException(e, "as a subtype of");
263         }
264     }
265 }
266 
267 
268