parent
8c95ed6568
commit
2334610fa9
|
@ -64,6 +64,8 @@ public class CoreJackson2Module extends SimpleModule {
|
|||
UnmodifiableSetMixin.class);
|
||||
context.setMixInAnnotations(Collections.<Object>unmodifiableList(Collections.emptyList()).getClass(),
|
||||
UnmodifiableListMixin.class);
|
||||
context.setMixInAnnotations(Collections.<Object, Object>unmodifiableMap(Collections.emptyMap()).getClass(),
|
||||
UnmodifiableMapMixin.class);
|
||||
context.setMixInAnnotations(User.class, UserMixin.class);
|
||||
context.setMixInAnnotations(UsernamePasswordAuthenticationToken.class,
|
||||
UsernamePasswordAuthenticationTokenMixin.class);
|
||||
|
|
|
@ -63,6 +63,7 @@ import org.springframework.util.ClassUtils;
|
|||
* mapper.registerModule(new WebServletJackson2Module());
|
||||
* mapper.registerModule(new WebServerJackson2Module());
|
||||
* mapper.registerModule(new OAuth2ClientJackson2Module());
|
||||
* mapper.registerModule(new Saml2Jackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
|
@ -86,6 +87,8 @@ public final class SecurityJackson2Modules {
|
|||
|
||||
private static final String ldapJackson2ModuleClass = "org.springframework.security.ldap.jackson2.LdapJackson2Module";
|
||||
|
||||
private static final String saml2Jackson2ModuleClass = "org.springframework.security.saml2.jackson2.Saml2Jackson2Module";
|
||||
|
||||
private SecurityJackson2Modules() {
|
||||
}
|
||||
|
||||
|
@ -134,6 +137,9 @@ public final class SecurityJackson2Modules {
|
|||
if (ClassUtils.isPresent(ldapJackson2ModuleClass, loader)) {
|
||||
addToModulesList(loader, modules, ldapJackson2ModuleClass);
|
||||
}
|
||||
if (ClassUtils.isPresent(saml2Jackson2ModuleClass, loader)) {
|
||||
addToModulesList(loader, modules, saml2Jackson2ModuleClass);
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
|
@ -60,8 +60,11 @@ dependencies {
|
|||
|
||||
provided 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver'
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.skyscreamer:jsonassert"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
|
|
|
@ -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