From 8b7f77276144ef671726cf9bcb3882610f9a4e3f Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 29 Jan 2018 12:54:33 -0600 Subject: [PATCH] Update to Jackson 2.9.4 Fixes: gh-4985 --- .../CasAuthenticationTokenMixinTests.java | 2 +- .../security/jackson2/CoreJackson2Module.java | 1 + .../jackson2/SecurityJackson2Modules.java | 3 + .../UnmodifiableListDeserializer.java | 61 +++++++++++++++++++ .../jackson2/UnmodifiableListMixin.java | 50 +++++++++++++++ .../SimpleGrantedAuthorityMixinTests.java | 6 +- ...PasswordAuthenticationTokenMixinTests.java | 2 +- gradle/dependency-management.gradle | 6 +- .../web/savedrequest/DefaultSavedRequest.java | 9 ++- 9 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java create mode 100644 core/src/main/java/org/springframework/security/jackson2/UnmodifiableListMixin.java diff --git a/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java b/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java index 56b618ca30..bd66b6cf67 100644 --- a/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java +++ b/cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java @@ -55,7 +55,7 @@ public class CasAuthenticationTokenMixinTests { public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON + "]]"; - public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", [" + AUTHORITY_JSON + "]]"; + public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" + AUTHORITY_JSON + "]]"; // @formatter:off public static final String USER_JSON = "{" diff --git a/core/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java b/core/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java index b21de20bfa..4dda441791 100644 --- a/core/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java +++ b/core/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java @@ -58,6 +58,7 @@ public class CoreJackson2Module extends SimpleModule { context.setMixInAnnotations(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class); context.setMixInAnnotations(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class); context.setMixInAnnotations(Collections.unmodifiableSet(Collections.emptySet()).getClass(), UnmodifiableSetMixin.class); + context.setMixInAnnotations(Collections.unmodifiableList(Collections.emptyList()).getClass(), UnmodifiableListMixin.class); context.setMixInAnnotations(User.class, UserMixin.class); context.setMixInAnnotations(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class); } diff --git a/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java b/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java index 57ddbf9086..13eafbbf81 100644 --- a/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java +++ b/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java @@ -142,7 +142,10 @@ public final class SecurityJackson2Modules { static class WhitelistTypeIdResolver implements TypeIdResolver { private static final Set WHITELIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList( "java.util.ArrayList", + "java.util.Collections$EmptyList", "java.util.Collections$EmptyMap", + "java.util.Collections$UnmodifiableRandomAccessList", + "java.util.Collections$SingletonList", "java.util.Date", "java.util.TreeMap", "java.util.HashMap", diff --git a/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java new file mode 100644 index 0000000000..fd0937d2df --- /dev/null +++ b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.jackson2; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Custom deserializer for {@link UnmodifiableListDeserializer}. + * + * @author Rob Winch + * @see UnmodifiableListMixin + * @since 5.0.2 + */ +class UnmodifiableListDeserializer extends JsonDeserializer { + + @Override + public List deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { + ObjectMapper mapper = (ObjectMapper) jp.getCodec(); + JsonNode node = mapper.readTree(jp); + List result = new ArrayList(); + if (node != null) { + if (node instanceof ArrayNode) { + ArrayNode arrayNode = (ArrayNode) node; + Iterator nodeIterator = arrayNode.iterator(); + while (nodeIterator.hasNext()) { + JsonNode elementNode = nodeIterator.next(); + result.add(mapper.readValue(elementNode.traverse(mapper), Object.class)); + } + } else { + result.add(mapper.readValue(node.traverse(mapper), Object.class)); + } + } + return Collections.unmodifiableList(result); + } +} diff --git a/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListMixin.java b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListMixin.java new file mode 100644 index 0000000000..baebb40707 --- /dev/null +++ b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListMixin.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.jackson2; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.util.Set; + +/** + * This mixin class used to deserialize java.util.Collections$UnmodifiableRandomAccessList + * and used with various AuthenticationToken implementation's mixin classes. + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * + * @author Rob Winch + * @see UnmodifiableListDeserializer + * @see CoreJackson2Module + * @see SecurityJackson2Modules + * @since 5.0.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonDeserialize(using = UnmodifiableListDeserializer.class) +class UnmodifiableListMixin { + + /** + * Mixin Constructor + * @param s the Set + */ + @JsonCreator + UnmodifiableListMixin(Set s) {} +} diff --git a/core/src/test/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixinTests.java b/core/src/test/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixinTests.java index a5e11f2e9b..9c6e57e527 100644 --- a/core/src/test/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixinTests.java +++ b/core/src/test/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixinTests.java @@ -36,11 +36,13 @@ public class SimpleGrantedAuthorityMixinTests extends AbstractMixinTests { // @formatter:off public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}"; - public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", [" + AUTHORITY_JSON + "]]"; + public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" + AUTHORITY_JSON + "]]"; public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON + "]]"; - public static final String NO_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", []]"; + public static final String NO_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]"; + + public static final String EMPTY_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$EmptyList\", []]"; public static final String NO_AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", []]"; // @formatter:on diff --git a/core/src/test/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixinTests.java b/core/src/test/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixinTests.java index 5ed28285b7..dbea50ce3b 100644 --- a/core/src/test/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixinTests.java +++ b/core/src/test/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixinTests.java @@ -69,7 +69,7 @@ public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixin // @formatter:off private static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON .replace("\"authenticated\": true, ", "\"authenticated\": false, ") - .replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON, SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON); + .replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON, SimpleGrantedAuthorityMixinTests.EMPTY_AUTHORITIES_ARRAYLIST_JSON); // @formatter:on @Test diff --git a/gradle/dependency-management.gradle b/gradle/dependency-management.gradle index 99e48ef15f..c2ab483066 100644 --- a/gradle/dependency-management.gradle +++ b/gradle/dependency-management.gradle @@ -28,9 +28,9 @@ dependencyManagement { dependency 'asm:asm:3.1' dependency 'ch.qos.logback:logback-classic:1.2.3' dependency 'ch.qos.logback:logback-core:1.2.3' - dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.0' - dependency 'com.fasterxml.jackson.core:jackson-core:2.9.2' - dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.2' + dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.4' + dependency 'com.fasterxml.jackson.core:jackson-core:2.9.4' + dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.4' dependency 'com.fasterxml:classmate:1.3.4' dependency 'com.github.stephenc.jcip:jcip-annotations:1.0-1' dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.60' diff --git a/web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java b/web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java index 7139f0086b..93280aa734 100644 --- a/web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java +++ b/web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java @@ -503,8 +503,13 @@ public class DefaultSavedRequest implements SavedRequest { this.headers.remove(HEADER_IF_MODIFIED_SINCE); this.headers.remove(HEADER_IF_NONE_MATCH); - if (!ObjectUtils.isEmpty(this.headers)) - savedRequest.headers.putAll(this.headers); + for (Map.Entry> entry : this.headers.entrySet()) { + String headerName = entry.getKey(); + List headerValues = entry.getValue(); + for (String headerValue : headerValues) { + savedRequest.addHeader(headerName, headerValue); + } + } return savedRequest; } }