From 6c3d183a94196f1f6c320d29f70e022f38195177 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Tue, 1 Mar 2022 12:02:55 -0700 Subject: [PATCH] Polish Saml2 Jackson Support Issue gh-10905 --- .../UnmodifiableMapDeserializerTests.java | 2 +- ...ml2AuthenticatedPrincipalDeserializer.java | 66 ----------------- ...faultSaml2AuthenticatedPrincipalMixin.java | 17 ++++- .../saml2/jackson2/JsonNodeUtils.java | 50 ------------- .../Saml2AuthenticationDeserializer.java | 71 ------------------- .../jackson2/Saml2AuthenticationMixin.java | 20 ++++-- .../saml2/jackson2/TestSaml2JsonPayloads.java | 1 - 7 files changed, 32 insertions(+), 195 deletions(-) delete mode 100644 saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalDeserializer.java delete mode 100644 saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/JsonNodeUtils.java delete mode 100644 saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationDeserializer.java diff --git a/core/src/test/java/org/springframework/security/jackson2/UnmodifiableMapDeserializerTests.java b/core/src/test/java/org/springframework/security/jackson2/UnmodifiableMapDeserializerTests.java index 066ac68bd6..9666b8a9e9 100644 --- a/core/src/test/java/org/springframework/security/jackson2/UnmodifiableMapDeserializerTests.java +++ b/core/src/test/java/org/springframework/security/jackson2/UnmodifiableMapDeserializerTests.java @@ -47,7 +47,7 @@ class UnmodifiableMapDeserializerTests extends AbstractMixinTests { Collections.unmodifiableMap(Collections.emptyMap()).getClass()); assertThat(map).isNotNull().isInstanceOf(Collections.unmodifiableMap(Collections.emptyMap()).getClass()) - .containsAllEntriesOf(Map.of("Key", "Value")); + .containsAllEntriesOf(Collections.singletonMap("Key", "Value")); } } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalDeserializer.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalDeserializer.java deleted file mode 100644 index 7bc017a972..0000000000 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalDeserializer.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2022 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 - * - * https://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.saml2.jackson2; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.type.TypeReference; -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 org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal; - -/** - * Custom deserializer for {@link DefaultSaml2AuthenticatedPrincipal}. - * - * @author Ulrich Grave - * @since 5.7 - * @see DefaultSaml2AuthenticatedPrincipalMixin - */ -class DefaultSaml2AuthenticatedPrincipalDeserializer extends JsonDeserializer { - - private static final TypeReference> SESSION_INDICES_LIST = new TypeReference>() { - }; - - private static final TypeReference>> ATTRIBUTES_MAP = new TypeReference>>() { - }; - - @Override - public DefaultSaml2AuthenticatedPrincipal deserialize(JsonParser jp, DeserializationContext ctxt) - throws IOException { - ObjectMapper mapper = (ObjectMapper) jp.getCodec(); - JsonNode jsonNode = mapper.readTree(jp); - - String name = JsonNodeUtils.findStringValue(jsonNode, "name"); - Map> attributes = JsonNodeUtils.findValue(jsonNode, "attributes", ATTRIBUTES_MAP, mapper); - List sessionIndexes = JsonNodeUtils.findValue(jsonNode, "sessionIndexes", SESSION_INDICES_LIST, mapper); - String registrationId = JsonNodeUtils.findStringValue(jsonNode, "registrationId"); - - DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(name, attributes, - sessionIndexes); - if (registrationId != null) { - principal.setRelyingPartyRegistrationId(registrationId); - } - return principal; - } - -} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalMixin.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalMixin.java index 80b04e5365..ca909bcc92 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalMixin.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalMixin.java @@ -16,9 +16,13 @@ package org.springframework.security.saml2.jackson2; +import java.util.List; +import java.util.Map; + import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal; @@ -40,7 +44,16 @@ import org.springframework.security.saml2.provider.service.authentication.Defaul */ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) -@JsonDeserialize(using = DefaultSaml2AuthenticatedPrincipalDeserializer.class) +@JsonIgnoreProperties(ignoreUnknown = true) class DefaultSaml2AuthenticatedPrincipalMixin { + @JsonProperty("registrationId") + String registrationId; + + DefaultSaml2AuthenticatedPrincipalMixin(@JsonProperty("name") String name, + @JsonProperty("attributes") Map> attributes, + @JsonProperty("sessionIndexes") List sessionIndexes) { + + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/JsonNodeUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/JsonNodeUtils.java deleted file mode 100644 index e91377bfe6..0000000000 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/JsonNodeUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2022 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 - * - * https://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.saml2.jackson2; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.MissingNode; - -final class JsonNodeUtils { - - private JsonNodeUtils() { - } - - static String findStringValue(JsonNode jsonNode, String fieldName) { - if (jsonNode == null) { - return null; - } - JsonNode value = jsonNode.findValue(fieldName); - return (value != null && value.isTextual()) ? value.asText() : null; - } - - static T findValue(JsonNode jsonNode, String fieldName, TypeReference valueTypeReference, - ObjectMapper mapper) { - if (jsonNode == null) { - return null; - } - JsonNode value = jsonNode.findValue(fieldName); - return (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null; - } - - static JsonNode readJsonNode(JsonNode jsonNode, String field) { - return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance(); - } - -} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationDeserializer.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationDeserializer.java deleted file mode 100644 index 6f82fb75e4..0000000000 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationDeserializer.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2002-2022 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 - * - * https://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.saml2.jackson2; - -import java.io.IOException; -import java.util.List; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.type.TypeReference; -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 org.springframework.security.core.AuthenticatedPrincipal; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; - -/** - * Custom deserializer for {@link Saml2Authentication}. - * - * @author Ulrich Grave - * @since 5.7 - * @see Saml2AuthenticationMixin - */ -class Saml2AuthenticationDeserializer extends JsonDeserializer { - - private static final TypeReference> GRANTED_AUTHORITY_LIST = new TypeReference>() { - }; - - private static final TypeReference OBJECT = new TypeReference() { - }; - - @Override - public Saml2Authentication deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - ObjectMapper mapper = (ObjectMapper) jp.getCodec(); - JsonNode jsonNode = mapper.readTree(jp); - - boolean authenticated = JsonNodeUtils.readJsonNode(jsonNode, "authenticated").asBoolean(); - JsonNode principalNode = JsonNodeUtils.readJsonNode(jsonNode, "principal"); - AuthenticatedPrincipal principal = getPrincipal(mapper, principalNode); - String saml2Response = JsonNodeUtils.findStringValue(jsonNode, "saml2Response"); - List authorities = JsonNodeUtils.findValue(jsonNode, "authorities", GRANTED_AUTHORITY_LIST, - mapper); - Object details = JsonNodeUtils.findValue(jsonNode, "details", OBJECT, mapper); - - Saml2Authentication authentication = new Saml2Authentication(principal, saml2Response, authorities); - authentication.setAuthenticated(authenticated); - authentication.setDetails(details); - return authentication; - } - - private AuthenticatedPrincipal getPrincipal(ObjectMapper mapper, JsonNode principalNode) throws IOException { - return mapper.readValue(principalNode.traverse(mapper), AuthenticatedPrincipal.class); - } - -} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationMixin.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationMixin.java index b74c618d9c..d8d936e4db 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationMixin.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationMixin.java @@ -16,10 +16,16 @@ package org.springframework.security.saml2.jackson2; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.Collection; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import org.springframework.security.core.AuthenticatedPrincipal; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; @@ -40,7 +46,13 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) -@JsonDeserialize(using = Saml2AuthenticationDeserializer.class) +@JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true) class Saml2AuthenticationMixin { + @JsonCreator + Saml2AuthenticationMixin(@JsonProperty("principal") AuthenticatedPrincipal principal, + @JsonProperty("saml2Response") String saml2Response, + @JsonProperty("authorities") Collection authorities) { + } + } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/TestSaml2JsonPayloads.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/TestSaml2JsonPayloads.java index d1e745ba5c..d0dd0deafe 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/TestSaml2JsonPayloads.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/TestSaml2JsonPayloads.java @@ -204,7 +204,6 @@ final class TestSaml2JsonPayloads { + " \"credentialsNonExpired\": true," + " \"enabled\": true" + " }," - + " \"authenticated\": true," + " \"principal\": " + DEFAULT_AUTHENTICATED_PRINCIPAL_JSON + "," + " \"saml2Response\": \"" + SAML_RESPONSE + "\"" + "}";