Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jackson serialization of DefaultSaml2AuthenticatedPrincipal: LinkedMultiValueMap is not in the allowlist #11785

Closed
felixscheinost opened this issue Sep 6, 2022 · 2 comments
Assignees
Labels
in: saml2 An issue in SAML2 modules type: bug A general bug
Milestone

Comments

@felixscheinost
Copy link

Describe the bug

Jackson serialization of DefaultSaml2AuthenticatedPrincipal doesn't work anymore since Spring Boot 2.7.3.

An exception is thrown:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: The class with org.springframework.util.LinkedMultiValueMap and name of org.springframework.util.LinkedMultiValueMap is not in the allowlist.

To Reproduce

Setup a Jackson object mapper like that

    val springSecurityObjectMapper: JsonMapper = jacksonMapperBuilder()
        .addModules(SecurityJackson2Modules.getModules(Companion::class.java.classLoader))

Then try to use the mapper to serialize an Authentication containing a DefaultSaml2AuthenticatedPrincipal constructed by OpenSaml4AuthenticationProvider.

Expected behavior

Serialization works.

Probable cause

I think this is the offending commit e092ec7

@felixscheinost felixscheinost added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Sep 6, 2022
@ugrave
Copy link
Contributor

ugrave commented Sep 9, 2022

I get the same problem after updating from spring security 5.7.2 to 5.7.3.
Problem is the fixed already mention above. If i copy the attribute map and use a LinkedHashMap againt it works.

Note sure which could be the best fix.
Either add the LinkedMultiValueMap to the SecurityJackson2Modules as mixing or copy the attribute Map in the DefaultSaml2AuthenticatedPrincipal constructor to a fix map implementation which is serializable as json (ex . LinkedHashMap)

My current workaround is to copy the principal and overide the attirbute map in a custom ResponseAuthenticationConverter:

     Saml2Authentication authentication = ... 
     DefaultSaml2AuthenticatedPrincipal principal = (DefaultSaml2AuthenticatedPrincipal) authentication.getPrincipal();
     DefaultSaml2AuthenticatedPrincipal copy = new DefaultSaml2AuthenticatedPrincipal(
        principal.getName(),
        new LinkedHashMap<>(principal.getAttributes()),
        principal.getSessionIndexes()
     );
     authentication =  new Saml2Authentication(
        copy, authentication.getSaml2Response(), authentication.getAuthorities()
     );

@sjohnr sjohnr added the in: saml2 An issue in SAML2 modules label Sep 9, 2022
@akashgupta2703
Copy link

I ran into same issue post upgrading spring boot from 2.7.0 to 3.0.0. For now, I added a mix-in for LinkedMultiValueMap as below -

@SuppressWarnings("serial")
public class CustomSaml2Jackson2Module extends SimpleModule {

	public CustomSaml2Jackson2Module() {
		super(CustomSaml2Jackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
	}
	
	@Override
	public void setupModule(SetupContext context) {
		
		SecurityJackson2Modules.enableDefaultTyping(context.getOwner());
		context.setMixInAnnotations(LinkedMultiValueMap.class, LinkedMultiValueMapMixin.class);
	}
}

Instead of extending SimpleModule, Saml2Jackson2Module can also be extended.

Mixin class -

import java.util.Map;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonDeserialize(using = LinkedMultiValueMapDeserializer.class)
public abstract class LinkedMultiValueMapMixin {

	@JsonCreator
	LinkedMultiValueMapMixin(Map<?, ?> map) {
		
	}
}

Custom Deserializer -

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

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;

public class LinkedMultiValueMapDeserializer extends JsonDeserializer<MultiValueMap<?, ?>>  {

	@Override
	public MultiValueMap<?, ?> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
		
		ObjectMapper mapper = (ObjectMapper) parser.getCodec();
		JsonNode mapNode = mapper.readTree(parser);
		Map<String, List<Object>> result = new LinkedHashMap<>();
		if (mapNode != null && mapNode.isObject()) {
			Iterable<Map.Entry<String, JsonNode>> fields = mapNode::fields;
			for (Map.Entry<String, JsonNode> field : fields) {
				result.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), new TypeReference<List<Object>>() {}));
			}
		}
		
		return new LinkedMultiValueMap<>(result);
	}
	
}

I then registered this and everything seems to work fine now. I am still running tests on LinkedMultiValueMapDeserializer to check whether it is working as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: saml2 An issue in SAML2 modules type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants