21 changed files with 1251 additions and 0 deletions
@ -0,0 +1,54 @@ |
|||||||
|
/* |
||||||
|
* 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.jackson2; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser; |
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext; |
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer; |
||||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
|
||||||
|
/** |
||||||
|
* Custom deserializer for {@link UnmodifiableMapMixin}. |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see UnmodifiableMapMixin |
||||||
|
*/ |
||||||
|
class UnmodifiableMapDeserializer extends JsonDeserializer<Map<?, ?>> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Map<?, ?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { |
||||||
|
ObjectMapper mapper = (ObjectMapper) jp.getCodec(); |
||||||
|
JsonNode node = mapper.readTree(jp); |
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>(); |
||||||
|
if (node != null && node.isObject()) { |
||||||
|
Iterable<Map.Entry<String, JsonNode>> fields = node::fields; |
||||||
|
for (Map.Entry<String, JsonNode> field : fields) { |
||||||
|
result.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Object.class)); |
||||||
|
} |
||||||
|
} |
||||||
|
return Collections.unmodifiableMap(result); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* 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.jackson2; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator; |
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
||||||
|
|
||||||
|
/** |
||||||
|
* This mixin class used to deserialize java.util.Collections$UnmodifiableMap and used |
||||||
|
* with various AuthenticationToken implementation's mixin classes. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* ObjectMapper mapper = new ObjectMapper(); |
||||||
|
* mapper.registerModule(new CoreJackson2Module()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see UnmodifiableMapDeserializer |
||||||
|
* @see CoreJackson2Module |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) |
||||||
|
@JsonDeserialize(using = UnmodifiableMapDeserializer.class) |
||||||
|
class UnmodifiableMapMixin { |
||||||
|
|
||||||
|
@JsonCreator |
||||||
|
UnmodifiableMapMixin(Map<?, ?> map) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
/* |
||||||
|
* 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.jackson2; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.skyscreamer.jsonassert.JSONAssert; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
class UnmodifiableMapDeserializerTests extends AbstractMixinTests { |
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
private static final String DEFAULT_MAP_JSON = "{" |
||||||
|
+ "\"@class\": \"java.util.Collections$UnmodifiableMap\"," |
||||||
|
+ "\"Key\": \"Value\"" |
||||||
|
+ "}"; |
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerialize() throws Exception { |
||||||
|
String mapJson = mapper |
||||||
|
.writeValueAsString(Collections.unmodifiableMap(Collections.singletonMap("Key", "Value"))); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(DEFAULT_MAP_JSON, mapJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserialize() throws Exception { |
||||||
|
Map<String, String> map = mapper.readValue(DEFAULT_MAP_JSON, |
||||||
|
Collections.unmodifiableMap(Collections.emptyMap()).getClass()); |
||||||
|
|
||||||
|
assertThat(map).isNotNull().isInstanceOf(Collections.unmodifiableMap(Collections.emptyMap()).getClass()) |
||||||
|
.containsAllEntriesOf(Map.of("Key", "Value")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
/* |
||||||
|
* 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<DefaultSaml2AuthenticatedPrincipal> { |
||||||
|
|
||||||
|
private static final TypeReference<List<String>> SESSION_INDICES_LIST = new TypeReference<List<String>>() { |
||||||
|
}; |
||||||
|
|
||||||
|
private static final TypeReference<Map<String, List<Object>>> ATTRIBUTES_MAP = new TypeReference<Map<String, List<Object>>>() { |
||||||
|
}; |
||||||
|
|
||||||
|
@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<String, List<Object>> attributes = JsonNodeUtils.findValue(jsonNode, "attributes", ATTRIBUTES_MAP, mapper); |
||||||
|
List<String> 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* 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.annotation.JsonAutoDetect; |
||||||
|
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; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson Mixin class helps in serialize/deserialize |
||||||
|
* {@link DefaultSaml2AuthenticatedPrincipal}. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* ObjectMapper mapper = new ObjectMapper(); |
||||||
|
* mapper.registerModule(new Saml2Jackson2Module()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see DefaultSaml2AuthenticatedPrincipalDeserializer |
||||||
|
* @see Saml2Jackson2Module |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) |
||||||
|
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) |
||||||
|
@JsonDeserialize(using = DefaultSaml2AuthenticatedPrincipalDeserializer.class) |
||||||
|
class DefaultSaml2AuthenticatedPrincipalMixin { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
/* |
||||||
|
* 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> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> 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(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* 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<Saml2Authentication> { |
||||||
|
|
||||||
|
private static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<List<GrantedAuthority>>() { |
||||||
|
}; |
||||||
|
|
||||||
|
private static final TypeReference<Object> OBJECT = new TypeReference<Object>() { |
||||||
|
}; |
||||||
|
|
||||||
|
@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<GrantedAuthority> 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* 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.annotation.JsonAutoDetect; |
||||||
|
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.Saml2Authentication; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson Mixin class helps in serialize/deserialize {@link Saml2Authentication}. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* ObjectMapper mapper = new ObjectMapper(); |
||||||
|
* mapper.registerModule(new Saml2Jackson2Module()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see Saml2AuthenticationDeserializer |
||||||
|
* @see Saml2Jackson2Module |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) |
||||||
|
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, |
||||||
|
isGetterVisibility = JsonAutoDetect.Visibility.NONE) |
||||||
|
@JsonDeserialize(using = Saml2AuthenticationDeserializer.class) |
||||||
|
class Saml2AuthenticationMixin { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
/* |
||||||
|
* 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.Version; |
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule; |
||||||
|
|
||||||
|
import org.springframework.security.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson module for saml2-service-provider. This module register |
||||||
|
* {@link Saml2AuthenticationMixin}, {@link DefaultSaml2AuthenticatedPrincipalMixin}, |
||||||
|
* {@link Saml2LogoutRequestMixin}, {@link Saml2RedirectAuthenticationRequestMixin} and |
||||||
|
* {@link Saml2PostAuthenticationRequestMixin}. |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
public class Saml2Jackson2Module extends SimpleModule { |
||||||
|
|
||||||
|
public Saml2Jackson2Module() { |
||||||
|
super(Saml2Jackson2Module.class.getName(), new Version(1, 0, 0, null, null, null)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setupModule(SetupContext context) { |
||||||
|
context.setMixInAnnotations(Saml2Authentication.class, Saml2AuthenticationMixin.class); |
||||||
|
context.setMixInAnnotations(DefaultSaml2AuthenticatedPrincipal.class, |
||||||
|
DefaultSaml2AuthenticatedPrincipalMixin.class); |
||||||
|
context.setMixInAnnotations(Saml2LogoutRequest.class, Saml2LogoutRequestMixin.class); |
||||||
|
context.setMixInAnnotations(Saml2RedirectAuthenticationRequest.class, |
||||||
|
Saml2RedirectAuthenticationRequestMixin.class); |
||||||
|
context.setMixInAnnotations(Saml2PostAuthenticationRequest.class, Saml2PostAuthenticationRequestMixin.class); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
/* |
||||||
|
* 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.util.Map; |
||||||
|
|
||||||
|
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.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson Mixin class helps in serialize/deserialize {@link Saml2LogoutRequest}. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* ObjectMapper mapper = new ObjectMapper(); |
||||||
|
* mapper.registerModule(new Saml2Jackson2Module()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see Saml2Jackson2Module |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) |
||||||
|
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) |
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||||
|
class Saml2LogoutRequestMixin { |
||||||
|
|
||||||
|
@JsonCreator |
||||||
|
Saml2LogoutRequestMixin(@JsonProperty("location") String location, |
||||||
|
@JsonProperty("relayState") Saml2MessageBinding relayState, |
||||||
|
@JsonProperty("parameters") Map<String, String> parameters, @JsonProperty("id") String id, |
||||||
|
@JsonProperty("relyingPartyRegistrationId") String relyingPartyRegistrationId) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
/* |
||||||
|
* 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.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.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson Mixin class helps in serialize/deserialize |
||||||
|
* {@link Saml2PostAuthenticationRequest}. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* ObjectMapper mapper = new ObjectMapper(); |
||||||
|
* mapper.registerModule(new Saml2Jackson2Module()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see Saml2Jackson2Module |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) |
||||||
|
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) |
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||||
|
class Saml2PostAuthenticationRequestMixin { |
||||||
|
|
||||||
|
@JsonCreator |
||||||
|
Saml2PostAuthenticationRequestMixin(@JsonProperty("samlRequest") String samlRequest, |
||||||
|
@JsonProperty("relayState") String relayState, |
||||||
|
@JsonProperty("authenticationRequestUri") String authenticationRequestUri) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
/* |
||||||
|
* 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.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.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson Mixin class helps in serialize/deserialize |
||||||
|
* {@link Saml2RedirectAuthenticationRequest}. |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* ObjectMapper mapper = new ObjectMapper(); |
||||||
|
* mapper.registerModule(new Saml2Jackson2Module()); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @author Ulrich Grave |
||||||
|
* @since 5.7 |
||||||
|
* @see Saml2Jackson2Module |
||||||
|
* @see SecurityJackson2Modules |
||||||
|
*/ |
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) |
||||||
|
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) |
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||||
|
class Saml2RedirectAuthenticationRequestMixin { |
||||||
|
|
||||||
|
@JsonCreator |
||||||
|
Saml2RedirectAuthenticationRequestMixin(@JsonProperty("samlRequest") String samlRequest, |
||||||
|
@JsonProperty("sigAlg") String sigAlg, @JsonProperty("signature") String signature, |
||||||
|
@JsonProperty("relayState") String relayState, |
||||||
|
@JsonProperty("authenticationRequestUri") String authenticationRequestUri) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,105 @@ |
|||||||
|
/* |
||||||
|
* 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.databind.ObjectMapper; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.skyscreamer.jsonassert.JSONAssert; |
||||||
|
|
||||||
|
import org.springframework.security.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
class DefaultSaml2AuthenticatedPrincipalMixinTests { |
||||||
|
|
||||||
|
private ObjectMapper mapper; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setUp() { |
||||||
|
this.mapper = new ObjectMapper(); |
||||||
|
ClassLoader loader = getClass().getClassLoader(); |
||||||
|
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerialize() throws Exception { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = TestSaml2JsonPayloads.createDefaultPrincipal(); |
||||||
|
|
||||||
|
String principalJson = this.mapper.writeValueAsString(principal); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON, principalJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerializeWithoutRegistrationId() throws Exception { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal( |
||||||
|
TestSaml2JsonPayloads.PRINCIPAL_NAME, TestSaml2JsonPayloads.ATTRIBUTES, |
||||||
|
TestSaml2JsonPayloads.SESSION_INDEXES); |
||||||
|
|
||||||
|
String principalJson = this.mapper.writeValueAsString(principal); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(principalWithoutRegId(), principalJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerializeWithoutIndices() throws Exception { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal( |
||||||
|
TestSaml2JsonPayloads.PRINCIPAL_NAME, TestSaml2JsonPayloads.ATTRIBUTES); |
||||||
|
principal.setRelyingPartyRegistrationId(TestSaml2JsonPayloads.REG_ID); |
||||||
|
|
||||||
|
String principalJson = this.mapper.writeValueAsString(principal); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(principalWithoutIndices(), principalJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserialize() throws Exception { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = this.mapper.readValue( |
||||||
|
TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON, DefaultSaml2AuthenticatedPrincipal.class); |
||||||
|
|
||||||
|
assertThat(principal).isNotNull(); |
||||||
|
assertThat(principal.getName()).isEqualTo(TestSaml2JsonPayloads.PRINCIPAL_NAME); |
||||||
|
assertThat(principal.getRelyingPartyRegistrationId()).isEqualTo(TestSaml2JsonPayloads.REG_ID); |
||||||
|
assertThat(principal.getAttributes()).isEqualTo(TestSaml2JsonPayloads.ATTRIBUTES); |
||||||
|
assertThat(principal.getSessionIndexes()).isEqualTo(TestSaml2JsonPayloads.SESSION_INDEXES); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserializeWithoutRegistrationId() throws Exception { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = this.mapper.readValue(principalWithoutRegId(), |
||||||
|
DefaultSaml2AuthenticatedPrincipal.class); |
||||||
|
|
||||||
|
assertThat(principal).isNotNull(); |
||||||
|
assertThat(principal.getName()).isEqualTo(TestSaml2JsonPayloads.PRINCIPAL_NAME); |
||||||
|
assertThat(principal.getRelyingPartyRegistrationId()).isNull(); |
||||||
|
assertThat(principal.getAttributes()).isEqualTo(TestSaml2JsonPayloads.ATTRIBUTES); |
||||||
|
assertThat(principal.getSessionIndexes()).isEqualTo(TestSaml2JsonPayloads.SESSION_INDEXES); |
||||||
|
} |
||||||
|
|
||||||
|
private static String principalWithoutRegId() { |
||||||
|
return TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON.replace(TestSaml2JsonPayloads.REG_ID_JSON, |
||||||
|
"null"); |
||||||
|
} |
||||||
|
|
||||||
|
private static String principalWithoutIndices() { |
||||||
|
return TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON |
||||||
|
.replace(TestSaml2JsonPayloads.SESSION_INDEXES_JSON, "[\"java.util.Collections$EmptyList\", []]"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* 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.databind.ObjectMapper; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.skyscreamer.jsonassert.JSONAssert; |
||||||
|
|
||||||
|
import org.springframework.security.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
class Saml2AuthenticationMixinTests { |
||||||
|
|
||||||
|
private ObjectMapper mapper; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setUp() { |
||||||
|
this.mapper = new ObjectMapper(); |
||||||
|
ClassLoader loader = getClass().getClassLoader(); |
||||||
|
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerialize() throws Exception { |
||||||
|
Saml2Authentication authentication = TestSaml2JsonPayloads.createDefaultAuthentication(); |
||||||
|
|
||||||
|
String authenticationJson = this.mapper.writeValueAsString(authentication); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_SAML2AUTHENTICATION_JSON, authenticationJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserialize() throws Exception { |
||||||
|
Saml2Authentication authentication = this.mapper |
||||||
|
.readValue(TestSaml2JsonPayloads.DEFAULT_SAML2AUTHENTICATION_JSON, Saml2Authentication.class); |
||||||
|
|
||||||
|
assertThat(authentication).isNotNull(); |
||||||
|
assertThat(authentication.getDetails()).isEqualTo(TestSaml2JsonPayloads.DETAILS); |
||||||
|
assertThat(authentication.getCredentials()).isEqualTo(TestSaml2JsonPayloads.SAML_RESPONSE); |
||||||
|
assertThat(authentication.getSaml2Response()).isEqualTo(TestSaml2JsonPayloads.SAML_RESPONSE); |
||||||
|
assertThat(authentication.getAuthorities()).isEqualTo(TestSaml2JsonPayloads.AUTHORITIES); |
||||||
|
assertThat(authentication.getPrincipal()).usingRecursiveComparison() |
||||||
|
.isEqualTo(TestSaml2JsonPayloads.createDefaultPrincipal()); |
||||||
|
assertThat(authentication.getDetails()).usingRecursiveComparison().isEqualTo(TestSaml2JsonPayloads.DETAILS); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* 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.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.skyscreamer.jsonassert.JSONAssert; |
||||||
|
|
||||||
|
import org.springframework.security.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
class Saml2LogoutRequestMixinTests { |
||||||
|
|
||||||
|
private ObjectMapper mapper; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setUp() { |
||||||
|
this.mapper = new ObjectMapper(); |
||||||
|
ClassLoader loader = getClass().getClassLoader(); |
||||||
|
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerialize() throws Exception { |
||||||
|
Saml2LogoutRequest request = TestSaml2JsonPayloads.createDefaultSaml2LogoutRequest(); |
||||||
|
|
||||||
|
String requestJson = this.mapper.writeValueAsString(request); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_LOGOUT_REQUEST_JSON, requestJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserialize() throws Exception { |
||||||
|
Saml2LogoutRequest logoutRequest = this.mapper.readValue(TestSaml2JsonPayloads.DEFAULT_LOGOUT_REQUEST_JSON, |
||||||
|
Saml2LogoutRequest.class); |
||||||
|
|
||||||
|
assertThat(logoutRequest).isNotNull(); |
||||||
|
assertThat(logoutRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID); |
||||||
|
assertThat(logoutRequest.getRelyingPartyRegistrationId()) |
||||||
|
.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID); |
||||||
|
assertThat(logoutRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST); |
||||||
|
assertThat(logoutRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE); |
||||||
|
assertThat(logoutRequest.getLocation()).isEqualTo(TestSaml2JsonPayloads.LOCATION); |
||||||
|
assertThat(logoutRequest.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT); |
||||||
|
Map<String, String> expectedParams = new HashMap<>(); |
||||||
|
expectedParams.put("SAMLRequest", TestSaml2JsonPayloads.SAML_REQUEST); |
||||||
|
expectedParams.put("RelayState", TestSaml2JsonPayloads.RELAY_STATE); |
||||||
|
expectedParams.put("AdditionalParam", TestSaml2JsonPayloads.ADDITIONAL_PARAM); |
||||||
|
assertThat(logoutRequest.getParameters()).containsAllEntriesOf(expectedParams); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
/* |
||||||
|
* 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.databind.ObjectMapper; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.skyscreamer.jsonassert.JSONAssert; |
||||||
|
|
||||||
|
import org.springframework.security.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
class Saml2PostAuthenticationRequestMixinTests { |
||||||
|
|
||||||
|
private ObjectMapper mapper; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setUp() { |
||||||
|
this.mapper = new ObjectMapper(); |
||||||
|
ClassLoader loader = getClass().getClassLoader(); |
||||||
|
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerialize() throws Exception { |
||||||
|
Saml2PostAuthenticationRequest request = TestSaml2JsonPayloads.createDefaultSaml2PostAuthenticationRequest(); |
||||||
|
|
||||||
|
String requestJson = this.mapper.writeValueAsString(request); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON, requestJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserialize() throws Exception { |
||||||
|
Saml2PostAuthenticationRequest authRequest = this.mapper |
||||||
|
.readValue(TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON, Saml2PostAuthenticationRequest.class); |
||||||
|
|
||||||
|
assertThat(authRequest).isNotNull(); |
||||||
|
assertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST); |
||||||
|
assertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE); |
||||||
|
assertThat(authRequest.getAuthenticationRequestUri()) |
||||||
|
.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* 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.databind.ObjectMapper; |
||||||
|
import org.junit.jupiter.api.BeforeEach; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.skyscreamer.jsonassert.JSONAssert; |
||||||
|
|
||||||
|
import org.springframework.security.jackson2.SecurityJackson2Modules; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
class Saml2RedirectAuthenticationRequestMixinTests { |
||||||
|
|
||||||
|
private ObjectMapper mapper; |
||||||
|
|
||||||
|
@BeforeEach |
||||||
|
void setUp() { |
||||||
|
this.mapper = new ObjectMapper(); |
||||||
|
ClassLoader loader = getClass().getClassLoader(); |
||||||
|
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldSerialize() throws Exception { |
||||||
|
Saml2RedirectAuthenticationRequest request = TestSaml2JsonPayloads |
||||||
|
.createDefaultSaml2RedirectAuthenticationRequest(); |
||||||
|
|
||||||
|
String requestJson = this.mapper.writeValueAsString(request); |
||||||
|
|
||||||
|
JSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON, requestJson, true); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void shouldDeserialize() throws Exception { |
||||||
|
Saml2RedirectAuthenticationRequest authRequest = this.mapper.readValue( |
||||||
|
TestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON, Saml2RedirectAuthenticationRequest.class); |
||||||
|
|
||||||
|
assertThat(authRequest).isNotNull(); |
||||||
|
assertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST); |
||||||
|
assertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE); |
||||||
|
assertThat(authRequest.getAuthenticationRequestUri()) |
||||||
|
.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI); |
||||||
|
assertThat(authRequest.getSigAlg()).isEqualTo(TestSaml2JsonPayloads.SIG_ALG); |
||||||
|
assertThat(authRequest.getSignature()).isEqualTo(TestSaml2JsonPayloads.SIGNATURE); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,220 @@ |
|||||||
|
/* |
||||||
|
* 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.util.Arrays; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||||
|
import org.springframework.security.core.userdetails.User; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; |
||||||
|
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; |
||||||
|
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; |
||||||
|
|
||||||
|
final class TestSaml2JsonPayloads { |
||||||
|
|
||||||
|
private TestSaml2JsonPayloads() { |
||||||
|
} |
||||||
|
|
||||||
|
static final Map<String, List<Object>> ATTRIBUTES; |
||||||
|
|
||||||
|
static { |
||||||
|
Map<String, List<Object>> tmpAttributes = new HashMap<>(); |
||||||
|
tmpAttributes.put("name", Collections.singletonList("attr_name")); |
||||||
|
tmpAttributes.put("email", Collections.singletonList("attr_email")); |
||||||
|
tmpAttributes.put("listOf", Collections.unmodifiableList(Arrays.asList("Element1", "Element2", 4, true))); |
||||||
|
ATTRIBUTES = Collections.unmodifiableMap(tmpAttributes); |
||||||
|
} |
||||||
|
|
||||||
|
static final String REG_ID = "REG_ID_TEST"; |
||||||
|
static final String REG_ID_JSON = "\"" + REG_ID + "\""; |
||||||
|
|
||||||
|
static final String SESSION_INDEXES_JSON = "[" + " \"java.util.Collections$UnmodifiableRandomAccessList\"," |
||||||
|
+ " [ \"Index 1\", \"Index 2\" ]" + "]"; |
||||||
|
static final List<String> SESSION_INDEXES = Collections.unmodifiableList(Arrays.asList("Index 1", "Index 2")); |
||||||
|
|
||||||
|
static final String PRINCIPAL_NAME = "principalName"; |
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
static final String DEFAULT_AUTHENTICATED_PRINCIPAL_JSON = "{" |
||||||
|
+ " \"@class\": \"org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal\"," |
||||||
|
+ " \"name\": \"" + PRINCIPAL_NAME + "\"," |
||||||
|
+ " \"attributes\": {" |
||||||
|
+ " \"@class\": \"java.util.Collections$UnmodifiableMap\"," |
||||||
|
+ " \"listOf\": [" |
||||||
|
+ " \"java.util.Collections$UnmodifiableRandomAccessList\"," |
||||||
|
+ " [ \"Element1\", \"Element2\", 4, true ]" |
||||||
|
+ " ]," |
||||||
|
+ " \"email\": [" |
||||||
|
+ " \"java.util.Collections$SingletonList\"," |
||||||
|
+ " [ \"attr_email\" ]" |
||||||
|
+ " ]," |
||||||
|
+ " \"name\": [" |
||||||
|
+ " \"java.util.Collections$SingletonList\"," |
||||||
|
+ " [ \"attr_name\" ]" |
||||||
|
+ " ]" |
||||||
|
+ " }," |
||||||
|
+ " \"sessionIndexes\": " + SESSION_INDEXES_JSON + "," |
||||||
|
+ " \"registrationId\": " + REG_ID_JSON + "" |
||||||
|
+ "}"; |
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
static DefaultSaml2AuthenticatedPrincipal createDefaultPrincipal() { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(PRINCIPAL_NAME, |
||||||
|
ATTRIBUTES, SESSION_INDEXES); |
||||||
|
principal.setRelyingPartyRegistrationId(REG_ID); |
||||||
|
return principal; |
||||||
|
} |
||||||
|
|
||||||
|
static final String SAML_REQUEST = "samlRequestValue"; |
||||||
|
static final String RELAY_STATE = "relayStateValue"; |
||||||
|
static final String AUTHENTICATION_REQUEST_URI = "authenticationRequestUriValue"; |
||||||
|
static final String SIG_ALG = "sigAlgValue"; |
||||||
|
static final String SIGNATURE = "signatureValue"; |
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
static final String DEFAULT_REDIRECT_AUTH_REQUEST_JSON = "{" |
||||||
|
+ " \"@class\": \"org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest\"," |
||||||
|
+ " \"samlRequest\": \"" + SAML_REQUEST + "\"," |
||||||
|
+ " \"relayState\": \"" + RELAY_STATE + "\"," |
||||||
|
+ " \"authenticationRequestUri\": \"" + AUTHENTICATION_REQUEST_URI + "\"," |
||||||
|
+ " \"sigAlg\": \"" + SIG_ALG + "\"," |
||||||
|
+ " \"signature\": \"" + SIGNATURE + "\"" |
||||||
|
+ "}"; |
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
static final String DEFAULT_POST_AUTH_REQUEST_JSON = "{" |
||||||
|
+ " \"@class\": \"org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest\"," |
||||||
|
+ " \"samlRequest\": \"" + SAML_REQUEST + "\"," |
||||||
|
+ " \"relayState\": \"" + RELAY_STATE + "\"," |
||||||
|
+ " \"authenticationRequestUri\": \"" + AUTHENTICATION_REQUEST_URI + "\"" |
||||||
|
+ "}"; |
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
static final String ID = "idValue"; |
||||||
|
static final String LOCATION = "locationValue"; |
||||||
|
static final String BINDNG = "REDIRECT"; |
||||||
|
static final String RELYINGPARTY_REGISTRATION_ID = "registrationIdValue"; |
||||||
|
static final String ADDITIONAL_PARAM = "additionalParamValue"; |
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
static final String DEFAULT_LOGOUT_REQUEST_JSON = "{" |
||||||
|
+ " \"@class\": \"org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest\"," |
||||||
|
+ " \"id\": \"" + ID + "\"," |
||||||
|
+ " \"location\": \"" + LOCATION + "\"," |
||||||
|
+ " \"binding\": \"" + BINDNG + "\"," |
||||||
|
+ " \"relyingPartyRegistrationId\": \"" + RELYINGPARTY_REGISTRATION_ID + "\"," |
||||||
|
+ " \"parameters\": { " |
||||||
|
+ " \"@class\": \"java.util.Collections$UnmodifiableMap\"," |
||||||
|
+ " \"SAMLRequest\": \"" + SAML_REQUEST + "\"," |
||||||
|
+ " \"RelayState\": \"" + RELAY_STATE + "\"," |
||||||
|
+ " \"AdditionalParam\": \"" + ADDITIONAL_PARAM + "\"" |
||||||
|
+ " }" |
||||||
|
+ "}"; |
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
static Saml2PostAuthenticationRequest createDefaultSaml2PostAuthenticationRequest() { |
||||||
|
return Saml2PostAuthenticationRequest.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full() |
||||||
|
.assertingPartyDetails((party) -> party.singleSignOnServiceLocation(AUTHENTICATION_REQUEST_URI)) |
||||||
|
.build()).samlRequest(SAML_REQUEST).relayState(RELAY_STATE).build(); |
||||||
|
} |
||||||
|
|
||||||
|
static Saml2RedirectAuthenticationRequest createDefaultSaml2RedirectAuthenticationRequest() { |
||||||
|
return Saml2RedirectAuthenticationRequest |
||||||
|
.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full() |
||||||
|
.assertingPartyDetails((party) -> party.singleSignOnServiceLocation(AUTHENTICATION_REQUEST_URI)) |
||||||
|
.build()) |
||||||
|
.samlRequest(SAML_REQUEST).relayState(RELAY_STATE).sigAlg(SIG_ALG).signature(SIGNATURE).build(); |
||||||
|
} |
||||||
|
|
||||||
|
static Saml2LogoutRequest createDefaultSaml2LogoutRequest() { |
||||||
|
return Saml2LogoutRequest |
||||||
|
.withRelyingPartyRegistration( |
||||||
|
TestRelyingPartyRegistrations.full().registrationId(RELYINGPARTY_REGISTRATION_ID) |
||||||
|
.assertingPartyDetails((party) -> party.singleLogoutServiceLocation(LOCATION) |
||||||
|
.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT)) |
||||||
|
.build()) |
||||||
|
.id(ID).samlRequest(SAML_REQUEST).relayState(RELAY_STATE) |
||||||
|
.parameters((params) -> params.put("AdditionalParam", ADDITIONAL_PARAM)).build(); |
||||||
|
} |
||||||
|
|
||||||
|
static final Collection<GrantedAuthority> AUTHORITIES = Collections |
||||||
|
.unmodifiableList(Arrays.asList(new SimpleGrantedAuthority("Role1"), new SimpleGrantedAuthority("Role2"))); |
||||||
|
|
||||||
|
static final Object DETAILS = User.withUsername("username").password("empty").authorities("A", "B").build(); |
||||||
|
static final String SAML_RESPONSE = "samlResponseValue"; |
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
static final String DEFAULT_SAML2AUTHENTICATION_JSON = "{" |
||||||
|
+ " \"@class\": \"org.springframework.security.saml2.provider.service.authentication.Saml2Authentication\"," |
||||||
|
+ " \"authorities\": [" |
||||||
|
+ " \"java.util.Collections$UnmodifiableRandomAccessList\"," |
||||||
|
+ " [" |
||||||
|
+ " {" |
||||||
|
+ " \"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\"," |
||||||
|
+ " \"authority\": \"Role1\"" |
||||||
|
+ " }," |
||||||
|
+ " {" |
||||||
|
+ " \"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\"," |
||||||
|
+ " \"authority\": \"Role2\"" |
||||||
|
+ " }" |
||||||
|
+ " ]" |
||||||
|
+ " ]," |
||||||
|
+ " \"details\": {" |
||||||
|
+ " \"@class\": \"org.springframework.security.core.userdetails.User\"," |
||||||
|
+ " \"password\": \"empty\"," |
||||||
|
+ " \"username\": \"username\"," |
||||||
|
+ " \"authorities\": [" |
||||||
|
+ " \"java.util.Collections$UnmodifiableSet\", [" |
||||||
|
+ " {" |
||||||
|
+ " \"@class\":\"org.springframework.security.core.authority.SimpleGrantedAuthority\"," |
||||||
|
+ " \"authority\":\"A\"" |
||||||
|
+ " }," |
||||||
|
+ " {" |
||||||
|
+ " \"@class\":\"org.springframework.security.core.authority.SimpleGrantedAuthority\"," |
||||||
|
+ " \"authority\":\"B\"" |
||||||
|
+ " }" |
||||||
|
+ " ]]," |
||||||
|
+ " \"accountNonExpired\": true," |
||||||
|
+ " \"accountNonLocked\": true," |
||||||
|
+ " \"credentialsNonExpired\": true," |
||||||
|
+ " \"enabled\": true" |
||||||
|
+ " }," |
||||||
|
+ " \"authenticated\": true," |
||||||
|
+ " \"principal\": " + DEFAULT_AUTHENTICATED_PRINCIPAL_JSON + "," |
||||||
|
+ " \"saml2Response\": \"" + SAML_RESPONSE + "\"" |
||||||
|
+ "}"; |
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
static Saml2Authentication createDefaultAuthentication() { |
||||||
|
DefaultSaml2AuthenticatedPrincipal principal = createDefaultPrincipal(); |
||||||
|
Saml2Authentication authentication = new Saml2Authentication(principal, SAML_RESPONSE, AUTHORITIES); |
||||||
|
authentication.setDetails(DETAILS); |
||||||
|
return authentication; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue